I have a template with a Style that uses an EventSetter to set a common event handler to some Hyperlinks. But I want to handle this event not in the resource dictionary's .cs file but in the custom control's .cs file. How can I do this? I am in the process of moving resources into theme .xaml files. I thought about separating the functionality part of the Hyperlink style, but where should I put the event setter? I thought I can use commands, but is there a more concise tehnique that does not require changing each Hyperlink element and works for elements that do not support commands?

I use .NET Framework 4.7.2. I have made a few searches over the web and a simple test example:

App.xaml

<Application x:Class="wpf_test_2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:wpf_test_2"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Dictionary.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:wpf_test_2">
    <ControlTemplate x:Key="MyControlTemplate">
        <ControlTemplate.Resources>
            <Style TargetType="Button">
                <!-- obviously, this does not compile: -->
                <EventSetter Event="Click" Handler="MyHandler"/>
            </Style>
        </ControlTemplate.Resources>
        <UniformGrid Rows="5" Columns="5">
            <Button>A</Button>
            <Button>B</Button>
            <Button>C</Button>
            <Button>D</Button>
            <Button>E</Button>
            <Button>F</Button>
            <Button>G</Button>
            <Button>H</Button>
            <Button>I</Button>
        </UniformGrid>
    </ControlTemplate>
</ResourceDictionary>

MainWindow.xaml

<Window x:Class="wpf_test_2.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:wpf_test_2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:MyControl/>
    </Grid>
</Window>

MyControl.xaml

<Control x:Class="wpf_test_2.MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:wpf_test_2"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
         Template="{DynamicResource MyControlTemplate}">
</Control>

MyControl.xaml.cs

/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : Control
{
    public MyControl()
    {
        InitializeComponent();
    }

    private void MyHandler(object sender, RoutedEventArgs e)
    {
        var b = sender as Button;

        MessageBox.Show($"Button {b.Content.ToString()} was clicked!");
    }
}

Screenshot

screenshot

Expected: the project compiles, and when I click one of the buttons, a MessageBox appears with the content of the button as string.

Actual: the project does not compile.

Thank you.

1

There are 1 best solutions below

0
On

Inspired by Clemens' comment, I created a RoutedUICommand, a corresponding command binding, and setters which set Command and CommandParameter:

AboutWindow.xaml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:cs_timed_silver"
        x:Class="cs_timed_silver.AboutWindow"
        mc:Ignorable="d"  
        Width="448" Height="200" ResizeMode="NoResize"
        WindowStartupLocation="CenterOwner"

        Loaded="Window_Loaded"
        Closed="Window_Closed"

        Style="{DynamicResource AboutWindowStyle}">

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static local:AboutWindow.NavigateCommand}"
                        CanExecute="CommandBinding_CanExecute"
                        Executed="CommandBinding_Executed"/>
    </Window.CommandBindings>
</Window>

AboutWindow.xaml.cs

    public partial class AboutWindow : Window
    {
        public static RoutedUICommand NavigateCommand { get; set; } =
            new RoutedUICommand("", "NavigateCommand", typeof(AboutWindow));

        [...]

        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            System.Diagnostics.Process.Start(
                new System.Diagnostics.ProcessStartInfo((e.Parameter as Uri).AbsoluteUri));
        }

Light.xaml

<Style x:Key="AboutWindowStyle" TargetType="local:AboutWindow">
    [...]
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Window}">
                <ControlTemplate.Resources>
                    [...]
                    <Style TargetType="{x:Type Hyperlink}">
                        <Setter Property="Command" Value="{Binding NavigateCommand}"/>
                        <Setter Property="CommandParameter"
                            Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=NavigateUri}"/>
                        </Style>
                    </ControlTemplate.Resources>
                    [...]