Reputation: 413
I am trying to catch a RountedEvent
fired from multiple instances of a UserControl
. This works in the non-MVVM world easily by catching the routed event at the Window
level (local:MyControl.MyEvent="Window_CodeBehindHandler"). But I need to forward that event on to the View Model, and I am struggling to get an Interaction.Triggers
setup to behave the same. The EventTrigger
behaves as expected if I pass in a specific SourceName
or SourceObject
or place the Interaction.Triggers
inside of the UserControl in the XAML, but that doesn't work the same.
I have tried various versions of setting SourceName
or SourceObject
to the control type or name without luck.
I've put together an MRE here: WpfInteractionEventTriggerMRE. The "real" implementation has an ItemsView
bound to a collection created at runtime of these control objects, with a lot more going on than a single "Select" event, so "just use an {x} control with Click already implemented and restyle it" suggestions aren't helpful.
<Window x:Class="WpfApp5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp5"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
local:ElipseControl.Select="Window_Select"> <!-- This works as expected -->
<b:Interaction.Triggers> <!-- This does not -->
<b:EventTrigger EventName="Select">
<b:InvokeCommandAction Command="{Binding ElipseSelectedCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<local:ElipseControl Grid.Row="0" ElipseName="One" />
<local:ElipseControl Grid.Row="1" ElipseName="Two" />
</Grid>
</Window>
I am hoping there is something I am just overlooking here.
Upvotes: 0
Views: 870
Reputation: 413
The custom event trigger worked. The MRE in the original question is updated. Here is the relevant updated code.
<Window x:Class="WpfApp5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp5"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<b:Interaction.Triggers>
<local:RoutedEventTrigger RoutedEvent="local:ElipseControl.Select" >
<b:InvokeCommandAction Command="{Binding ElipseSelectedCommand}" PassEventArgsToCommand="True" />
</local:RoutedEventTrigger>
</b:Interaction.Triggers>
<Grid>
<ItemsControl ItemsSource="{Binding Elipses}" />
</Grid>
</Window>
As per custom event trigger, a slightly updated version:
public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
private RoutedEventHandler _eventHandler;
public RoutedEvent RoutedEvent { get; set; }
public RoutedEventTrigger()
{
}
protected override void OnAttached()
{
var associatedElement = GetAssociatedElement();
if (associatedElement != null && RoutedEvent != null)
{
_eventHandler = new RoutedEventHandler(OnRoutedEvent);
associatedElement.AddHandler(RoutedEvent, _eventHandler);
}
}
protected override void OnDetaching()
{
var associatedElement = GetAssociatedElement();
if (associatedElement != null && RoutedEvent != null && _eventHandler != null)
{
associatedElement.RemoveHandler(RoutedEvent, _eventHandler);
}
base.OnDetaching();
}
private FrameworkElement GetAssociatedElement()
{
FrameworkElement associatedElement = null;
if (AssociatedObject is Behavior behavior)
{
associatedElement = ((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
}
else if (AssociatedObject is FrameworkElement frameworkElement)
{
associatedElement = frameworkElement;
}
if (associatedElement == null)
{
throw new ArgumentException("Routed Event Trigger can only be associated to framework elements");
}
return associatedElement;
}
private void OnRoutedEvent(object sender, RoutedEventArgs e) => base.OnEvent(e);
protected override string GetEventName() => RoutedEvent?.Name ?? string.Empty;
}
Upvotes: 1
Reputation: 169200
The EventTrigger
that you are using doesn't support attached events. You will either have to implement your own custom event trigger or you could simply invoke the command from the Window_Select
event handler in the window:
var viewModel = DataContext as YourViewModel;
if (viewModel != null)
viewModel.ElipseSelectedCommand.Execute(null);
This doesn't break the MVVM pattern in any way since you are invoking the very same command from the very same view compared to if you have used an interaction trigger. MVVM isn't about eliminating code from the view - it's about separation of concerns.
Upvotes: 1