WPF事件

鼠标输入事件

必须继承 FrameworkElement:UIElement

鼠标事件:

MouseEnter

MouseLeave

MouseDown

MouseUp

MouseMove

MouseLeftButtonDown

MouseLeftButtonUp

MouseRightButtonDown

MouseRightButtonUp

MouseDoubleClick

Click:事件:特殊

<Button Content="Mouse Event" MouseLeftButtonDown="Button_MouseLeftButtonDown" />

所有对象都能写出MouseLeftButtonDown这个事件

由于 Button 中,重写了MouseLeftButtonDown事件,先处理的是Click事件,处理完之后事件完毕

所以不会处理Button_MouseLeftButtonDown

Button上的MouseLeftButtonDown无法直接触发,但是可以触发PreviewMouseLeftButtonDown事件

捕获鼠标:
// 第一种 把对象绑定到光标上 两种处理
Mouse.Capture(border);
// 第二种 强制捕捉鼠标
//this.border.CaptureMouse();
========================================
// 释放控件鼠标强制捕捉
Mouse.Capture(null);
//this.border.ReleaseMouseCapture();

键盘输入事件

KeyDown

KeyUp

TextInput

TextChanged

拖拽事件

Drop

DragLeave

DragOver

DragEnter

触控事件与笔势 (Stylus开头的事件)

如果在触控设备中,鼠标事件时灵时不灵的时候,换Touch事件处理

Touch PreviewTouch Win7 Surface

多点触控(Manipulation开头的事件),必须打开IsManipulationEnabled="True“

自定义事件

自定义普通事件

C#代码定义:

using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;

namespace XH.EventLesson
{
    /// <summary>
    /// CustomEventWindow.xaml 的交互逻辑
    /// </summary>
    public partial class CustomEventWindow : Window
    {
        public CustomEventWindow()
        {
            InitializeComponent();

            Test test = new Test();
            test.CustomEvent += Test_CustomEvent;
            test.CustomEvent -= Test_CustomEvent;
            test.CustomStringEvent += Test_CustomStringEvent;
            test.CustomStringEvent -= Test_CustomStringEvent;
        }


        private void Test_CustomStringEvent(object? sender, string e)
        {
            throw new NotImplementedException();
        }
        private void Test_CustomEvent(object? sender, MyEventArags e)
        {
            string value = e.Value;
            throw new NotImplementedException();
        }

    }
    // 普通事件
    class Test
    {
        // Framework 4.0 及以下框架下 可以使用
        public event EventHandler<MyEventArags> CustomEvent;

        // 4.5 及以上可以已使用以下方式
        public event EventHandler<string> CustomStringEvent;

        public Test()
        {
            CustomEvent?.Invoke(this, new MyEventArags() { Value = "Hello" });
            CustomStringEvent?.Invoke(this, "Hello");
        }
    }

    class MyEventArags : EventArgs
    {
        public string Value { get; set; }
    }
}
自定义路由事件

C#代码定义:

using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;

namespace XH.EventLesson
{
    /// <summary>
    /// CustomEventWindow.xaml 的交互逻辑
    /// </summary>
    public partial class CustomEventWindow : Window
    {
        public CustomEventWindow()
        {
            InitializeComponent();
        }
        private void ChildClass_Tap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================ChildClass_Tap================");
        }
        private void ParentClass_Tap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================ParentClass_Tap================");
        }

        private void ParentClass_PreviewTap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================ParentClass_PreviewTap================");
        }

        private void ChildClass_PreviewTap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================ChildClass_PreviewTap================");
        }

        private void Grid_PreviewTap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================Grid_PreviewTap================");
        }

        private void Grid_Tap(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("================Grid_Tap================");
        }
    }
    // 用于路由事件

    public class BaseClass : ContentControl
    {
        // 路由事件的声明与注册(冒泡事件)
        protected static readonly RoutedEvent TapEvent =
            EventManager.RegisterRoutedEvent(
                "Tap",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(BaseClass));

        // 封装
        public event RoutedEventHandler Tap
        {
            add => AddHandler(TapEvent, value);
            remove => RemoveHandler(TapEvent, value);
        }

        // 封装
        public event RoutedEventHandler PreviewTap
        {
            add => AddHandler(PreviewTapEvent, value);
            remove => RemoveHandler(PreviewTapEvent, value);
        }
        // 路由事件的声明与注册(隧道事件)
        protected static readonly RoutedEvent PreviewTapEvent =
            EventManager.RegisterRoutedEvent(
                "PreviewTap",
                RoutingStrategy.Tunnel,
                typeof(RoutedEventHandler),
                typeof(BaseClass));

    }

    public class ParentClass : BaseClass
    {
         
    }

    public class ChildClass : BaseClass
    {
        public ChildClass()
        {
            // 场景 类里数据执行到某个时机的时候 触发上面的路由事件
            Task.Run(async () =>
            {
                await Task.Delay(2000);

                // 触发路由事件
                this.Dispatcher.Invoke(() =>
                {
                    // 冒泡:从里到外,隧道:从外到里
                    // 先Child 在 Parent
                    this.RaiseEvent(new RoutedEventArgs(TapEvent, this));
                    // 先Parent 在 Child
                    this.RaiseEvent(new RoutedEventArgs(PreviewTapEvent, this));
                });
            });
        }

    }
}

