Commands and menus

239 views Asked by At

I've got a UserControl I've built which contains a number of Borders that contain TextBlock controls. The Text property of the TextBlocks are set to data from my ViewModel object.

I've added a couple of context menus to the UserControl's resources:

<UserControl.Resources>
    <ContextMenu x:Key="ContextMenu">
        <MenuItem Header="Copy Plate"   Command="cs:CarSystemCommands.CopyPlateCommand" />
        <MenuItem Header="Search Plate" Command="cs:CarSystemCommands.SearchPlateCommand" />
    </ContextMenu>
    <ContextMenu x:Key="TextBoxContextMenu">
        <MenuItem Header="_Copy"        Command="Copy" />
        <MenuItem Header="Copy Plate"   Command="cs:CarSystemCommands.CopyPlateCommand" />
        <MenuItem Header="Search Plate" Command="cs:CarSystemCommands.SearchPlateCommand" />
    </ContextMenu>
</UserControl.Resources>

I've added references to the context menus to my TextBlocks. Here's an example:

<Border Background="#FFFFFF78" 
        BorderBrush="Black" 
        BorderThickness="2" 
        ContextMenu="{StaticResource TextBoxContextMenu}"
        Grid.Column="0" 
        Margin="5,10,5,5">
    <TextBlock FontSize="18" 
               FontWeight="Bold"
               HorizontalAlignment="Center" 
               Margin="5" 
               x:Name="Camera" 
               VerticalAlignment="Center" />
</Border>

Finally, I've added CommbandBindings to the UserControl:

<UserControl.CommandBindings>
    <CommandBinding Command="Copy"                                  CanExecute="CopyCommand_CanExecute" Executed="CopyCommand_Executed" />
    <CommandBinding Command="cs:CarSystemCommands.CopyPlateCommand" CanExecute="CopyCommand_CanExecute" Executed="CopyPlateCommand_Executed" />
</UserControl.CommandBindings>

I have two problems with all of this to date:

  1. When I right click on the controls, I see the menus, but the choices are greyed out. I've got breakpoints placed at the start of the CopyCommand_CanExecute, CopyCommand_Executed and CopyPlateCommand_Executed methods. These breakpoints were hit so far in only one run. I don't know why they were hit that time but not before or since. How do I make sure that the CanExecute method is called every time?

  2. The one time the breakpoints did get hit, the argument's Source property wasn't the control I thought it was, it was the UserControl. I need to know which of the TextBlocks on the UserControl raised the event. How do I do that?

Tony

Edit:

The UserControl also contains a ComboBox control. I've found that the CanExecute methods do get run when I right click on the TextBlocks after I drop down the ComboBox. I don't have to select anything in the combobox, I just have to click on it.

Any ideas what the ComboBox might be doing that my code isn't?

The other thing is that after dropping down the ComboBox, the OriginalSource of the Execute event arguments is the ComboBox. I want to use the same Copy command on several of the TextBlocks, and I need to know which one's Context Menu was used so I know which TextBlock's Text to copy to the clipboard. Help!

2

There are 2 answers

0
Tony Vitabile On BEST ANSWER

This problem has been resolved in my code. It turns out that the problem was that the XAML processor was not able to determine what the target of the commands were. That is, it couldn't figure out where to send the command to.

I was able to fix this using code in my code behind. I'm still interested in a XAML fix for the problem, but I haven't had time to look into it. In the meantime the solution I've implemented works fine.

For those interested, here is the code I wrote which sets the target of the command:

private void FixMenuItems( FrameworkElement element, Func<MenuItem, bool> condition ) {
    foreach ( MenuItem menuItem in element.ContextMenu.Items ) {
        if ( condition( menuItem ) ) {
            menuItem.CommandTarget = this;
        }
    }
}

To use the function, you pass a reference to the control with the context menu you're trying to fix to the method, along with a function that takes a MenuItem as a parameter returns true if the MenuItem's CommandTarget property should be set to the current object.

0
Tony Vitabile On

I've found a partial answer to my problem. I figured out a way that my code can determine which TextBlock's Copy command was executed from the ContextMenu. Instead of using a common ContextMenu for the three TextBlocks that need this command, I created a separate ContextMenu for each TextBlock. I then added a CommandParameter to the Copy menu item for that TextBlock with the name of the property I wanted to copy. Then, in the CopyCommand_Executed event handler, I check the value of the e.Parameter value and copy the value of the appropriate control to the clipboard. This works great.

However, my problem with the CanExecute events not firing until I select something in the ComboBox remains. I still need to figure this one out.

Edit:

Well, everything seems to be working fine now, including the CanExecute events. I was trying various things that must have been breaking something, but now that I've got the CommandParameter working, everything else seems OK. I"m going to mark this as the answer.

Second Edit:

Well, actually, everything is working on the UserControl that I was working on when I posted this. But there are still problems.

The UserControl mentioned above is actually embedded in another UserControl. The first UserControl's content is contained in a StackPanel; I set the StackPanel as a FocusScope and that seems to be when everything started working.

The second control has a StackPanel at the root and a number of Grids inside that. The first Grid holds the first UserControl and some other things. A second Grid below that has two DataGrids that have ContextMenus. Its these ContextMenus that aren't working now.

I've made the StackPanel a FocusScope. The context menus on the first UserControl don't work unless I make it a FocusScope, so it is one, too. Finally, I've made the Grid that holds the two DataGrids a FocusScope.

I think the problem has to do with FocusScopes, but I have no idea how it relates, and no combination that I've tried works. Any ideas?