鼠标输入事件
必须继承 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;
}
效果:实现画矩形
标签:sender,void,AssociatedObject,private,start,事件,WPF,border From: https://blog.csdn.net/qq_48148522/article/details/140560693