У меня есть коллекция кнопок в сетке. Для каждой из этих кнопок я хочу обработать события MouseEnter и MouseLeave, чтобы анимировать высоту кнопки (и сделать некоторые другие интересные вещи). Все это работает хорошо, пока я не начну слишком быстро двигать мышь над кнопками и выключать их, что в конечном итоге приводит к тому, что события происходят раньше, чем завершается другая. Как лучше всего убедиться, что события ждут друг друга перед тем, как их запустить?
UPDATE:
Следуя совету x0r, я преобразовал свой код во внутренний класс, который наследуется от Button
и имеет необходимые методы для выполнения анимации. К сожалению, это не решило проблему, потому что - я думаю - Я обрабатываю событие Completed
первой анимации в двух разных местах. (поправьте меня если я ошибаюсь). Вот мой код:
internal class MockButton : Button
{
#region Fields
private Storyboard _mouseEnterStoryBoard;
private Storyboard _mouseLeaveStoryBoard;
private Double _width;
#endregion
#region Properties
internal Int32 Index { get; private set; }
#endregion
#region Ctors
internal MockButton(Int32 index) : this(index, 200)
{
}
internal MockButton(Int32 index, Double width)
{
this.Index = index;
this._width = width;
}
#endregion
#region Event Handlers
internal void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
}
internal void OnMouseLeave()
{
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
#endregion
}
ОБНОВЛЕНИЕ 2:
Некоторые события запускаются несколько раз. Примером этого является событие Click
на кнопке закрытия моего Rule
объекта ...
public Rule(Action<Int32> closeAction)
{
this.Style = Application.Current.Resources["RuleDefaultStyle"] as Style;
this.CloseAction = closeAction;
this.Loaded += (s, e) =>
{
if (_closeButton != null)
{
_closeButton.Click += (btn, args) =>
{
if (this.CloseAction != null)
{
this.CloseAction.Invoke(this.Index);
}
};
if (_closeButtonShouldBeVisible)
{
_closeButton.Visibility = System.Windows.Visibility.Visible;
}
}
};
}
И ниже Action<Int32>
, который я передаю Rule
объекту как CloseAction
:
private void RemoveRule(Int32 ruleIndex)
{
Rule ruleToRemove = Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex));
Storyboard sb = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
sb.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Opacity"));
animation.Duration = TimeSpan.FromMilliseconds(300);
animation.From = 1;
animation.To = 0;
sb.Children.Add(animation);
Storyboard.SetTarget(animation, ruleToRemove);
sb.Completed += (s, e) =>
{
if (Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex)) != null)
{
this.Rules.RemoveAt(ruleIndex);
}
};
sb.Begin();
}
ОБНОВЛЕНИЕ 3:
Чтобы избежать запуска анимации слишком рано, я подумал, что могу отложить событие MouseEnter
, поэтому, если пользователь просто слишком быстро прокручивает элемент, он не запускается. Но сейчас у меня проблема: скажем, пользователь наведет курсор мыши на элемент, а затем наведет курсор мыши. Если я использую свойство Storyboard.BeginTime
, это не защитит от такого поведения, потому что, несмотря на то, что анимация задерживается, она все равно в конечном итоге запустится ... Так есть ли способ, которым я мог бы предотвратить это?
Есть предложения?