首页 > 其他分享 >WPF鼠标、键盘、拖拽事件、用行为封装事件

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

时间:2023-01-27 22:31:16浏览次数:52  
标签:Canvas sender AssociatedObject 事件 WPF Border 鼠标

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

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

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事件

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

2

  • 界面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);
}

用行为封装事件

通过一个案例来讲解

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

2

用事件来实现

主要是通过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

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

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

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中需要对这三个事件进行绑定。行为则可以将这三个事件封装在一起。

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

标签:Canvas,sender,AssociatedObject,事件,WPF,Border,鼠标
From: https://blog.51cto.com/u_15943685/6024377

相关文章

  • WPF鼠标、键盘、拖拽事件、用行为封装事件
    WPF鼠标、键盘、拖拽事件、用行为封装事件本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Beha......
  • C# WPF 报错 CS0103 当前上下文中不存在名称的解决方法
    使用WPF时有时会出现此错误 但是此部分是软件自动生成的,所以不可能出错,有时候重启VS就会好,但是也会遇到重启几次仍然报错的情况这个问题的解决方法是,在调试中打开,启动......
  • WPF的MVVM的数据驱动学习示例
    项目结构  界面数据绑定<Windowx:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="h......
  • WPF的APP生命周期以及全局异常捕获
    WPF的APP生命周期以及全局异常捕获APP生命周期wpf项目目录中有一个App.xaml.cs文件,该文件中App是一个partical类,与之对应的另一partical部分在App.g.i.cs文件中,该文件是......
  • WPF的APP生命周期以及全局异常捕获
    WPF的APP生命周期以及全局异常捕获APP生命周期wpf项目目录中有一个App.xaml.cs文件,该文件中App是一个partical类,与之对应的另一partical部分在App.g.i.cs文件中,该文件是在......
  • 表单中设置了 `keyup.enter.native` 的按键事件,但是回车后没有执行查询,反而会刷新页面
    From: https://www.cnblogs.com/shayloyuki/p/16524712.html 场景添加属性对话框中,input输入后点击enter,不会执行绑定的handleDialogQuery事件,反而会刷新整个页面;......
  • 【Reactor模型】事件驱动模型 - Reactor模型简述
    模型演进抛出一个问题:线程怎样才能高效地处理多个连接的业务?当一个连接对应一个线程时,线程一般采用[read->业务处理->send]的处理流程,如果当前连接没有数据可读,那么线程......
  • 基于EasyX和Raylib的鼠标操作
    EasyX提供的鼠标操作需要结合WindowsAPI使用,比较简陋。官方示例用法是在//https://codebus.cn/yangw/mouse-operation。Raylib可以提供强大的多的鼠标操作。不过......
  • WPF-3D图形
    WPF-3D图形WPF的3D功能可以在不编写任何c#代码的情况下进行绘制,只需要使用xaml即可完成3D图形的渲染。本文主要讲述了WPF-3D中的关键概念,以及常用到的命中测试、2d控件如......
  • js 造事件
    MDN造https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createEventMouseEvent:MDNhttps://developer.mozilla.org/zh-CN/docs/Web/API/MouseEventw3cht......