也许是因为Silverlight是跑在浏览器上的缘故,也许是Silverlight才刚刚起步,从设计之初到现在,它对鼠标事件的响应,仍然只有可怜的MouseLeftButtonDown、MouseLeftButtonUp、MouseEnter、MouseLeave和MouseMove五个事件而已。且不论它并没有对右键和滚轮(这个也许是为了跨平台,据说MacOS是没有滚轮的)的支持,就连双击这个简单的事件它也没有包装。因此,我自己写了一个Helper,实现了在Silverlight中的双击事件。
我仔细的研究了WinForm中的双击事件,我发现WinForm中的事件还真是多,和点击有关的事件,就有MouseClick、MouseDown、MouseUp、MouseDoubleClick、Click、DoubleClick等等。。。其实可以毫不客气的说,所有的事件都是对MouseDown和MouseUp的封装,但是这样的确可以让开发人员感到非常的方便。经过研究我发现,如果用户双击某一个控件,第一次点击会触发Click事件,第二次点击会触发DoubleClick而Click不会触发,同时这两次点击,都会触发MouseDown事件。
那么,我在Silverlight上的模拟,至少要达到这个要求。但是考虑到Silverlight上鼠标事件较少,又没有对右键的支持,因此我决定将情况分成两种,一种是双击的第一次会触发单击事件,另一种是双击后,两次点击都不会触发单击事件。我想,后一种也是有一定需求的,比如我做了一个MediaPlayer,我双击是为了全屏观看,单击是为了控制播放、暂停,这时如果双击的时候也触发了单击事件,那么全屏之后就暂停了,那多不爽啊。
其实通过监视MouseLeftButtonDown事件,可以很简单的模拟双击事件,只需要不停记录点击的时间,并且和上一次点击的时间比较,如果时间差小于某一个值,则触发双击事件,某则触发发单击事件。这样做,能够实现WinForm那种单双击的机制,但是无法实现我说的第二种机制,即双击的时候,单击永远不会触发。为了实现这个功能,我加入了一个Timer,当确定无法实现双击的时候,我会通过它来触发单击,这样做会造成单击事件触发有一定的延迟,但是这一点延迟也是微不足道的,毕竟双击的两次点击间隔也不是很长。该类的核心实现代码如下:
1 public class DoubleClickHelper
2 {
3 Constant#region Constant
4 private const long m_DoubleClickInterval = 2000000;
5 #endregion
6
7 Events#region Events
8 public event EventHandler<MouseButtonEventArgs> DoubleClick;
9 public event EventHandler<MouseButtonEventArgs> Click;
10 #endregion Events
11
12 Field#region Field
13 private DispatcherTimer m_Timer;
14 private bool m_IsSpecial;
15 private FrameworkElement m_Element;
16 private long m_DoubleClickTicks = 0;
17 private object Temp_Sender;
18 private MouseButtonEventArgs Temp_EventArgs;
19 #endregion
20
21 Property#region Property
22 private FrameworkElement Element
23 {
24 get { return m_Element; }
25 set
26 {
27 if (value != null && m_Element != value)
28 {
29 m_Element = value;
30 m_Element.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
31 }
32 }
33 }
34
35 public bool IsSpecial
36 {
37 get { return m_IsSpecial; }
38 set
39 {
40 m_IsSpecial = value;
41 if (IsSpecial)
42 {
43 this.m_Timer = new DispatcherTimer();
44 this.m_Timer.Interval = TimeSpan.FromTicks(m_DoubleClickInterval);
45 this.m_Timer.Tick += new EventHandler(m_Timer_Tick);
46 }
47 }
48 }
49 #endregion
50
51 Construstor#region Construstor
52 public DoubleClickHelper(FrameworkElement Element)
53 : this(Element, false) { }
54
55
56 public DoubleClickHelper(FrameworkElement Element, bool IsSpecial)
57 {
58 this.Element = Element;
59 this.IsSpecial = IsSpecial;
60 }
61 #endregion
62
63 Event Handlers#region Event Handlers
64 void m_Timer_Tick(object sender, EventArgs e)
65 {
66 RaiseSingleClick(Temp_Sender, Temp_EventArgs);
67 m_Timer.Stop();
68 }
69
70 void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
71 {
72 if (IsSpecial)
73 {
74 Temp_Sender = sender;
75 Temp_EventArgs = e;
76 m_Timer.Start();
77 }
78
79 if (DateTime.Now.Ticks - m_DoubleClickTicks < m_DoubleClickInterval)
80 {
81 if (IsSpecial)
82 m_Timer.Stop();
83 RaiseDoubleClick(sender, e);
84 }
85 else
86 {
87 if (!IsSpecial)
88 RaiseSingleClick(sender, e);
89 }
90 m_DoubleClickTicks = DateTime.Now.Ticks;
91 }
92 #endregion
93
94 Raise Events#region Raise Events
95 void RaiseDoubleClick(object sender, MouseButtonEventArgs e)
96 {
97 if (DoubleClick != null)
98 DoubleClick(sender, e);
99 }
100
101 void RaiseSingleClick(object sender, MouseButtonEventArgs e)
102 {
103 if (Click != null)
104 Click(sender, e);
105 }
106 #endregion
107 }
对于这个类的用法,我认为很简单,只需要把你希望引发双击事件的UI元素(FrameworkElement)作为参数传递给这个类,并且为其规定双击是否在第一次点击触发单击即可(构造器已经重载,如果只传UI元素进去,那么默认与WinForm的机制是一样的)。但是值得一提的是,这个类是否能够使用,完全取决于它能否handle那个UI元素的MouseLeftButtonDown事件,由于一些特殊的原因,比如在某些类中,已经将MouseLeftButtonDown事件Handle过,并且将e.Handled设为true,那么我们将无法得知它何时触发MouseLeftButtonDown事件,还有如果事件路由被某些不可预测的原因阻击,那么我们也将无法得到MouseLeftButtonDown事件。因此,这个类也存在一定的局限性,不过还好,如果这个类不Work的话,那么你不用它而直接在代码里监视事件也不会Work,我做的也仅仅是对事件的进一步封装而已。
我做了一个测试,代码在这里:http://files.cnblogs.com/wodehuajianrui/DoubleClick.rar