XAML代码使用:

<Window x:Class="XH.EventLesson.CustomEventWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:XH.EventLesson"
  mc:Ignorable="d"
  Title="CustomEventWindow" Height="450" Width="800">
  <Grid local:BaseClass.PreviewTap="Grid_PreviewTap" local:BaseClass.Tap="Grid_Tap">
    <local:ParentClass Tap="ParentClass_Tap" PreviewTap="ParentClass_PreviewTap">
      <local:ChildClass Tap="ChildClass_Tap" PreviewTap="ChildClass_PreviewTap" />
    </local:ParentClass>
  </Grid>
</Window>

使用演示:打印log

总结:冒泡:从里到外,隧道:从外到里

路由事件

逻辑树与视觉树

冒泡与隧道

MouseLeftButtonDown

PreviewMouseLeftButtonDown

路由事件的拦截

 private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
     // 禁止路由事件传递
     e.Handled = true;
 }

注意:带有Preview的事件是隧道事件,不带的是路由事件。

路由拦截后的逻辑执行

每次拦截之后,就先执行当前的路由事件,不会触发冒泡事件

键盘输入事件

有个特别注意:TextInput

<TextBox TextInput="TextBox_TextInput" />

TextBox中不处理TextInput事件,如果想要处理值输入事件可以使用PreviewTextInput,或者用TextChanged代替。

拖拽事件

事件:

DragEnter:拖拽进入当前对象

DragLeave:拖拽离开当前对象

DragOver:拖拽在当前对象上移动(和MouseOver差不多)

Drop:拖过来的鼠标松开的那一刻触发

AllowDrop:允许接受拖拽

案例:

XAML代码:

<Window x:Class="XH.EventLesson.DragDropEventWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:XH.EventLesson"
        mc:Ignorable="d"
        Title="DragDropEventWindow" Height="450" Width="800">
    <Grid>
        <!--DragEnter:拖拽进入当前对象
            Drop:拖过来的鼠标松开的那一刻触发-->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Border Background="Red" Height="30" Margin="10" 
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
            <Border Background="Orange" Height="30" Margin="10"
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
            <Border Background="Yellow" Height="30" Margin="10" 
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
            <Border Background="Green" Height="30" Margin="10" 
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
            <Border Background="Blue" Height="30" Margin="10" 
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
            <Border Background="Purple" Height="30" Margin="10"
                    MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
        </StackPanel>

        <!--AllowDrop:允许接受拖拽-->
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel Background="Transparent" Grid.Row="0"
                        AllowDrop="True"
                        DragEnter="StackPanel_DragEnter" 
                        DragLeave="StackPanel_DragLeave"
                        DragOver="StackPanel_DragOver"
                        Drop="StackPanel_Drop"/>
            <StackPanel Background="Transparent" Grid.Row="1"
                        AllowDrop="True"
                        DragEnter="StackPanel_DragEnter" 
                        DragLeave="StackPanel_DragLeave"
                        DragOver="StackPanel_DragOver"
                        Drop="StackPanel_Drop"/>
        </Grid>

    </Grid>
</Window>

C#事件处理:

