WPF路由事件
冒泡事件和隧道事件
WPF路由事件是一种特殊类型的事件,它提供了更强的传播能力。这种事件可以在元素树中向上冒泡和向下隧道传播,沿传播路径被事件处理程序处理。换句话说,路由事件是针对元素树中的多个侦听器(而不仅仅是引发该事件的对象)调用处理程序的事件。
逻辑数与可视化树
逻辑树由布局组件和控件组成,其最显著的特点是完全由布局组件和控件构成,包括列表类控件中的条目元素,换句话说,它的每个节点都是布局组件或控件。这种树形结构反映了界面的布局和控件的逻辑关系。例如,一个窗口可能包含多个面板,每个面板又包含多个按钮,这就是逻辑树的一种表达方式。
可视化树是用户界面的实际渲染结果,它是由可视化组件构成的。这些可视化组件可以是文本框、图片框、按钮等用户可以直接交互的界面元素。可视化树的构建是基于逻辑树的,但它会根据具体的视觉属性(如大小、位置等)进行渲染。
鼠标的冒泡事件与隧道事件
在WPF中带有Mouse字样的为鼠标事件
不带有Preview字样为鼠标冒泡事件
带有Preview字样为鼠标隧道事件
编写测试程序
在可视化树上所有子节点添加鼠标点击的隧道事件与冒泡事件
<UserControl x:Class="Zhaoxi.EventLesson.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Zhaoxi.EventLesson"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
MouseLeftButtonDown="UserControl_MouseLeftButtonDown"
PreviewMouseLeftButtonDown="UserControl_PreviewMouseLeftButtonDown">
<Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown" Background="Transparent"
ButtonBase.Click="Grid_Click"
PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown"
QueryCursor="Grid_QueryCursor">
<Border Width="200" Height="40" Background="Orange"
MouseLeftButtonDown="Border_MouseLeftButtonDown"
PreviewMouseLeftButtonDown="Border_PreviewMouseLeftButtonDown"/>
</Grid>
</UserControl>
代码中添加相关事件的方法
private void UserControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======UserControl_PreviewMouseLeftButtonDown=======");
}
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======UserControl_MouseLeftButtonDown=======");
}
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_PreviewMouseLeftButtonDown=======");
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_MouseLeftButtonDown=======");
}
private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Border_PreviewMouseLeftButtonDown=======");
}
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Border_MouseLeftButtonDown=======");
}
运行后点击Border在输出窗口中打印结果如下,可以清晰看到事件的前后顺序,先隧道后冒泡
======UserControl_PreviewMouseLeftButtonDown=======
======Grid_PreviewMouseLeftButtonDown=======
======Border_PreviewMouseLeftButtonDown=======
======Border_MouseLeftButtonDown=======
======Grid_MouseLeftButtonDown=======
======UserControl_MouseLeftButtonDown=======
事件拦截
方法中使用e.Handled = true;进行事件拦截
在Grid_PreviewMouseLeftButtonDown处拦截
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_PreviewMouseLeftButtonDown=======");
e.Handled = true;
}
运行后点击Border在输出窗口中打印结果如下
======UserControl_PreviewMouseLeftButtonDown=======
======Grid_PreviewMouseLeftButtonDown=======
事件传递在Grid_PreviewMouseLeftButtonDown处截至,后续的事件不再执行
在Grid_MouseLeftButtonDown处拦截
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_MouseLeftButtonDown=======");
e.Handled = true;
}
运行后点击Border在输出窗口中打印结果如下
======UserControl_PreviewMouseLeftButtonDown=======
======Grid_PreviewMouseLeftButtonDown=======
======Border_PreviewMouseLeftButtonDown=======
======Border_MouseLeftButtonDown=======
======Grid_MouseLeftButtonDown=======
事件传递在Grid_MouseLeftButtonDown处截至,后续的事件不再执行
事件拦截后强制执行
取消WPF中UserControl的MouseLeftButtonDown="UserControl_MouseLeftButtonDown"
改用代码中使用AddHandler方法挂在UserControl的MouseLeftButtonDown事件,并在AddHandler的参数bool handledEventsToo传进true强制执行
public UserControl1()
{
InitializeComponent();
// 强调:鼠标左键的冒泡事件
// 事件消息被Handled的时候,依然执行
this.AddHandler(
MouseLeftButtonDownEvent,
new MouseButtonEventHandler(UserControl_MouseLeftButtonDown),
true);
}
在Border_MouseLeftButtonDown处拦截
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Border_MouseLeftButtonDown=======");
e.Handled = true;
}
运行后点击Border在输出窗口中打印结果如下
======UserControl_PreviewMouseLeftButtonDown=======
======Grid_PreviewMouseLeftButtonDown=======
======Border_PreviewMouseLeftButtonDown=======
======Border_MouseLeftButtonDown=======
======UserControl_MouseLeftButtonDown=======
Grid_MouseLeftButtonDown被拦截,但UserControl_MouseLeftButtonDown被强制执行
特殊情况——Button_Click
编写WPF测试代码
<UserControl x:Class="Zhaoxi.EventLesson.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Zhaoxi.EventLesson"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
PreviewMouseLeftButtonDown="UserControl_PreviewMouseLeftButtonDown">
<Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown" Background="Transparent"
ButtonBase.Click="Grid_Click"
PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown"
QueryCursor="Grid_QueryCursor">
<Button Width="200" Height="40" Background="Orange"
MouseLeftButtonDown="Button_MouseLeftButtonDown"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown"
Click="Button_Click"/>
</Grid>
</UserControl>
代码中添加相关事件的方法
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.Unloaded += UserControl1_Unloaded;
// 强调:鼠标左键的冒泡事件
// 事件消息被Handled的时候,依然执行
this.AddHandler(
MouseLeftButtonDownEvent,
new MouseButtonEventHandler(UserControl_MouseLeftButtonDown),
true);
}
private void UserControl1_Unloaded(object sender, RoutedEventArgs e)
{
}
private void UserControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======UserControl_PreviewMouseLeftButtonDown=======");
}
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======UserControl_MouseLeftButtonDown=======");
}
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_PreviewMouseLeftButtonDown=======");
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Grid_MouseLeftButtonDown=======");
}
private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Border_PreviewMouseLeftButtonDown=======");
}
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Border_MouseLeftButtonDown=======");
}
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Button_PreviewMouseLeftButtonDown=======");
}
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("======Button_MouseLeftButtonDown=======");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("======Button_Click=======");
}
private void Grid_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("======Grid_Click=======");
}
}
运行后点击Button在输出窗口中打印结果如下
======UserControl_PreviewMouseLeftButtonDown=======
======Grid_PreviewMouseLeftButtonDown=======
======Button_PreviewMouseLeftButtonDown=======
======UserControl_MouseLeftButtonDown=======
======Button_Click=======
======Grid_Click=======
输出结果中Button_MouseLeftButtonDown和Grid_MouseLeftButtonDown没有执行但UserControl_MouseLeftButtonDown被执行
是因为UserControl_MouseLeftButtonDown是强制执行,说明Button_PreviewMouseLeftButtonDown执行完成后事件被拦截
Button_Click执行时机在鼠标点击抬起后先执行冒牌事件再执行单击事件
Grid_Click类似Button_Click的冒泡事件,但Grid中没有Button_Click事件所有需要使用ButtonBase.Click="Grid_Click"来侦听Button_Click的冒泡事件,Button_Click的定义再ButtonBase中
特殊情况——TextBox_TextInput
编写WPF测试代码
<StackPanel>
<TextBox TextInput="TextBox_TextInput"
PreviewTextInput="TextBox_PreviewTextInput"
TextChanged="TextBox_TextChanged"/>
<TextBox/>
</StackPanel>
代码中添加相关事件的方法
public partial class KeyboardEventWindows : Window
{
public KeyboardEventWindows()
{
InitializeComponent();
}
private void TextBox_TextInput(object sender, TextCompositionEventArgs e)
{
Debug.WriteLine("=================TextBox_TextInput================");
}
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Debug.WriteLine("=================TextBox_PreviewTextInput================");
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Debug.WriteLine("=================TextBox_TextChanged================");
}
}
输入字符后输出窗口中打印结果如下
TextBox_TextInput事件不会触发,但可以用TextBox_TextChanged替代
当输入回车时TextBox_PreviewTextInput会被触发但TextBox_TextChanged不会