WPF Event Bubbling and Routed Events
July 16, 2013 Leave a comment
Consider a User Control with a single button on it:
- <UserControl x:Class="Tff.ButtonClickBubble.MainUserControl"
- 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"
- mc:Ignorable="d" Height="100" Width="100">
- <Grid Background="Red" Margin="0,0,0,0">
- <Button x:Name="MainButton"
- Content="Push Me" HorizontalAlignment="Left"
- Margin="19,36,0,0" VerticalAlignment="Top"
- Width="62" Height="25"
- Click="MainButton_Click"/>
- </Grid>
- </UserControl>
In the code behind, the click event shows a dialog
- public partial class MainUserControl : UserControl
- {
- public MainUserControl()
- {
- InitializeComponent();
- }
- private void MainButton_Click(object sender, RoutedEventArgs e)
- {
- MessageBox.Show(e.Source.ToString());
- }
- }
After hitting F6 so the control will show in my toolbox, I then put an instance of that control on a basic page like so:
- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:Tff.ButtonClickBubble" x:Class="Tff.ButtonClickBubble.MainWindow"
- Title="MainWindow" Height="236" Width="184">
- <Grid x:Name="MainGrid" Background="AliceBlue">
- <local:MainUserControl HorizontalAlignment="Left" Margin="35,44,0,0" VerticalAlignment="Top"/>
- </Grid>
- </Window>
When I hit F5, I get the expected MessageBox:
So now I want the main window to intercept that button click and pop its own dialog box. The Window class does not have a click event – rather it has a mousedown event. By adding the mouse down event hander to the main window, I have this:
- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:Tff.ButtonClickBubble" x:Class="Tff.ButtonClickBubble.MainWindow"
- Title="MainWindow" Height="236" Width="184" MouseDown="Window_MouseDown">
And in the code behind:
- private void Window_MouseDown(object sender, MouseButtonEventArgs e)
- {
- MessageBox.Show(e.Source.ToString());
- }
Unfortunately, that doesn’t work. The button click event swallows the event so the MouseDown only fires on the part of the window where the button is not:
So I went into StackOverflow and I found this post that I think describes the problem. So I changed the MouseDown to the PreviewMouseDown event and sure enough, I can handle the event from the main screen:
But the ClickEvent on the button is not being fired. I then added a e.handled = false but that did not help:
- private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
- {
- MessageBox.Show(e.Source.ToString());
- e.Handled = false;
- }
So the answer marked as correct on stack overflow does apply here. The answer below the marked as correct answer is relevant. I tried to add a handler to the Grid (Windows don’t have AddHandler methods) like this:
- public MainWindow()
- {
- InitializeComponent();
- MainGrid.AddHandler(MouseDownEvent, new MouseButtonEventHandler(MainGrid_MouseDown), true);
- }
- private void MainGrid_MouseDown(object sender, MouseButtonEventArgs e)
- {
- MessageBox.Show(e.Source.ToString());
- }
The problem is that it is not working either. Fortunately, the answer below THAT one does work:
- <Window
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:Tff.ButtonClickBubble" x:Class="Tff.ButtonClickBubble.MainWindow"
- Title="MainWindow" Height="236" Width="184" >
- <Grid x:Name="MainGrid" Background="AliceBlue" Button.Click="MainGrid_MouseDown">
- <local:MainUserControl HorizontalAlignment="Left" Margin="35,44,0,0" VerticalAlignment="Top"/>
- </Grid>
- </Window>
And
- public partial class MainWindow : Window
- {
- public MainWindow()
- {
- InitializeComponent();
- }
- private void MainGrid_MouseDown(object sender, RoutedEventArgs e)
- {
- MessageBox.Show(e.Source.ToString());
- }
- }
They key thing is wiring up the Grid like this: Button.Click="MainGrid_MouseDown"