Search code examples
wpfmouserouted-events

PreviewMouseDown not tunnelling as expected


I have the following tree on my app:

MainWindow (Window)
> LayoutRoot (Grid)
  > (MyCustomControl)
    > Item1 (Grid)
      > Button (Button1)

MyCustomControl derives from ItemsControl and displays its items on a StackPanel.

MyCustomControl needs to know when the mouse is clicked inside itself, so i have overriden the OnPreviewMouseDown method and expected to get notified of any mouse press inside my control.

What happens: if i click inside Button1 the PreviewMouseDown event travels along the tree and OnPreviewMouseDown is executed as expected. But if i click on Item1 the the PreviewMouseDown event stops just after MainWindow and does not reach even LayoutRoot.

Here are the routed event details i got using Snoop:

Clicking Button1:

(Window)
> (Border)
  > (AdornerDecorator)
    > (ContentPresenter)
      > LayoutRoot (Grid)
        > (MyCustomControl)
          > (Border)
            > (StackPanel)
              > Item1 (Grid)
                > Button1 (Button)
                  > Chrome (ButtonChrome)

Clicking Item1:

(Window)
> (Border)

The event is never reported as handled, so it should go on tunneling as far as i know.

What am i missing here?


Solution

  • You would need to ensure that your custom control is hit-testable. If you have a ControlTemplate like:

    <ControlTemplate>
        <ItemsPresenter />
    </ControlTemplate>
    

    Then your custom control won't be hit-testable, by itself. Even if you have a ControlTemplate like:

    <ControlTemplate>
        <Border Background="{TemplateBinding Background}" ...>
            <ItemsPresenter />
        </Border>
    </ControlTemplate>
    

    Then if Background is null, then your control won't be hit-testable, by itself. If you simply set the Background property to Transparent, then it will be hit-testable.

    The rule of thumb is if your control or one of it's descendents does not render something at a given location (i.e. pixel), even if it's Transparent, then the mouse won't register events for it.