Page view counter

Jesse Liberty - Silverlight Geek

More Signal Less Noise

Beta 2 Event Bubbling

There are many great changes in Beta 2, and some are designed to make Silverlight more like WPF. One side effect of this is that event bubbling has changed just a bit and that bit breaks some of the examples I love to use.

In Beta 1, the distinction was that all the controls handled their own click events but the more primitive events such as many of the mouse events were allowed to bubble. This allowed for interesting if not terribly useful demonstrations in which I placed a check box inside a button and was able to demonstrate that if I were to use the click event on the check box the button would never see the click, but if I used a MouseLeftButtonDown, hey! Presto! the button did see the event.

This has now changed in Beta 2 to make Silverlight behave more like WPF. By and large (other than breaking my demo) this is a good thing.  The consistency now is that objects that directly derive from UI element do support event bubbling for the mouse events (that is,  Ellipse, Glyphs, Image, InkPresenter, Line, MediaElement, Path, Polygon, Polyline, Rectangle and TextBlock.)  In addition, the container classes do.

Thus, if you set up the following bizarre application in which you have a grid, with a grid in it, that contains a stack panel that in turn contains a rectangle, a TextBlock, a check box, a button and a listbox (the last to show the results)…

<UserControl x:Class="EventBubbling.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="800">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid x:Name="InterimGrid" >
            <StackPanel x:Name="myStackPanel" Orientation="Vertical"  >
                <Rectangle x:Name="myRect" Width="30" Height="30" Fill="Blue"
                    Stroke="Red" StrokeThickness="2" Margin="0,10,0,10" />
                <TextBlock x:Name="myTextBlock" Text="Hello"  
                    HorizontalAlignment="Center" FontSize="14" Margin="0,10,0,10"/>
                <CheckBox x:Name="myCheckBox" Content="Check Me" Width="80" 
                    Height="40" Margin="0,10,0,10" HorizontalAlignment="Center" />
                <Button x:Name="myButton" Content="TinyButton" Height="30" 
                    Width="70" Margin="0,10,0,10" FontSize="12" HorizontalAlignment="Center" />
                <ListBox x:Name="Messages" Width="150" Height="400" />      
        </StackPanel>
      </Grid>
    </Grid>
</UserControl>

and you add MouseLeftButtonDown event handlers for everything (except the list box):

void Page_Loaded(object sender, RoutedEventArgs e)
{
    LayoutRoot.MouseLeftButtonDown += new MouseButtonEventHandler(
        LayoutRoot_MouseLeftButtonDown);
    InterimGrid.MouseLeftButtonDown += new MouseButtonEventHandler(
        InterimGrid_MouseLeftButtonDown);
    myStackPanel.MouseLeftButtonDown += new MouseButtonEventHandler(
        myStackPanel_MouseLeftButtonDown);
    myRect.MouseLeftButtonDown += new MouseButtonEventHandler(
        myRect_MouseLeftButtonDown);
    myTextBlock.MouseLeftButtonDown += new MouseButtonEventHandler(
        myTextBlock_MouseLeftButtonDown);
    myCheckBox.MouseLeftButtonDown += new MouseButtonEventHandler(
        myCheckBox_MouseLeftButtonDown);
    myButton.MouseLeftButtonDown += new MouseButtonEventHandler(
        myButton_MouseLeftButtonDown);
}

 

 

 

where all the handlers do the same thing, they add to the list box (there are more efficient ways to write this!)

void InterimGrid_MouseLeftButtonDown(
    object sender, MouseButtonEventArgs e)
{
    Messages.Items.Add(" InterimGrid ");
}

What you find is that when you click on the rectangle or the text block the event bubbles all the way to the top most grid, but if you click on the checkbox or the button the event is “handled” by that control and is not bubbled at all.

BubblingNoMore

In the image, I’ve clicked on the rectangle and the event was registered by the rectangle, the StackPanel that encloses it, the Grid that holds the StackPanel and the outermost grid.  I’m now clicking on the button, and while the button is registering the mouse down, it is not passing it along to anyone else.

One fascinating aspect of this (at least to me, but I don’t get out much) is that if I inject a button into the middle of the stream, by putting the rectangle, textblock checkbox and button inside an outer button…

