Vimal CK
Vimal CK

Reputation: 3563

How to pass MenuItem as command parameter for its child control

I have something like below. For MenuItem, here I am passing an object of that MenuItem as a CommandParameter. This works fine for me. My MenuItem holds a RadioButton and I want to use the MenuItem CommandParameter value for this RadioButton. Could anyone please help me how to do this. Thanks in Advance.

<MenuItem Header="Name"
          VerticalAlignment="Center"
          VerticalContentAlignment="Center"
          Command="{Binding SortCommand}"
          CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
      <MenuItem.Icon>
           <RadioButton VerticalAlignment="Center"
                        Margin="3"
                        IsChecked="True"
                        GroupName="sort"
                        Command="{Binding SortCommand}"
                        CommandParameter="..." />
      </MenuItem.Icon>
</MenuItem>

Now Command is executing only when I select the MenuItem. I want to do the same when user selects the RadioButton also. Below is the code which I am using for this.

public void OnSortCommandExecuted(object menuItem)
{
   MenuItem menu = menuItem as MenuItem;
   if (menu != null)
   {
      ((RadioButton)menu.Icon).IsChecked = !((RadioButton)menu.Icon).IsChecked;
      this.eAggregator.GetEvent<ImagesSortedEvent>().Publish(menu.Header.ToString());    
   }
}

Upvotes: 1

Views: 3214

Answers (1)

Rohit Vats
Rohit Vats

Reputation: 81233

Like I said in the comments as well, it's not a good practise to pass on UI component as CommandParameter to ViewModel since ViewModel shouldn't know about View.

I would suggest you to have proper binding in ViewModel. Create a bool property in ViewModel and bind with IsChecked DP of radioButton. That ways you don't have to pass any CommandParameter from View, simply check the status of bool property from command execute method.


Now, that why MenuItem can't be accessed from RadioButton?

RadioButton doesn't lie in same Visual tree as that of MenuItem.

So, you can't use RelativeSource to travel upto MenuItem. Also ElementName binding won't work here since this to work both elements should lie in same Visual Tree.

You might find over net to use x:Reference in such cases where two elements doesn't lie in same Visual tree but that won't work here since it will create cyclic dependency.

Last thing, you have to resort with it to use Freezable class object to hold an instance of MenuItem and use that resource in your bindings.

First of all you need to define class deriving from Freezable:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object),
                                     typeof(BindingProxy));
}

and you can use it from XAML like this to pass MenuItem:

    <MenuItem Header="Name"
              x:Name="menuItem"
              VerticalAlignment="Center"
              VerticalContentAlignment="Center"
              Command="{Binding SortCommand}"
              CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
        <MenuItem.Resources>
            <local:BindingProxy x:Key="proxy"
                                Data="{Binding Source={x:Reference menuItem}}"/>
        </MenuItem.Resources>
        <MenuItem.Icon>
            <RadioButton VerticalAlignment="Center"
                         Margin="3"
                         IsChecked="True"
                         GroupName="sort"
                         Command="{Binding SortCommand}"
                         CommandParameter="{Binding Data.CommandParameter,
                                              Source={StaticResource proxy}}"/>
        </MenuItem.Icon>
    </MenuItem>

Ofcourse you need to declare local namespace in XAML.

PS - I would still insist to use first approach to define proper bindings in ViewModel.


UPDATE

If MenuItem is placed under ContextMenu, then RelativeSource binding won't be possible. Approach described above will work in that case.

But in case you are placing MenuItem directly as child of some control (like Menu), RelativeSource binding will work:

CommandParameter="{Binding CommandParameter,
       RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}}"

Upvotes: 1

Related Questions