Silverlight鼠标事件:MouseEnter和MouseLeave冲突
我有一个网格中的按钮集合。对于这些按钮中的每一个,我都想处理MouseEnter和MouseLeave事件来设置按钮高度的动画(并做一些其他有趣的事情)。这一切都很好,直到我开始在按钮上移动鼠标太快,最终导致事件在另一个按钮完成之前发生。确保事件在触发前等待对方的最佳方式是什么 更新: 按照x0r的建议,我将代码重构为一个内部类,该类继承自Silverlight鼠标事件:MouseEnter和MouseLeave冲突,silverlight,events,animation,silverlight-4.0,mouseevent,Silverlight,Events,Animation,Silverlight 4.0,Mouseevent,我有一个网格中的按钮集合。对于这些按钮中的每一个,我都想处理MouseEnter和MouseLeave事件来设置按钮高度的动画(并做一些其他有趣的事情)。这一切都很好,直到我开始在按钮上移动鼠标太快,最终导致事件在另一个按钮完成之前发生。确保事件在触发前等待对方的最佳方式是什么 更新: 按照x0r的建议,我将代码重构为一个内部类,该类继承自按钮,并具有执行动画所需的方法。不幸的是,这并没有真正解决问题,因为——我想——我在两个不同的地方处理第一个动画的完成事件。(如果我错了,请纠正我)。这是我的
按钮
,并具有执行动画所需的方法。不幸的是,这并没有真正解决问题,因为——我想——我在两个不同的地方处理第一个动画的完成事件。(如果我错了,请纠正我)。这是我的密码:
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:
某些事件被多次触发。一个例子是我的规则
对象的关闭按钮上的单击
事件
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;
}
}
};
}
更新3:
为了避免动画运行太早,我想我可以延迟MouseEnter
事件,因此如果用户只是在项目上滚动过快,它就不会启动。但我现在有一个问题:假设用户将鼠标移到项目上,然后将鼠标移出。如果我使用Storyboard.BeginTime
属性,那就不能安全地防止这种行为,因为即使动画被延迟,它最终还是会开始。。。那么,我有没有办法防止这种情况发生
有什么建议吗 如果第一个情节提要仍在运行,请检查您的mouseleave
eventhandler,如果是这种情况,请将第二个情节提要的开头附加到第一个情节提要的已完成的事件上:
private void OnOddRowMouseLeave(object sender, MouseEventArgs e)
{
...
if(_firstStoryboard.GetCurrentState() != ClockState.Stopped)
_firstStoryboard.Completed += (s,e) => _secondStoryboard.Begin();
else
_secondStoryboard.Begin()
Silverlight所做的一切都是异步的,因此最有可能发生的情况是,由于您正在快速地进出框,所以在鼠标指针有机会完成之前,鼠标左键被触发。你可以设置你的两个事件,以便它们有一个指示器来指示另一个事件是否正在进行中。例如,你可以这样做
bool mouseOut =false;
bool mouseIn =false;
void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if(!this.mouseOut)
{
this.mouseIn = true;
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();
if(this.mouseOut)
{
this.OnMouseLeave();
}
this.mouseIn = false;
}
}
void OnMouseLeave()
{
if(!this.mouseIn)
{
this.mouseOut = false;
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();
}
}
else
{
this.mouseOut = true;
}
}
我实际上还没有检查过这段代码,但这至少可以帮助您更接近您想要的。这应该足够快,让你的用户不会意识到,如果他们快速浏览它,它并没有完全在退出时启动。但这应该有助于避免重叠
另一种方法是将初始事件设置为null,并在事件完成时让“事件中的鼠标”将“事件中的鼠标”设置为“事件中的鼠标”,但问题是,如果在设置事件之前鼠标熄灭,则无法触发事件。我已更新了问题,请您看一下好吗?谢谢。@Kassem:你的代码是如何运行的(什么东西没有正常工作?)顺便说一句:你可能还应该检查MouseEnter,如果MouseLeave情节提要仍然在运行,如果是这样的话,停止MouseLeave情节提要我仍然不明白为什么会出现这些问题。我注意到的一件事是,一些事件被多次触发,而它们只应该被调用一次!一个例子是关闭按钮的点击事件(检查新的更新)。@Kassem:events只触发一次,是否会多次附加事件处理程序?您的代码看起来不错,但您应该通过设置断点进行检查
bool mouseOut =false;
bool mouseIn =false;
void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if(!this.mouseOut)
{
this.mouseIn = true;
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();
if(this.mouseOut)
{
this.OnMouseLeave();
}
this.mouseIn = false;
}
}
void OnMouseLeave()
{
if(!this.mouseIn)
{
this.mouseOut = false;
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();
}
}
else
{
this.mouseOut = true;
}
}