Я пытаюсь поймать RountedEvent, запущенный из нескольких экземпляров UserControl. Это легко работает в мире без MVVM, перехватывая перенаправленное событие на уровне Window (local:MyControl.MyEvent="Window_CodeBehindHandler"). Но мне нужно перенаправить это событие в модель просмотра, и я изо всех сил пытаюсь заставить настройку Interaction.Triggers вести себя так же. EventTrigger ведет себя так, как ожидалось, если я передаю конкретный SourceName или SourceObject или помещаю Interaction.Triggers внутрь UserControl в XAML, но это не работает так же.
Я безуспешно пробовал различные варианты установки SourceName или SourceObject для типа или имени элемента управления.
Я собрал MRE здесь: WpfInteractionEventTriggerMRE. «Настоящая» реализация имеет ItemsView привязку к коллекции, созданной во время выполнения этих объектов управления, причем происходит гораздо больше, чем одно событие «Выбор», поэтому «просто используйте элемент управления {x} с уже реализованным Click и измените его стиль. "предложения не помогают.
<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>
Я надеюсь, что есть что-то, что я просто пропускаю здесь.





Используемый вами EventTrigger не поддерживает вложенные события. Вам придется либо реализовать свой собственный пользовательский триггер события, либо вы можете просто вызвать команду из обработчика событий Window_Select в окне:
var viewModel = DataContext as YourViewModel;
if (viewModel != null)
viewModel.ElipseSelectedCommand.Execute(null);
Это никоим образом не нарушает шаблон MVVM, поскольку вы вызываете ту же самую команду из того же представления, что и при использовании триггера взаимодействия. MVVM — это не устранение кода из представления, а разделение задач.
Хотя этот триггер пользовательского события тоже выглядит очень просто.
Пользовательский триггер события сработал. MRE в исходном вопросе обновлен. Вот соответствующий обновленный код.
<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>
Согласно пользовательскому триггеру события, слегка обновленная версия:
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;
}
Я понял это сегодня утром. Вместо пользовательского триггера события я собираю прикрепленное поведение и свойство. Я опубликую это как ответ в ближайшее время.