Search code examples
c#wpfeventseventsetter

Propagate an event from style to MainWindow in WPF


I have a custom style in a separate XAML CustomTabItem.xaml which raises an event like:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                x:Class="myProject.CustomTabItem">
    ...
    ...

    <MenuItem Header="One">
        <MenuItem.Style>
            <Style TargetType="{x:Type MenuItem}">
                <EventSetter Event="Click" Handler="ClickNewSpaceOne"/>
            </Style>
        </MenuItem.Style>
    </MenuItem>

    ...
    ...

</ResourceDictionary>

And this easily raises an event in a file I created called CustomTabItem.xaml.cs:

namespace myProject
{
    partial class CustomTabItem
    {
        private void ClickNewSpaceOne(object sender, RoutedEventArgs e)
        {
            //do stuff here
        }
    }
}

This all works fine, but I now need to raise an event in the MainWindow (of course in the event handler ClickNewSpaceOne) but I cannot figure out how to propagate this event to the MainWindow.

I found this article but it doesn't really look like the same situation, so any different article that I didn't find or any answer I would really appreciate.


Solution

  • The practice of using EventSetter in this case, not the best. And here's why:

    • He is bound to the BAML file that and should be event handler

    Because it, is limited to the global function of the event, he just looking event handler in xaml.cs file. Also, because of this, from MSDN:

    Event setters cannot be used in a style that is contained in a theme resource dictionary.

    • EventSetter can not be set in the Trigger

    Quote from link:

    Because using EventSetter to wire up event handler is a compile-time feature which is plumbed through IStyleConnector interface, there is another interface called IComponentConnector which is used by the XAML compiler to wire up event handler for standalone XAML elements.

    What alternatives?

    1 - Attached dependency property

    Use the attached dependency property and its UIPropertyMetadata, you implement the necessary logic. For example:

    // GetValue
    // SetValue
    
    public static readonly DependencyProperty SampleProperty =
                                          DependencyProperty.RegisterAttached("Sample",
                                          typeof(bool),
                                          typeof(SampleClass),
                                          new UIPropertyMetadata(false, OnSample));
    
    private static void OnSample(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool && ((bool)e.NewValue) == true)
        {
            // do something...
        }
    }
    

    More information can be found here:

    How to inherit Button behaviour in WPF style?

    Quit application from a user Control

    How to clear the contents of a PasswordBox when login fails without databinding?

    2 - Commands

    Command in the WPF is a very powerful. Quote from MSDN:

    The first purpose is to separate the semantics and the object that invokes a command from the logic that executes the command. This allows for multiple and disparate sources to invoke the same command logic, and it allows the command logic to be customized for different targets.

    In this case, they can and should be used in Styles, Templates, DataTemplates. In style, you can set a command like this:

    <Setter Property="Command" 
            Value="{Binding DataContext.YourCommand,
                    RelativeSource={Relative Source AncestorType={x:Type Control}}}">
    

    Also, if you want to reference the command, you can declare the command as a static property then you can use Static extention to reference it.

    3 - Using EventTrigger with Interactivity

    In this case, the command is called by the EventTrigger. For example:

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter" >
            <i:InvokeCommandAction Command="{Binding MyCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    

    More information, can be founded here:

    Using EventTrigger in XAML for MVVM

    Binding WPF events to MVVM Viewmodel commands