using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace XH.EventLesson
{
    /// <summary>
    /// DragDropEventWindow.xaml 的交互逻辑
    /// </summary>
    public partial class DragDropEventWindow : Window
    {
        public DragDropEventWindow()
        {
            InitializeComponent();
        }

        private void StackPanel_DragEnter(object sender, DragEventArgs e)
        {
            (sender as StackPanel).Background = Brushes.LightGray;
            Debug.WriteLine("==============StackPanel_DragEnter==============");
        }

        private void StackPanel_DragLeave(object sender, DragEventArgs e)
        {
            (sender as StackPanel).Background = Brushes.Transparent;
            Debug.WriteLine("==============StackPanel_DragLeave==============");
        }

        private void StackPanel_DragOver(object sender, DragEventArgs e)
        {
            Debug.WriteLine("==============StackPanel_DragOver==============");
        }

        private void StackPanel_Drop(object sender, DragEventArgs e)
        {
            Debug.WriteLine("==============StackPanel_Drop==============");

            // 直接传入border
            //FrameworkElement ctl = (FrameworkElement)e.Data.GetData(typeof(Border));
            //(ctl.Parent as StackPanel).Children.Remove(ctl);
            //(sender as StackPanel).Children.Add(ctl);

            // 传入背景色然后绘制新的border
            string brush = (string)e.Data.GetData(typeof(string));
            Border border = new Border()
            {
                Height = 30,
                Margin = new Thickness(10),
                Background = (Brush)new BrushConverter().ConvertFromString(brush),
            };
            border.MouseLeftButtonDown += Border_MouseLeftButtonDown;
            (sender as StackPanel).Children.Add(border);
        }

        private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Border border = sender as Border;
            // 开始一个拖动处理 准备相关数据
            // DragDropEffects 只关注鼠标状态和显示 不负责拖拽对象
            // DragDrop.DoDragDrop(sender, sender, DragDropEffects.Move);
            DragDrop.DoDragDrop(border, border.Background.ToString(), DragDropEffects.Move);
        }
    }
}

效果:实现拖拽功能

Touch与多点触控事件

行为对象

行为对象的定义

行为并不是WPF中的核心的部分,是Expression Blend的设计特性。使用行为的地方,也是可以使用触发器取代的。

行为对象的使用

C#中进行对象的封装:

using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace XH.EventLesson
{
    /// <summary>
    /// BehaviorWindow.xaml 的交互逻辑
    /// </summary>
    public partial class BehaviorWindow : Window
    {
        public BehaviorWindow()
        {
            InitializeComponent();
        }
    }
    // 创建一个行为类 用来封装事件逻辑:对象移动的事件逻辑

    public class DragMoveBehavior:Behavior<FrameworkElement>
    {
        private Point start_point_border;
        private Point start_point_canvas;
        private bool is_moving_border = false;
        private bool is_moving_canvas = false;
        private double start_x_border, start_y_border, start_x_canvas, start_canvas;
        // 执行当前行为所依附的对象的事件挂载
        protected override void OnAttached()
        {
            base.OnAttached();

            // AssociatedObject:所依附的对象
            AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
            AssociatedObject.MouseMove += AssociatedObject_MouseMove; 
            AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp; 
        }
        // 
        protected override void OnDetaching()
        {
            base.OnDetaching();

            AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
            AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
            AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
        }

        private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            is_moving_border = false;

            // 释放控件鼠标强制捕捉
            Mouse.Capture(null);
            //this.border.ReleaseMouseCapture();
            e.Handled = true;
        }

        private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (!is_moving_border) return;

            // 光标移动后 在canvas上的当前位置
            Point p = e.GetPosition(AssociatedObject.Parent as Canvas);
            double offset_x = p.X - start_point_border.X;
            double offset_y = p.Y - start_point_border.Y;

            //Canvas.SetLeft(this.border, offset_x + start_x);
            //Canvas.SetTop(this.border, offset_y + start_y);
            // 移动的差值是光标开始和移动的数值
            Canvas.SetLeft(AssociatedObject, offset_x + start_x_border);
            Canvas.SetTop(AssociatedObject, offset_y + start_y_border);

            e.Handled = true;
        }

        private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 获取光标canvas上的当前位置
            start_point_border = e.GetPosition(AssociatedObject.Parent as Canvas);

            start_x_border = Canvas.GetLeft(AssociatedObject);
            start_y_border = Canvas.GetTop(AssociatedObject);

            is_moving_border = true;

            // 第一种 把对象绑定到光标上 两种处理
            Mouse.Capture(AssociatedObject);

            e.Handled = true;
            // 第二种 强制捕捉鼠标
            //this.border.CaptureMouse();
        }
    }
}

XAML中对行为对象的使用:

<Window x:Class="XH.EventLesson.BehaviorWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:XH.EventLesson"
  mc:Ignorable="d"
  xmlns:b ="http://schemas.microsoft.com/xaml/behaviors"
  Title="BehaviorWindow" Height="450" Width="800">
  <Canvas Name="canvas">
    <Border Width="100" Height="30" Background="Orange" Canvas.Left="0" Canvas.Top="0">
      <!--把所需要的事件,包装为Behaviors对象-->
      <b:Interaction.Behaviors>
        <local:DragMoveBehavior />
      </b:Interaction.Behaviors>
    </Border>
    <Border Width="100" Height="30" Background="Green" Canvas.Left="100" Canvas.Top="30">
      <b:Interaction.Behaviors>
        <local:DragMoveBehavior />
      </b:Interaction.Behaviors>
    </Border>
    <Label Content="Label" Width="100" Height="30" Background="Gray" Canvas.Left="0" Canvas.Top="30">
      <b:Interaction.Behaviors>
        <local:DragMoveBehavior />
      </b:Interaction.Behaviors>
    </Label>
    <!--目前封装的Behaviors不适用button-->
  </Canvas>
