Click here to Skip to main content
15,885,365 members
Articles / Desktop Programming / WPF
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
4.86/5 (4 votes)
4 Aug 2011CPOL2 min read 75.7K   2   4
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Canada Canada

Comments and Discussions

 
GeneralReason for my vote of 4 Although it's the only possible solu... Pin
GATzilla3-Aug-11 1:02
GATzilla3-Aug-11 1:02 
GeneralReason for my vote of 5 Thanks for the tip. Pin
JohnShen228-Jul-11 14:45
JohnShen228-Jul-11 14:45 
Reason for my vote of 5
Thanks for the tip.
GeneralReason for my vote of 1 This is a hack and a lack of WPF bas... Pin
GATzilla25-Jul-11 16:54
GATzilla25-Jul-11 16:54 
GeneralI'm more of a beginner than an expert. I wonder if you can u... Pin
Mark McE25-Jul-11 15:39
Mark McE25-Jul-11 15:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.