美文网首页
WPF RoutedEvent and HitTest

WPF RoutedEvent and HitTest

作者: 勿念情 | 来源:发表于2019-01-30 17:56 被阅读0次

    学习的时候切忌心浮气躁,慢慢的过每一个知识点,不要漏掉任何细节。不然当遇到细节问题的时候,会恼,会闹,会悔不该当初——花一下午调bug最后只改了一个参数有感。

    相信很多用过WPF的人都知道WPF中的路由事件。一般看书的话,这个知识点也会在前几章讲到。总的来说,也就是

    • WPF的控件都存在于一颗Visual tree当中。
    • 事件在控件中的传递,其实也就是事件在Visual tree中的传递
    • 隧道事件从上往下,由树根开始向叶子传播
    • 冒泡事件从下往上,由子节点开始向根传播

    假设我们有一个Visual tree长这样:

    MainWindow    
    |_Border
      |_Grid
        |_TextBlock
    

    那么如果用户点击了TextBlock。那么会产生什么事件,然后会怎么传递呢?
    答案是

    1. 会产生PreviewMouseDownMouseDown事件
    2. PreviewMouseDown是隧道事件,事件的顺序是MainWindow->Border->Grid->TextBlock
    3. MouseDown是冒泡事件,事件的顺序与之前相反,是TextBlock->Grid->Border->MainWindow

    Tips:
    如何查看WPF中的事件?有一个开源工具snoop可以帮助你。下图是一个实际示例,UI结构以及操作和上述一致。

    example

    好,我们再看一个例子。

    MainWindow    
    |_Border
      |_Grid
        |_TextBlock(Margin="32")
    

    和上个例子不同的地方在于,我们把TextBlock的边距扩大了。这就意味着,我们可以点击在TextBlock的边距上,那么会发生什么呢?先自己想想哦。

    正确答案

    注意这里的border是window中自带的,不是我们自己声明的。所以正确答案是,只传播到了MainWindow。为了区别,我给声明的Border随便起了个名字。

    要是答对了的同学,那不是一般的棒!
    我们这里有两个问题:

    1. PreviewMouseDown只传递到了MainWindow。作为一个隧道事件,没有继续往下传递。
    2. 触发事件的是MainWindow,不是Border也不是Grid

    先解答第一个问题。路由事件的准确触发顺序应该是

    1. 隧道事件从根开始,传播到产生事件的控件为止。如果中间有控件处理(e.Handled = true)掉,就停止传播。
    2. 冒泡事件从产生事件的控件开始,传播到根节点为止。如果中间有控件处理(e.Handled = true)掉,就停止传播。
    3. 系统提供的Preview事件先触发。如果被处理(e.Handled = true)掉,不会在产生对应的冒泡事件。

    第二个问题就很恼人了。总的来说就是

    • 如果没有控件没有被渲染,那么该控件不能被HitTest,也不能被路由事件触发。(参见这里
      也就是说,BorderGrid没能触发,是因为他们没有Background,没有被渲染。如果加上,即使你加的是TransParent,也会有效。
      添加了TransParent为Border的背景
      添加了TransParent为Grid的背景

    也就是说,如果你希望下面的控件能够触发事件。那么让上面的控件不能被HitTest就可以了。我今天遇到的坑是,上面一层自己画的一个框,用的函数是

    dc.DrawGeometry(Brushes.Transparent, new Pen(brush, GraphicsLineWidth), PathGeometry);
    

    改成

    dc.DrawGeometry(null, new Pen(brush, GraphicsLineWidth), PathGeometry);
    

    就好了。

    相关文章

      网友评论

          本文标题:WPF RoutedEvent and HitTest

          本文链接:https://www.haomeiwen.com/subject/nsycsqtx.html