<UserControl x:Class="EventBubbling.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="600">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid x:Name="InterimGrid" >
            <Grid.RowDefinitions>
                <RowDefinition Height="120" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Button x:Name="OuterButton" Width="300" Height="100" Grid.Row="0">
                <StackPanel x:Name="myStackPanel" Orientation="Horizontal"  >
                    <Rectangle x:Name="myRect" Width="50" Height="30" Fill="Blue"
                        Stroke="Red" StrokeThickness="2" Margin="5,0,5,0" />
                    <TextBlock x:Name="myTextBlock" Text="Hello"  
                        VerticalAlignment="Center" FontSize="14" Margin="5,0,5,0"/>
                    <CheckBox x:Name="myCheckBox" Content="Check Me" Width="80" 
                        Height="40" Margin="5,0,5,0" VerticalAlignment="Center" />
                    <Button x:Name="myButton" Content="TinyButton" Height="30" 
                        Width="70" Margin="5,0,5,0" FontSize="12" VerticalAlignment="Center" />
                </StackPanel>
            </Button>
            <ListBox x:Name="Messages" Width="150" Height="400" Grid.Row="1"/>      
        </Grid>
    </Grid>
</UserControl>

The key changes are that I’ve created two rows (one for the controls, one for the list box) and injected a button above the stack panel that holds the stack panel and all its contents (now horizontal). Let’s run the same experiment…

BubblingNoMore2

Once again, I clicked on the Rectangle, and then was clicking on the button when I took the image. Once again the button had no bubbling, but notice that the Rectangle did not bubble as far as it had previously. After it hit the stack panel it hit the outer button which ate the MouseLeftButtonDown event, and did not pass it along to any containers above it.

All of this is as it should be, and will end a lot of confusion for folks coming over from WPF, but if you learned about bubbling from me, then you’ll want to know about this change.

(Source code posted when Beta2 Ships)

Thanks

Comments

johnnystock said:

That does make more sense to me now.  

I eagerly await more Beta 2 information as well (as well as a keyboard that corrects my spelling on the fly, that one may have to wait longer though)

:)

# June 3, 2008 4:03 PM

chris_vickerson said:

can we *see* the list of new features too? :D

# June 3, 2008 8:09 PM

Page Brooks said:

Silverlight 2 - Beta 2 arrives this week!

# June 3, 2008 10:06 PM

jesseliberty said:

>> can we *see* the list of new features too <<

The minute I have a comprehensive list, I will publish it or a link to it.

# June 4, 2008 8:39 AM

Dew Drop – June 4, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop &ndash; June 4, 2008 | Alvin Ashcraft's Morning Dew

# June 4, 2008 8:39 AM

kamii47 said:

Is there any news about Expression blend 2.5 new beta

# June 4, 2008 1:12 PM

Beta 2 Event Bubbling said:

Pingback from  Beta 2 Event Bubbling

# June 5, 2008 10:26 PM

Lyynx said:

When I wire up a MouseLeftButtonDown event to a slider control the event is not raised when I click the button. Is this because the container it is in is handling the event?

My code no longer works;

           theSlider.MouseLeftButtonDown += new MouseButtonEventHandler(theSlider_MouseLeftButtonDown);

           theSlider.MouseEnter += new MouseEventHandler(theSlider_MouseEnter);

           theSlider.ValueChanged +=new RoutedPropertyChangedEventHandler<double>(theSlider_ValueChanged);

Will test the theory that something else is handling the event first.

thanks

Stephen

# June 10, 2008 3:23 AM

brauliod said:

Jesse,

 Mmmm... I used the OnLButtonDown in the root canvas to perform drag and drop operations, now this doesn't work.

 As workarounds I have tought:

 - Convert all my textblocks to textboxes and subscribe to the Left Button Down event, but.. can I easily or manually resend the message to the root canvas?

 - A nasty trick is to place a transparent area, ... like we have done with the nasty HTML DIV's... but I think there must be a cleaner solution.

 Thanks,

   Braulio

# June 13, 2008 3:51 AM

Flyte said:

I've spent all day trawling Google, MSDN and Silverlight.net for an answer to firing MouseLeftButtonDown/Up events for a Slider, but thus far none of the solutions have worked.

I've tried putting grids / canvasses behind the slider, but it just doesn't fire when I use the slider. If I put a rectangle over the slider and make it invisible, I can't use the slider but the events do fire!

Please Jesse, could you fix it for me?!

Thanks,

Ellis

# June 26, 2008 7:03 PM