Search code examples
wpfxamlrouted-events

How to use Interaction.EventTrigger and bubbling custom events in WPF


I am trying to handle a RoutedEvent from a UserControl I built by hooking it up to a command using Interaction.Triggers. The following works--it calls the AddingNewThingCommand:

<WrapPanel>

    <local:MyCustomUserControl Header="MyHeader1"
                               ItemsSource="{Binding List1}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="AddingNewThing">
                <prism:InvokeCommandAction Command="{Binding DataContext.AddingNewThingCommand, ElementName=rootViewElement}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </local:MyCustomUserControl >

    <local:MyCustomUserControl Header="MyHeader2"
                               ItemsSource="{Binding List2}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="AddingNewThing">
                <prism:InvokeCommandAction Command="{Binding DataContext.AddingNewThingCommand, ElementName=rootViewElement}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </local:MyCustomUserControl >

</WrapPanel>

Now I actually have a lot of these user control instances in this wrap panel, so I would prefer to move the interaction triggers to the parent element -- the WrapPanel. The following does not work:

<WrapPanel>
    <i:Interaction.Triggers>
        <!-- Also tried local:MyCustomUserControl.AddingNewThing -->
        <i:EventTrigger EventName="MyCustomUserControl.AddingNewThing">
            <prism:InvokeCommandAction Command="{Binding DataContext.AddingNewThingCommand, ElementName=rootViewElement}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <local:MyCustomUserControl Header="MyHeader1"
                               ItemsSource="{Binding List1}"/>
    <local:MyCustomUserControl Header="MyHeader2"
                               ItemsSource="{Binding List2}"/>
</WrapPanel>

Does EventTrigger work with bubbling events?

My RoutedEvent:

    public static readonly RoutedEvent AddingNewThingEvent = EventManager.RegisterRoutedEvent(
        "AddingNewThing", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyCustomUserControl));
    public event RoutedEventHandler AddingNewThing
    {
        add { AddHandler(AddingNewThingEvent, value); }
        remove { RemoveHandler(AddingNewThingEvent, value); }
    }

Solution

  • Normally, the routed event system permits handlers for any routed event to be attached to any UIElement or ContentElement. That's how the routed events work.

    However, the System.Windows.Interactivity.EventTrigger is not a part of the visual tree and therefore cannot participate in this routed event dispatching. The EventTrigger subscribes directly to the CLR event with given EventName (using Reflection: Type.GetEvent).

    The event source object will be determined using the following order:

    1. If SourceObject property of the EventTrigger is set, try to get that object. If it's not null, use it.
    2. If SourceName of the EventTrigger is set, get the object using a named objects resolver.
    3. Otherwise, use the associated object the EventTrigger is directly attached to.

    The EventName has to be a simple event name. Fully qualified event names (i.e. Type.EventName) are not supported. This is in contrast with System.Windows.EventTrigger, where you can specify the fully qualified routed event name in the RoutedEvent property.

    So in short, you cannot simplify your WrapPanel in that way. Maybe you could use an ItemsControl with a WrapPanel as ItemsPanel and define a DataTemplate containing your MyCustomUserControl with the EventTriggers.