学习WPF路由事件

(建议大家使用工具 snoop,可以到blois.us/Snoop下载安装。)

先来了解下什么是WPF里的路由事件

我们创建一个WPF应用程序,代码如下:

<Window x:Class="Wpfceshi.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" MouseDown="Window_MouseDown"  >
    <Grid MouseDown="Grid_MouseDown" x:Name="grid">
        <Button Height="30" Width="100" Content="点击我" MouseDown="Button_MouseDown"/>
    </Grid>
</Window>

 

using System.Windows;
using System.Windows.Input;

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

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Window被点击");
        }

        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Grid被点击");
        }

        private void Button_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Button被点击");
        }
    }
}

调试运行,鼠标右键点击按钮,会依次弹出三个对话框。(注意一定是鼠标右键,否则引发不了事件)

这里大家也许就会问了,我点击的是按钮,为什么Grid和Window也会引发事件呢?其实这就是路由事件的机制,引发的事件由源元素逐级传到上层的元素,Button—>Grid—>Window,这样就导致这几个元素都接收到了事件。

那么如何让Grid和Window不处理这个事件呢?

我们只需要在Button_MouseDown这个方法中加上e.Handled = true; 这样就表示事件已经被处理,其他元素不需要再处理这个事件了。

private void Button_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Button被点击");
            e.Handled = true;
        }

这时如果我们需要Grid也参与处理这个事件该怎么做呢?我们只需要给他AddHandler即可。

修改代码如下

public Window1()
        {
            InitializeComponent();
            grid.AddHandler(Grid.MouseDownEvent, new RoutedEventHandler(Grid_MouseDown1), true);
        }

再加上这个方法

private void Grid_MouseDown1(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Grid被点击");
        }

到此大家应该对路由事件有大概的认识了吧。

上面我们看到的只是路由事件中的一种方式:气泡。还有两种:隧道、直接。

总结:

气泡事件最为常见,它表示事件从源元素扩散(传播)到可视树,直到它被处理或到达根元素。这样您就可以针对源元素的上方层级对象处理事件。例如,您可向嵌入的 Grid 元素附加一个 Button.Click 处理程序,而不是直接将其附加到按钮本身。气泡事件有指示其操作的名称(例如,MouseDown)。
隧道事件采用另一种方式,从根元素开始,向下遍历元素树,直到被处理或到达事件的源元素。这样上游元素就可以在事件到达源元素之前先行截取并进行处理。根据命名惯例,隧道事件带有前缀 Preview(例如 PreviewMouseDown)。
直接事件类似 .NET Framework 中的正常事件。该事件唯一可能的处理程序是与其挂接的委托。
 
对于隧道事件,大家可以写个小程序测试一下
<Window x:Class="Wpfceshi.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" PreviewMouseDown="Window_PreviewMouseDown"  >
    <Grid PreviewMouseDown="Grid_PreviewMouseDown" x:Name="grid">
        <Button Height="30" Width="100" Content="点击我" PreviewMouseDown="Button_PreviewMouseDown"/>
    </Grid>
</Window>
 

using System.Windows;
using System.Windows.Input;

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

        private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Button被点击");
        }

        private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Grid被点击");
        }

        private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Window被点击");
        }
    }
}

可以看到,隧道事件的传递刚好与气泡事件相反。

 

文章就到这里吧,希望对你有帮助。

posted @ 2009-06-28 22:19  孙鹏  阅读(3549)  评论(5编辑  收藏  举报