Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Passing events from child to parent control's built in handler in WPF

0.00/5 (No votes)
4 Aug 2011 1  
Small sample code to show how to force a parent control to handle an event that was consumed by a child control.

As part of a project, I had to have TextBoxes inside ListView cells to allow text selection inside each cell. However, the TextBox ate up the MouseDown event so the ListView won't select a row when you click on a cell. What made it more complicated was that I also needed multiple selection along with the built-in hotkeys (Ctrl+click, Shift+click, Ctrl+arrow key, etc). To fix this, I subscribed to the PreviewMouseDown event on each TextBox and raised the event again on the ListViewItem (its parent).

Here is what the XAML sort of looked like in my project:

XML
<ListView x:Name ="myListView"
          SelectionMode="Multiple"
          Grid.IsSharedSizeScope="True">
    <ListView.Resources>
        <Style x:Key="ReadOnlyTextBoxStyle" TargetType="TextBox">
            <EventSetter Event="MouseDown" Handler="TextBox_MouseDown" 
                         HandledEventsToo="True"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="IsReadOnly" Value="True"/>
        </Style>
    </ListView.Resources>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition  Width="Auto"  SharedSizeGroup="group1"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <TextBox Style="{StaticResource ReadOnlyTextBoxStyle}"
                         Text="{Binding FieldLabel}"/>
                <Button Text="{Binding FieldValue}"
                        Command="{Binding SeeDetailsCmd}"/>
                         
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>


HandleEventsToo vs. PreviewEvent

In the EventSetter set HandleEventsToo to true to handle already "handled" events. This is better than using Preview events to catch handled events in this case since this will preserve the intended order of events handling; selection event handling is activated on the ListView after MouseDown on TextBox as opposed to the other way around if PreviewMouseDown was used on the TextBox instead.

What If Multiple Controls Needs to Re-Raise Events in parent controls?

In the case described above, I only have one TextBox that needs to raise an event on the parent ListView. However, if I had more than one TextBox or other controls that needs to raise the same Event(e.g. MouseDown), then I should put the EventSetter on the parent/container of the those controls (e.g. ListViewItem) instead and raise the event there. In this way, the parent/container control will catch all the events propagated from child controls and re-raise them to the desired control.

Here is the handler:


C#
private void TextBox_MouseDown(object sender, MouseButtonEventArgs e)
{
    ListViewItem lvi = null;
    TextBox tb = sender as TextBox;
    // Get a reference to the parent ListViewItem control
    DependencyObject temp = tb;
    int maxlevel = 0;
    while (!(temp is ListViewItem) && maxlevel < 1000)
    {
        temp = VisualTreeHelper.GetParent(temp);
        maxlevel++;
    }
    lvi = temp as ListViewItem;
    if (lvi != null){
    {
        //Copy over event arg members and raise it
        MouseButtonEventArgs newarg = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, 
                                          e.ChangedButton, e.StylusDevice);
        newarg.RoutedEvent = ListViewItem.MouseDownEvent;
        newarg.Source = sender;
        lvi.RaiseEvent(newarg);
    }
}


As GATzilla have pointed out in a comment below(under the first alternate), I could have used a WPF DataGrid to achieve the same effect. He/She made some very good points about this tip. However, this was just meant to be an example to illustrate activating parent built-in event handlers for "handled" events. The technique illustrated here is meant to be a "last resort" type of thing to use.

The code above feels more like a hack to me so I'm wondering if anyone can provide a cleaner solution. Thanks in advance. Hope this helps someone.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here