Nasai
Nasai

Reputation: 93

Binding a MenuItem's Command via MVVM

I have a system set up where a ContextMenu hierarchy is populated dynamically using a MVVM architecture. All of my bindings function properly except for Command. My view is a ContextMenu that specifies an ItemContainerStyle.

The ContextMenu's ItemContainerStyle is set to this:

<ContextMenu.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Command" Value="{Binding Command, Mode=OneWay}"/>
        <Setter Property="IsCheckable" Value="{Binding IsCheckable}"/>
        <Setter Property="IsChecked" Value="{Binding IsChecked, Mode=TwoWay}"/>
        <Setter Property="Header" Value="{Binding Label}"/>
        <Setter Property="ItemsSource" Value="{Binding Children}"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=IsVisible}" Value="False">
                <Setter Property="Visibility" Value="Collapsed"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ContextMenu.ItemContainerStyle>

So far, there is no ItemTemplate, as seemingly I have been able to accomplish all the desired functionality in the style.

The ViewModel must be constructed with an instance of the model it wraps, so it seems the DataContext of the ContextMenu cannot be explicitly set to the ViewModel (the compiler complains that it does not have a parameterless constructor. The complaint mentions that a type converter can also be used, though I am unsure as to what that actually means (could solve the issue).

The relevant pieces of my ViewModel are as follows, starting with the two following read-only public facing members that are available to be bound to:

public CommandBinding CommandBinding { get; private set; }
public RoutedCommand Command { get { return CommandBinding.Command as RoutedCommand; } }

CommandBinding and its command are instantiated in the constructor:

CommandBinding = new CommandBinding(new RoutedCommand(), CommandBinding_Executed, CommandBinding_CanExecute);

The methods referred to in that construction simply operate on members of the model, and are implemented as follows:

void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    if (ContextItem.Command != null) ContextItem.Command(ContextItem);
}

void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = ContextItem.IsEnabled;
    if (ContextItem.ExecuteConditions != null) e.CanExecute = ContextItem.ExecuteConditions.GetInvocationList().Cast<ExecuteCondition>().All(s => s() == true);
}

It seems that when the binding to Command actually works, all of the items appear dimmed, as if CanExecute were returning false. However, when I set a breakpoint in CanExecute, execution never breaks at that point (though perhaps this is due to layout threading?). Even if I explicitly set e.CanExecute to true and comment out the other lines in CommandBinding_CanExecute, the items still appear dimmed. In XAML, I have tried binding to both the Command and CommandBinding members with and without Path=, all to the same effect. When I set the binding mode to OneWayToSource, the debugger appropriately complains that the property is read-only and cannot be operated on (I want the ViewModel to provide the command, so this is intended).

I have read the other examples and solutions to related issues. For those that follow the MVVM pattern, I cannot determine how my implementation differs.

For any solution, I must insist that I can still require the ViewModel to be constructed with the model as a parameter, and I would prefer the view to remain all XAML, with no C# code behind.

Upvotes: 1

Views: 1502

Answers (1)

Nasai
Nasai

Reputation: 93

Seems the CommandBinding was the issue. I ended up creating my own implementation of ICommand that allows me to specify Execute and CanExecute delegates on construction... which worked perfectly.

This fixed the problem and the implementation was simple, but it is still unclear to me as to why my use of CommandBindings was not working.

Upvotes: 1

Related Questions