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事件
案例,将某个控件拖拽到另一个区域
界面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);
}
用行为封装事件
通过一个案例来讲解
案例,实现某个控件的随意拖动
用事件来实现
主要是通过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:如果鼠标移动过快,会出现图形不能跟随的情况
情况2:如果有多个元素,会出现选中其他元素的情况
下图演示中,鼠标箭头未松开
锁定鼠标有两种方式
(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


猜你喜欢
- 本文实例讲述了C#检测DataSet是否为空的方法。分享给大家供大家参考。具体如下:下面的代码片段通过判断DataSet的Table数量来判
- 目录断言对象、数组、集合ObjectUtilsStringUtilsCollectionUtils文件、资源、IO 流FileCopyUti
- 我们先假设一个场景想象一下,当一个项目出现bug的时候,恰巧这个时候需要你去修改,而当你打开项目之后,眼前的代码让你有一种特别严重的陌生感,
- 一、swagge简介前后端分离:后端︰后端控制层,服务层,数据访问层【后端团队】前端:前端控制层,视图层【前端团队】前后端通过API进行交互
- 1. 是否需要整合 ?不需要 : 单独使用Springmvc. 需要将原先Spring中的内容通通迁移到Springmvc中. 例如:数据源
- import java.util.ArrayList;import java.util.List;public class Test2 {&
- Android使用RecyclerView1. 什么是RecyclerViewRecyclerView 是 Android-support-
- Fragment是Android honeycomb 3.0开始新增的概念,Fragment名为碎片不过却和Activity十
- 前言在实际开发当中,对于某些关键业务,我们通常需要记录该操作的内容,一个操作调一次记录方法,每次还得去收集参数等等,会造成大量代码重复。 我
- Transfer-Encoding简介transfer-eccoding所描述的是消息请求(request)和响应(response)所附带
- 本文实例讲述了Android实现完整游戏循环的方法。分享给大家供大家参考。具体如下:1. DroidzActivity.java:packa
- 本文实例为大家分享了Unity3D Ui利用shader添加效果的具体代码,供大家参考,具体内容如下// Upgrade NOTE: rep
- 有了Eureka服务注册发现、Hystrix断路器、Ribbon服务调用负载均衡,以及spring cloud config 集群配置中心,
- 一、Stream流介绍在JDK8时,JAVA新增了lambda表达式,它与 java.io 包里的 InputStream和 OutputS
- 上一篇讲解了类型,通过类型来开始本篇的学习;int a[10];上述代码中的a是什么类型呢?相信很多人都知道是一个数组类型,具体来说是一个i
- 前端模板框架为Bootstrap,系统分为前台和后台。后台主要为管理员角色,功能有:商品类型管理、商品管理、订单管理、会员管理、管理员管理等
- 一、概述RocketMQ主要提供了两种消费模式:集群消费以及广播消费。我们只需要在定义消费者的时候通过setMessageModel(Mes
- 前言工作中使用mybatis时我们需要根据数据表字段创建pojo类、mapper文件以及dao类,并且需要配置它们之间的依赖关系,这样的工作
- Java 8 , Lambda + foreach 语法糖, 写起来非常的 cleanpublic static void main(Str
- disruptor不过多介绍了,描述下当前的业务场景,两个应用A,B,应用 A 向应用 B 传递数据 . 数据传送比较快,如果用http直接