</Window>

可以实现自由拖拽功能

目前封装的Behaviors不适用button:

因为Button中的MouseLeftButtonDown事件,被Click给拦截了。

Demo

实现色块拖动功能

XAML代码:

<Canvas x:Name="canvas" Background="Transparent"
        MouseLeftButtonDown="canvas_MouseLeftButtonDown"
        MouseLeftButtonUp="canvas_MouseLeftButtonUp"
        MouseMove="canvas_MouseMove">
    <!--在对象(Border)上的鼠标的按下 MouseLeftButtonDown
        移动鼠标 MouseMove
        释放鼠标 MouseLeftButtonUp-->
    <Border Width="120" Height="40" Background="Orange"
            Canvas.Left="50" Canvas.Top="50"
            MouseLeftButtonDown="Border_MouseLeftButtonDown"
            MouseMove="Border_MouseMove"
            MouseLeftButtonUp="Border_MouseLeftButtonUp"/>
    
    <Border Width="120" Height="40" Background="Green"
            Canvas.Left="150" Canvas.Top="150"
            MouseLeftButtonDown="Border_MouseLeftButtonDown"
            MouseMove="Border_MouseMove"
            MouseLeftButtonUp="Border_MouseLeftButtonUp"/>
</Canvas>

C#事件处理:

private Point start_point;
private bool is_moving = false;
private double start_x, start_y;
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Border border = (Border)sender;
    // 获取光标canvas上的当前位置
    start_point = e.GetPosition(this.canvas);

    start_x = Canvas.GetLeft(border);
    start_y = Canvas.GetTop(border);

    is_moving = true;

    // 第一种 把对象绑定到光标上 两种处理
    Mouse.Capture(border);
    // 第二种 强制捕捉鼠标
    //this.border.CaptureMouse();
}

private void Border_MouseMove(object sender, MouseEventArgs e)
{
    Border border = (Border)sender;
    if (is_moving)
    {
        // 光标移动后 在canvas上的当前位置
        Point p = e.GetPosition(this.canvas);
        double offset_x = p.X - start_point.X;
        double offset_y = p.Y - start_point.Y;

        //Canvas.SetLeft(this.border, offset_x + start_x);
        //Canvas.SetTop(this.border, offset_y + start_y);
        // 移动的差值是光标开始和移动的数值
        Canvas.SetLeft(border, offset_x + start_x);
        Canvas.SetTop(border, offset_y + start_y);
    }
}

private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    is_moving = false;

    // 释放控件鼠标强制捕捉
    Mouse.Capture(null);
    //this.border.ReleaseMouseCapture();
}

效果:能实现色块根据鼠标拖动效果:

实现在canvas中画方块:

XAML代码:

<Canvas x:Name="canvas" Background="Transparent"
  MouseLeftButtonDown="canvas_MouseLeftButtonDown"
  MouseLeftButtonUp="canvas_MouseLeftButtonUp"
  MouseMove="canvas_MouseMove"
  ButtonBase.Click="canvas_Click">
</Canvas>

C#事件处理:

Rectangle rectangle = new Rectangle()
{
    Stroke = Brushes.Red,
    StrokeDashArray = new DoubleCollection { 2, 2 },
    StrokeThickness = 1,
};
private Point current_point;
private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    start_point = e.GetPosition(this.canvas);

    this.canvas.Children.Add(rectangle);

    Canvas.SetLeft(rectangle, start_point.X);
    Canvas.SetTop(rectangle, start_point.Y);

    // 初始化矩形
    rectangle.Width = 0;
    rectangle.Height = 0;

    is_moving = true;
}

private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    is_moving = false;

    this.canvas.Children.Remove(rectangle);
}

private void canvas_MouseMove(object sender, MouseEventArgs e)
{
    if (!is_moving) return;

    current_point = e.GetPosition(this.canvas);

    rectangle.Width = (current_point.X - start_point.X) < 0 ? 0 : current_point.X - start_point.X;
    rectangle.Height = (current_point.Y - start_point.Y) < 0 ? 0: current_point.Y - start_point.Y;
}

效果:实现画矩形

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值