WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

作者:步、步、为营 时间:2021-05-24 00:22:38 

本文主要介绍了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

如果要对某个键进行处理则可以

private void TextBox_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事件

案例,将某个控件拖拽到另一个区域

WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

界面XAML

<Grid>
   <Grid.ColumnDefinitions>
       <ColumnDefinition/>
       <ColumnDefinition/>
   </Grid.ColumnDefinitions>
   <StackPanel x:Name="panel"  Background="#F7F9FA">
       <Border Background="Orange" Height="30" Width="30" MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
   </StackPanel>
   <!--必须设置Background,否则默认为null,null是没有背景和Transparent不同-->
   <!--AllowDrop="True"必须设置-->
   <Canvas x:Name="canvas" Grid.Column="1" Drop="Canvas_Drop" AllowDrop="True"
           Background="Transparent">
   </Canvas>
</Grid>

实现代码

private void Canvas_Drop(object sender, DragEventArgs e)
{
   var data = e.Data.GetData(typeof(Border));
   //canvas.Children.Add(data);//直接这样不可以,因为同一个实例不允许在于两个容器中
   //先在之前的容器中移除,再添加
   panel.Children.Remove(data as UIElement);
   canvas.Children.Add(data as UIElement);

//获得鼠标相对于canvas的位置
   var point = e.GetPosition((Canvas)sender);
   Canvas.SetLeft(data as UIElement, point.X);
   Canvas.SetTop(data as UIElement, point.Y);
}

private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
   Border border = sender as Border;
   DragDrop.DoDragDrop(border, border, DragDropEffects.Copy);
}

用行为封装事件

通过一个案例来讲解

案例,实现某个控件的随意拖动

WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

用事件来实现

主要是通过MouseLeftButtonDown、MouseLeftButtonUp和MouseMove三个事件来实现

XAML界面

<Canvas>
   <Border Background="Orange" Width="100" Height="50" Canvas.Left="100" Canvas.Top="100"
           MouseLeftButtonDown="Border_MouseLeftButtonDown"
           MouseLeftButtonUp="Border_MouseLeftButtonUp"
           MouseMove="Border_MouseMove"
           />
</Canvas>

实现代码

private Canvas _parentCanvas = null;
private bool _isDragging = false;
private Point _mouseCurrentPoint;
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
   //获得承载Border的父对象
   if (_parentCanvas == null)
       _parentCanvas = (Canvas)VisualTreeHelper.GetParent(sender as Border);

this._isDragging = true;
   //获得相对于border的坐标
   this._mouseCurrentPoint = e.GetPosition(sender as Border);
   //关键,锁定鼠标即不让鼠标选中其他元素
   (sender as Border).CaptureMouse();
}

private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
   if (_isDragging)
   {
       //关键,取消锁定鼠标
       (sender as Border).ReleaseMouseCapture();
       _isDragging = false;
   }
}

private void Border_MouseMove(object sender, MouseEventArgs e)
{
   if (_isDragging)
   {
       //获得相对于Canvas的坐标
       Point point = e.GetPosition(_parentCanvas);

(sender as Border).SetValue(Canvas.TopProperty, point.Y - _mouseCurrentPoint.Y);
       (sender as Border).SetValue(Canvas.LeftProperty, point.X - _mouseCurrentPoint.X);
   }
}

关键点

在进行移动的时候,一定要锁定鼠标,也就是不让鼠标可以选中其他元素,如果不锁定会出现以下情况:

情况1:如果鼠标移动过快,会出现图形不能跟随的情况

WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

情况2:如果有多个元素,会出现选中其他元素的情况

下图演示中,鼠标箭头未松开

WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

锁定鼠标有两种方式

(sender as Border).CaptureMouse()//锁定
(sender as Border).ReleaseMouseCapture();//解锁

System.Windows.Input.Mouse.Capture(sender as Border);//锁定
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()在对象销毁时触发

public class DragMoveBehavior : Behavior<Border>
{
   // 当挂载到对应的对象上的时候触发
   protected override void OnAttached()
   {
       base.OnAttached();
//方法与上面相同
       //this.AssociatedObject表示关联的对象
       this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
       this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
       this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
   }

private Canvas _parentCanvas = null;
   private bool _isDragging = false;
   private Point _mouseCurrentPoint;
   private void AssociatedObject_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);
       }
   }

private void AssociatedObject_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
   {
       if (_isDragging)
       {
           // 释放鼠标锁定
           //this.AssociatedObject.ReleaseMouseCapture();
           System.Windows.Input.Mouse.Capture(null);
           _isDragging = false;
       }
   }

private void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
   {
       this._isDragging = true;
       // Canvas
       if (_parentCanvas == null)
           _parentCanvas = (Canvas)VisualTreeHelper.GetParent(sender as Border);
       // 当前鼠标坐标
       this._mouseCurrentPoint = e.GetPosition(sender as Border);
       // 鼠标锁定
       //this.AssociatedObject.CaptureMouse();
       System.Windows.Input.Mouse.Capture(this.AssociatedObject);
   }

// 对象销毁
   protected override void OnDetaching()
   {
       this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
       this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
       this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
   }
}

XAML中代码

<Canvas>
   <Border Background="Orange" Width="100" Height="50" Canvas.Left="100" Canvas.Top="100">
       <i:Interaction.Behaviors>
           <local:DragMoveBehavior/>
       </i:Interaction.Behaviors>
   </Border>
</Canvas>

来源:https://www.cnblogs.com/qsnn/p/17069430.html

标签:WPF,鼠标,键盘,拖拽,封装,事件
0
投稿

猜你喜欢

  • 如何通过Java实现时间轴过程解析

    2022-01-02 00:31:32
  • 如何用java程序(JSch)运行远程linux主机上的shell脚本

    2023-11-24 12:35:58
  • Android编程之防止反编译的实现方法

    2021-10-31 04:23:35
  • SpringMVC接收多个对象的4种方法

    2023-11-23 06:24:18
  • Qt 使用QDialog实现界面遮罩的示例(蒙版)

    2022-10-31 07:58:37
  • 分析Android多主题颜色的相关问题

    2023-01-08 09:55:58
  • Java编程实现基于用户的协同过滤推荐算法代码示例

    2022-09-06 12:44:24
  • Java编程实现统计一个字符串中各个字符出现次数的方法

    2023-01-24 18:02:20
  • SpringData如何通过@Query注解支持JPA语句和原生SQL语句

    2022-08-26 22:07:29
  • Java编程实现对十六进制字符串异或运算代码示例

    2023-11-06 15:58:15
  • SpringBoot集成redis实现分布式锁的示例代码

    2022-08-24 19:21:56
  • Git工具 conflict冲突问题解决方案

    2023-07-27 10:27:30
  • 获取Android手机中所有短信的实现代码

    2023-08-04 16:55:30
  • C# WebApi CORS跨域问题解决方案

    2022-12-21 05:01:33
  • Java容器源码LinkedList原理解析

    2023-06-03 02:06:23
  • springboot 整合hbase的示例代码

    2023-02-11 19:35:20
  • SpringMVC请求数据详解讲解

    2021-09-13 08:56:56
  • SpringBoot使用WebSocket实现前后端交互的操作方法

    2022-07-06 12:34:35
  • 使用@RequestBody配合@Valid校验入参参数

    2023-05-04 22:36:09
  • 基于rocketmq的有序消费模式和并发消费模式的区别说明

    2021-10-29 08:41:02
  • asp之家 软件编程 m.aspxhome.com