I have a class, MyAttachedEventClassAquarium that defines a custom attached as taken from the MSDN documentation. I have a Window that uses EventTrigger in XAML to hook the event to be handled on the Window's viewmodel. The viewmodel is declared as a local resource.

<Window.Resources>
    <local:WinVM x:Key="myWinVM" />
</Window.Resources>
<i:Interaction.Triggers>
    <i:EventTrigger EventName="NeedsCleaning" SourceName="MyAttachedEventClassAquarium">
        <ei:CallMethodAction MethodName="MyCustomEventWasRaised" TargetObject="{StaticResource myWinVM}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

I use the window's own RaiseEvent to raise the attached event from a button press handler:

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        ((Window1)((Grid)((Button)sender).Parent).Parent).RaiseEvent(new RoutedEventArgs(MyAttachedEventClassAquarium.NeedsCleaningEvent));
    }

Why won't my handler be called?

Thanks in advance.

B.

有帮助吗?

解决方案

(EDITED)

This approach to hooking events for a ViewModel to process looks unlikely to be maintainable. To troubleshoot it it might be a good idea to use the Snoop utility to take the visual and logical trees apart, to make sure that the objects you're casting, all of which implement a RaiseEvent method, are in fact the objects you want.

You might also need to define your SourceName parameter as a resource on your Window.

Those are guesses, and they describe a very different approach to message passing than MVVM patterns call for. Rather than digging into your logical tree like that, which is a very brittle approach, use the documented and proven patterns found below and elsewhere: Access the DataContext of your Window and call the VM method directly. Or, implement an ICommand in your ViewModel class and bind commands to it.

This first approach breaks the most pure approaches to MVVM but, it will work.

private void button1_Click(object sender, RoutedEventArgs e)
    {
       var VM = this.DataContext As WinVM;

       if (VM != null) 
       {
         VM.MyCustomEventWasRaised();
       }
    }

Another approach is to implement an ICommand on your ViewModel class, and configure the Button in the View to use the command:

<Button x:Name="Button1" Command="{Binding NeedsCleaning}" />

This latter will eliminate the need for any XAML code-behind code in most cases.

其他提示

It looks to me like the problem is SourceName -- that needs to be an instance, not the class name? If I add the event source to the visual tree as "aquarium", and change the "SourceName" to "aquarium", then the action gets triggered:

<Window>
    <Window.Resources>
        <local:WinVM x:Key="myWinVM" />
    </Window.Resources>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="NeedsCleaning" SourceName="aquarium">
            <ei:CallMethodAction MethodName="MyCustomEventWasRaised" TargetObject="{StaticResource myWinVM}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <StackPanel>
        <my:MyAttachedEventClassAquarium x:Name="aquarium" />
        <Button x:Name="button" Click="button1_Click" Content="Raise Event" />
    </StackPanel>

</Window>

private void button1_Click(object sender, RoutedEventArgs e)
{
    aquarium.RaiseEvent(new RoutedEventArgs(MyAttachedEventClassAquarium.NeedsCleaningEvent));
}

Just adding: the above approach seems to work only if I both raise the event on the aquarium object, and also use the same aquarium object as the EventTrigger's SourceName. This seems to be something to do with the System.Windows.Interactivity.EventTrigger class (bug?), because when I use the DependencyObject.Triggers.EventTrigger class, the event bubbles like you would expect.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top