0

I have a view where I handle the whole navigation of the application. Now I want to add an EventTrigger to all the controls of type <vw:NavigationRadioButton/>

<vw:NavigationRadioButton Text="Frisse Conche" RegionName="MainRegion" ViewName="PanoramaFrisse">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PreviewMouseDown">
        <i:InvokeCommandAction Command="{Binding Path=CheckRecipeChangedCommand, Source={vw:AdapterBinding RecipeAdapter}}" CommandParameter="MainRegion;PanoramaFrisse"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</vw:NavigationRadioButton>

Also, if thats possible: I want to pass the value of the properties RegionName and ViewName to my command (as I did manually in this example)

If thats possible, how can I accomplish this?

user2877820
  • 287
  • 4
  • 19
  • *"add ... to all controls of the type"* = style with `TargetType`. Never used Blend though. – Sinatr Nov 20 '17 at 13:22
  • @Sinatr Unfortunately `Interaction.Behaviors` and `Interaction.Triggers` collections are not usable from within `Style` since they are read-only properties (same issue exists for example for `Grid.ColumnDefinitions`). – Grx70 Nov 20 '17 at 13:26
  • @Grx70, [doable](https://stackoverflow.com/q/22321966/1997232). – Sinatr Nov 20 '17 at 14:11

1 Answers1

0

Usual solution to this kind of problems is to use a proper (usually implicit) Style. Unfortunately, it is not possible to populate Interaction.Behaviors nor Interaction.Triggers collections via a style1. At least not directly, although there exist some workarounds, such as this one mentioned by @Sinatr in his comment.

In my opinion however a better solution is to create dedicated helper class with an attached property, which additionally will allow you to kill two birds with one stone and pass proper parameter to the command:

public static class NavigationRadioButtonHelper
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            name: "Command",
            propertyType: typeof(ICommand),
            ownerType: typeof(NavigationRadioButtonHelper),
            defaultMetadata: new PropertyMetadata(null, HandleCommandChanged));

    public static ICommand GetCommand(NavigationRadioButton control)
        => (ICommand)control.GetValue(CommandProperty);

    public static void SetCommand(NavigationRadioButton control, ICommand value)
        => control.SetValue(CommandProperty, value);

    private static void HandleCommandChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            ((NavigationRadioButton)d).AddHandler(
                routedEvent: Mouse.PreviewMouseDownEvent,
                handler: (MouseButtonEventHandler)HandlePreviewMouseDown);
        }
        else
        {
            ((NavigationRadioButton)d).RemoveHandler(
                routedEvent: Mouse.PreviewMouseDownEvent,
                handler: (MouseButtonEventHandler)HandlePreviewMouseDown);
        }
    }

    private static void HandlePreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        var control = (NavigationRadioButton)sender;
        var command = GetCommand(control);
        var parameter = $"{control.RegionName},{control.ViewName}";
        if (command != null && command.CanExecute(parameter))
            command.Execute(parameter);
    }
}

What happens here is when a command is assigned to the property, a handler is added for the Mouse.PreviewMouseDownEvent to the associated object which simply executes the attached command, conveniently constructing the required parameter at the same time. When the command is set to null, the handler is removed. And since this functionality relies solely on a single attached dependency property, it can be easily used in combination with a style:

<Style TargetType="{x:Type vw:NavigationRadioButton}">
    <Setter Property="ns:NavigationRadioButtonHelper.Command" Value="{Binding (...)}" />
</Style>

I'm assuming you do not have the power to modify the NavigationRadioButton class, in which case this logic could be incorporated within that class without the need for a helper class.


1 Collections generally cannot be populated via a style, only assigned. Neither of the two properties are publicly exposed, nor have corresponding setter methods publicly available, and as such can be considered to be read-only in the sense that their values cannot be assigned, only modified.

Grx70
  • 10,041
  • 1
  • 40
  • 55