WPF鼠标、键盘、拖拽事件、用行为封装事件

简介: 本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。

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(objectsender, KeyEventArgse)

{

   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(objectsender, DragEventArgse)

{

   vardata=e.Data.GetData(typeof(Border));

   //canvas.Children.Add(data);//直接这样不可以,因为同一个实例不允许在于两个容器中

   //先在之前的容器中移除,再添加

   panel.Children.Remove(dataasUIElement);

   canvas.Children.Add(dataasUIElement);

 

   //获得鼠标相对于canvas的位置

   varpoint=e.GetPosition((Canvas)sender);

   Canvas.SetLeft(dataasUIElement, point.X);

   Canvas.SetTop(dataasUIElement, point.Y);

}

 

privatevoidBorder_MouseLeftButtonDown(objectsender, MouseButtonEventArgse)

{

   Borderborder=senderasBorder;

   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(objectsender, MouseButtonEventArgse)

{

   //获得承载Border的父对象

   if (_parentCanvas==null)

       _parentCanvas= (Canvas)VisualTreeHelper.GetParent(senderasBorder);

 

   this._isDragging=true;

   //获得相对于border的坐标

   this._mouseCurrentPoint=e.GetPosition(senderasBorder);

   //关键,锁定鼠标即不让鼠标选中其他元素

   (senderasBorder).CaptureMouse();

}

 

privatevoidBorder_MouseLeftButtonUp(objectsender, MouseButtonEventArgse)

{

   if (_isDragging)

   {

       //关键,取消锁定鼠标

       (senderasBorder).ReleaseMouseCapture();

       _isDragging=false;

   }

}

 

privatevoidBorder_MouseMove(objectsender, MouseEventArgse)

{

   if (_isDragging)

   {

       //获得相对于Canvas的坐标

       Pointpoint=e.GetPosition(_parentCanvas);

 

       (senderasBorder).SetValue(Canvas.TopProperty, point.Y-_mouseCurrentPoint.Y);

       (senderasBorder).SetValue(Canvas.LeftProperty, point.X-_mouseCurrentPoint.X);

   }

}

关键点

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

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

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

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

锁定鼠标有两种方式

(senderasBorder).CaptureMouse()//锁定

(senderasBorder).ReleaseMouseCapture();//解锁

 

System.Windows.Input.Mouse.Capture(senderasBorder);//锁定

System.Windows.Input.Mouse.Capture(null);//解锁

用行为来封装

上文中主要是通过MouseLeftButtonDown、MouseLeftButtonUp和MouseMove三个事件来实现,在XAML中需要对这三个事件进行绑定。行为则可以将这三个事件封装在一起。

  1. 使用行为需要nuget安装Microsoft.Xaml.Behaviors.Wpf,FrameWork版本安装System.Windows.Interactivity.WPF
  2. 新建一个类,继承自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(objectsender, System.Windows.Input.MouseEventArgse)

   {

       if (_isDragging)

       {

           // 相对于Canvas的坐标

           Pointpoint=e.GetPosition(_parentCanvas);

           // 设置最新坐标

           this.AssociatedObject.SetValue(Canvas.TopProperty, point.Y-_mouseCurrentPoint.Y);

           this.AssociatedObject.SetValue(Canvas.LeftProperty, point.X-_mouseCurrentPoint.X);

       }

   }

 

   privatevoidAssociatedObject_MouseLeftButtonUp(objectsender, System.Windows.Input.MouseButtonEventArgse)

   {

       if (_isDragging)

       {

           // 释放鼠标锁定

           //this.AssociatedObject.ReleaseMouseCapture();

           System.Windows.Input.Mouse.Capture(null);

           _isDragging=false;

       }

   }

 

   privatevoidAssociatedObject_MouseLeftButtonDown(objectsender, System.Windows.Input.MouseButtonEventArgse)

   {

       this._isDragging=true;

       // Canvas

       if (_parentCanvas==null)

           _parentCanvas= (Canvas)VisualTreeHelper.GetParent(senderasBorder);

       // 当前鼠标坐标

       this._mouseCurrentPoint=e.GetPosition(senderasBorder);

       // 鼠标锁定

       //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;

   }

}

  1. XAML中代码

<Canvas>

   <BorderBackground="Orange"Width="100"Height="50"Canvas.Left="100"Canvas.Top="100">

       <i:Interaction.Behaviors>

           <local:DragMoveBehavior/>

       </i:Interaction.Behaviors>

   </Border>

</Canvas>


相关文章
|
4月前
|
C# 微服务 Windows
模块化革命:揭秘WPF与微服务架构的完美融合——从单一职责原则到事件聚合器模式,构建高度解耦与可扩展的应用程序
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中借鉴微服务架构思想,实现模块化设计。通过将WPF应用分解为独立的功能模块,并利用事件聚合器实现模块间解耦通信,可以有效提升开发效率和系统可维护性。文中还提供了具体示例代码,展示了如何使用事件聚合器进行模块间通信,以及如何利用依赖注入进一步提高模块解耦程度。此方法不仅有助于简化复杂度,还能使应用更加灵活易扩展。
111 0
|
7月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
213 2
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
如何解决WPF中 ScrollViewer 内包含 TreeView 或者 ListBox 等控件时滚轮事件被劫持的问题
|
Java C# 程序员
WPF程序中的弱事件模式
原文:WPF程序中的弱事件模式 在C#中,得益于强大的GC机制,使得我们开发程序变得非常简单,很多时候我们只需要管使用,而并不需要关心什么时候释放资源。但是,GC有的时并不是按照我们所期望的方式工作。 例如,我想实现一个在窗口的标题栏中实时显示当前的时间,一个比较常规的做法如下:     var...
1158 0
|
C# 数据安全/隐私保护
用WPF写一个登录界面,我想在输入完密码后按回车就能够验证登陆,而不需要用鼠标单击登陆按钮
原文:用WPF写一个登录界面,我想在输入完密码后按回车就能够验证登陆,而不需要用鼠标单击登陆按钮 在wpf中,将按钮的IsDefault设置为true ​​​​
1209 0
|
C#
WPF 获得鼠标相对于屏幕的位置,相对于控件的位置
原文:WPF 获得鼠标相对于屏幕的位置,相对于控件的位置 相对于屏幕的位置   第一步:     ///     /// 用于获得鼠标相对于屏幕的位置    ///     public class Win32    {        [StructLayout(LayoutKind.
1160 0
|
C#
WPF 事件触发命令
原文:WPF 事件触发命令 方法一使用mvvmlight: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.
1592 0
|
前端开发 C#
从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator?
原文:从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? ...
1520 0
|
C# 开发框架
Prism for WPF再探(基于Prism事件的模块间通信)
原文:Prism for WPF再探(基于Prism事件的模块间通信) 上篇博文链接 Prism for WPF初探(构建简单的模块化开发框架) 一、简单介绍:   在上一篇博文中初步搭建了Prism框架的各个模块,但那只是搭建了一个空壳,里面的内容基本是空的,在这一篇我将实现各个模块间的通信,在上一篇博文的基础上改的。
1602 0
|
.NET C# Windows
WPF 自定义路由事件
原文:WPF 自定义路由事件 WPF中的路由事件 as U know,和以前Windows消息事件区别不再多讲,这篇博文中,将首先回顾下WPF内置的路由事件的用法,然后在此基础上自定义一个路由事件。 1.WPF内置路由事件   WPF中的大多数事件都是路由事件,WPF有3中路由策略: 具体不多讲,单需要注意的是WPF路由事件是沿着VIsualTree传递的。
1164 0