1
votes

I had a simple TextBlock on a View that I added a ContextMenu for. I wanted to wire the Command for the MenuItem to execute a method on the View. After the better part of the day I found only one working solution which leads me to believe I am missing something. There are so many things that just aren't making sense to me.

This is the only way I could get this to work.

<TextBlock Text="Test">
    <TextBlock.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Test Me" Command="w:Command.TestMe" />
        </ContextMenu>
    </TextBlock.ContextMenu>
</TextBlock>

public static class Command
{
    public static readonly RoutedUICommand TestMe= new RoutedUICommand("Test Me", "TestMe", typeof(MainWindow));
}

In MainWindow class:

    public MainWindow()
    { 
        CommandBindings.Add(new CommandBinding(Command.EditAlarm, new ExecutedRoutedEventHandler(EditAlarmDetails), new CanExecuteRoutedEventHandler(CanEditAlarm)));
        InitializeComponent();

        Focus();
    }
    public void CanTestMe(object sender, CanExecuteRoutedEventArgs e)
    {            
        e.CanExecute = true;
    }

    public void TestMeDetails(object sender, ExecutedRoutedEventArgs e)
    {
        TestView view = new TestView();
        view.ShowDialog();
    } 

Why is it when I had the CommandBindings Declared in the XAML I finally got it to work but the MenuItem was always disabled?

<Window.CommandBindings>
    <CommandBinding Command="w:Command.TestMe" Executed="TestMeDetails" CanExecute="CanTestMe" /> 
</Window.CommandBindings> 

Why wasn't I able to just declare a RelayCommand and reference like I normally would from a ViewModel?

public ICommand TestCommand
    {
        get
        {
            if (testCommand == null)
                testCommand = new RelayCommand(param => TestMeDetails(), param => CanTestMe());

            return changeStatusCommand;
        }
    } 

public bool CanTestMe()
{
    return true;
}

public void TestMeDetails() { }

Why does the RoutedUICommand Class have to be a in a seperate Static class to work but have a typeof(MainWindow) instead of the static instance of itself typeof(Command)? There must be a piece of this that I am not understanding. Surely it can't be this convoluted to wire up a simple command event to a button or element?

1

1 Answers

2
votes

Ideally ICommand are supposed to stay in ViewModel but in case you want to create that in code behind of Window, you can achieve that as well (without creating RoutedCommand).

You can bind with command like you do in ViewModel but before that you need to consider these cases.

  1. ContextMenu doesn't lie in same visual tree of the control where it is applied over. So, to do bindings with parent window you have to use PlacementTarget.
  2. Once you get placement target you can do binding with code behind like you do for any command in ViewModel.

This code will work:

XAML

<TextBlock Text="Test"
            Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                                                     AncestorType=Window}}">
    <TextBlock.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Test Me"
                      Command="{Binding PlacementTarget.Tag.TestCommand, 
                               RelativeSource={RelativeSource Mode=FindAncestor, 
                                                  AncestorType=ContextMenu}}"/>
        </ContextMenu>
    </TextBlock.ContextMenu>
</TextBlock>

Notice, I have placed window in Tag and accessed from MenuItem using PlacementTarget.

Code behind

private ICommand testCommand;
public ICommand TestCommand
{
    get
    {
        if (testCommand == null)
            testCommand = new RelayCommand(param => TestMeDetails(),
                                           param => CanTestMe());

        return testCommand;
    }
}

public bool CanTestMe()
{
    return true;
}

public void TestMeDetails() { }