WPF鼠标、键盘、拖拽事件、用行为封装事件
本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。
Windows中的事件通过消息机制来完成,也就是Windows系统来捕获用户输入(如鼠标点击、键盘输入),然后Windows发送一个消息给应用程序,应用程序进行具体的处理。在Winform中,窗体中每个控件都是有独立的句柄,也就是每个控件都可以收到Windows系统传来的消息,但是在WPF中,窗体中的控件是没有句柄的,所以只能是窗体进行消息捕获,WPF框架经过处理再传递给相应的控件。这是WPF和Winform在事件处理上的不同之处。
鼠标事件
常用的鼠标事件包括:
MouseEnter、MouseLeave、MouseDown、MouseUp、MouseMove、MouseLeftButtonDown、MouseLeftButtonUp、MouseRightButtonDown、MouseRightButtonUp、MouseDoubleClick
值得注意的是诸如Button一类的控件,具有Click事件,它其实是仍然是调用了MouseLeftButtonDown等底层事件,然后进行截断,也就是说Button控件只能调用Click事件而不能调用MouseLeftButtonDown事件,因为在Click事件中,调用了MouseLeftButtonDown事件,而且应用了
e.Handled = true;
阻止事件向下传下去。如果要在Click事件之前进行事件处理,则可以使用
PreviewMouseLeftButtonDown
事件。
键盘输入事件
用的最多的键盘输入事件有:
KeyDown、KeyUp、TextInput
如果要对某个键进行处理则可以
privatevoidTextBox_KeyDown(object sender,KeyEventArgs e){if(e.Key == Key.Enter){//e.Handled = true;//表示已经处理完成//具体逻辑}}
注意TextBox是不能捕获到
TextInput
事件的,只能捕获到
KeyDown、TextChanged
等事件,但也可以捕获到
PreviewTextInput
事件,事件捕获顺序是
KeyDown-PreviewTextInput-TextChanged
。
案例:做一个搜索栏,输入文字后回车搜索
实现方式1:可以在TextBox上增加KeyDown事件,捕获Key.Enter键。
实现方式2:增加一个Button按钮,设置
<Button Content="搜索" IsDefault="True"/>
拖拽事件
拖拽事件包括:Drop、DragLeave、DragOver、DragEnter事件
案例,将某个控件拖拽到另一个区域
- 界面XAML
<Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><StackPanelx:Name="panel"Background="#F7F9FA"><BorderBackground="Orange"Height="30"Width="30"MouseLeftButtonDown="Border_MouseLeftButtonDown"/></StackPanel><!--必须设置Background,否则默认为null,null是没有背景和Transparent不同--><!--AllowDrop="True"必须设置--><Canvasx:Name="canvas"Grid.Column="1"Drop="Canvas_Drop"AllowDrop="True"Background="Transparent"></Canvas></Grid>
- 实现代码
privatevoidCanvas_Drop(object sender,DragEventArgs e){var data = e.Data.GetData(typeof(Border));//canvas.Children.Add(data);//直接这样不可以,因为同一个实例不允许在于两个容器中//先在之前的容器中移除,再添加
panel.Children.Remove(data asUIElement);
canvas.Children.Add(data asUIElement);//获得鼠标相对于canvas的位置var point = e.GetPosition((Canvas)sender);
Canvas.SetLeft(data asUIElement, point.X);
Canvas.SetTop(data asUIElement, point.Y);}privatevoidBorder_MouseLeftButtonDown(object sender,MouseButtonEventArgs e){Border border = sender asBorder;
DragDrop.DoDragDrop(border, border, DragDropEffects.Copy);}
用行为封装事件
通过一个案例来讲解
案例,实现某个控件的随意拖动
用事件来实现
主要是通过
MouseLeftButtonDown、MouseLeftButtonUp和MouseMove
三个事件来实现
- XAML界面
<Canvas><BorderBackground="Orange"Width="100"Height="50"Canvas.Left="100"Canvas.Top="100"MouseLeftButtonDown="Border_MouseLeftButtonDown"MouseLeftButtonUp="Border_MouseLeftButtonUp"MouseMove="Border_MouseMove"/></Canvas>
- 实现代码
privateCanvas _parentCanvas =null;privatebool _isDragging =false;privatePoint _mouseCurrentPoint;privatevoidBorder_MouseLeftButtonDown(object sender,MouseButtonEventArgs e){//获得承载Border的父对象if(_parentCanvas ==null)
_parentCanvas =(Canvas)VisualTreeHelper.GetParent(sender asBorder);this._isDragging =true;//获得相对于border的坐标this._mouseCurrentPoint = e.GetPosition(sender asBorder);//关键,锁定鼠标即不让鼠标选中其他元素(sender asBorder).CaptureMouse();}privatevoidBorder_MouseLeftButtonUp(object sender,MouseButtonEventArgs e){if(_isDragging){//关键,取消锁定鼠标(sender asBorder).ReleaseMouseCapture();
_isDragging =false;}}privatevoidBorder_MouseMove(object sender,MouseEventArgs e){if(_isDragging){//获得相对于Canvas的坐标Point point = e.GetPosition(_parentCanvas);(sender asBorder).SetValue(Canvas.TopProperty, point.Y - _mouseCurrentPoint.Y);(sender asBorder).SetValue(Canvas.LeftProperty, point.X - _mouseCurrentPoint.X);}}
关键点:
在进行移动的时候,一定要锁定鼠标,也就是不让鼠标可以选中其他元素,如果不锁定会出现以下情况:
情况1:如果鼠标移动过快,会出现图形不能跟随的情况
情况2:如果有多个元素,会出现选中其他元素的情况
下图演示中,鼠标箭头未松开
锁定鼠标有两种方式
(sender asBorder).CaptureMouse()//锁定(sender asBorder).ReleaseMouseCapture();//解锁
System.Windows.Input.Mouse.Capture(sender asBorder);//锁定
System.Windows.Input.Mouse.Capture(null);//解锁
用行为来封装
上文中主要是通过
MouseLeftButtonDown、MouseLeftButtonUp和MouseMove
三个事件来实现,在XAML中需要对这三个事件进行绑定。行为则可以将这三个事件封装在一起。
- 使用行为需要nuget安装
Microsoft.Xaml.Behaviors.Wpf
,FrameWork版本安装System.Windows.Interactivity.WPF
- 新建一个类,继承自
Behavior<T>
,类中重写OnAttached()和OnDetaching()方法。
OnAttached()表示当挂载到对应的对象上的时候触发
OnDetaching()在对象销毁时触发
publicclassDragMoveBehavior:Behavior<Border>{// 当挂载到对应的对象上的时候触发protectedoverridevoidOnAttached(){base.OnAttached();//方法与上面相同//this.AssociatedObject表示关联的对象this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;}privateCanvas _parentCanvas =null;privatebool _isDragging =false;privatePoint _mouseCurrentPoint;privatevoidAssociatedObject_MouseMove(object sender,System.Windows.Input.MouseEventArgs e){if(_isDragging){// 相对于Canvas的坐标Point point = e.GetPosition(_parentCanvas);// 设置最新坐标this.AssociatedObject.SetValue(Canvas.TopProperty, point.Y - _mouseCurrentPoint.Y);this.AssociatedObject.SetValue(Canvas.LeftProperty, point.X - _mouseCurrentPoint.X);}}privatevoidAssociatedObject_MouseLeftButtonUp(object sender,System.Windows.Input.MouseButtonEventArgs e){if(_isDragging){// 释放鼠标锁定//this.AssociatedObject.ReleaseMouseCapture();
System.Windows.Input.Mouse.Capture(null);
_isDragging =false;}}privatevoidAssociatedObject_MouseLeftButtonDown(object sender,System.Windows.Input.MouseButtonEventArgs e){this._isDragging =true;// Canvasif(_parentCanvas ==null)
_parentCanvas =(Canvas)VisualTreeHelper.GetParent(sender asBorder);// 当前鼠标坐标this._mouseCurrentPoint = e.GetPosition(sender asBorder);// 鼠标锁定//this.AssociatedObject.CaptureMouse();
System.Windows.Input.Mouse.Capture(this.AssociatedObject);}// 对象销毁protectedoverridevoidOnDetaching(){this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;}}
- XAML中代码
<Canvas><BorderBackground="Orange"Width="100"Height="50"Canvas.Left="100"Canvas.Top="100"><i:Interaction.Behaviors><local:DragMoveBehavior/></i:Interaction.Behaviors></Border></Canvas>
版权归原作者 步、步、为营 所有, 如有侵权,请联系我们删除。