views:

90

answers:

1

Hi,

I have a wpf window with several text box controls. I need to apply a common style that would apply a context menu to each control and i have defined it globally as follows,

<ContextMenu x:Key="textBoxMenu">
        <Separator/>
        <MenuItem Header="Affirm" 
                  Command="{Binding Path=AffirmCommand}" 
                  CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBox},AncestorLevel=1}}"/>                      
    </ContextMenu>

    <Style TargetType="{x:Type TextBox}" x:Key="TextBoxAffirmMenuStyle">
        <Setter Property="ContextMenu" Value="{DynamicResource textBoxMenu}" />
    </Style>

I Have used a Command to execute the appropriate method depending on the target of the context menu, which is in this case the text box.

To identify the controls uniquely, i have set the "Tag" property of each control with a unique string and i access this tag from the command parameter which is set to the target text box Control itself.

private bool CanAffirmExecute(object param)
        {

            string columnName = (param as FrameworkElement).Tag as string;

            if (this.CheckIsAffirmed(columnName))
                return true;
            else
                return false;
        }

private void AffirmExecute(object param)
        {

            string columnName = (param as FrameworkElement).Tag as string;

            this.Affirm(columnName);
        }

The problem with this is that once the command parameter gets set to a particular control, it will not change on subsequent context menu operations when right clicked on a different control. the Command parameter remains static and gets only the tag value set in the first control.

How can i get this to work so that i can access each of the tag values of the controls using the command?

thanks.

A: 

ContextMenu is at the root of its own visual tree, so any binding using RelativeSource.FindAncestor does not go past the ContextMenu.

A work around is to use a two stage binding with the PlacementTarget property as follows, and to analyse the object parameter in the method OnAffirmCommand(object obj) to control your behaviour. In this case the object is the actual TextBox.

Here is the context menu definition:

<Window.Resources>
  <ContextMenu x:Key="textBoxMenu">
    <Separator/>
    <MenuItem Header="Affirm"  
          Command="{Binding Path=AffirmCommand}"  
          CommandParameter="{Binding PlacementTarget.Tag, 
                                     RelativeSource={RelativeSource FindAncestor, 
                                     AncestorType={x:Type ContextMenu}}}"/>
   </ContextMenu>

   <Style TargetType="{x:Type TextBox}" x:Key="TextBoxAffirmMenuStyle">
       <Setter Property="ContextMenu" Value="{StaticResource textBoxMenu}" />
   </Style>
</Window.Resources>

Here are the text boxes:

<Grid>
    <Grid.RowDefinitions>
       <RowDefinition/>
       <RowDefinition/>
       <RowDefinition/>
    </Grid.RowDefinitions>

    <TextBox Grid.Row="0" ContextMenu="{StaticResource textBoxMenu}" Tag="{Binding RelativeSource={RelativeSource Self}}" Text="text in box 1"/>
    <TextBox Grid.Row="1" ContextMenu="{StaticResource textBoxMenu}" Tag="{Binding RelativeSource={RelativeSource Self}}" Text="text in box 2"/>
     <TextBox Grid.Row="2" ContextMenu="{StaticResource textBoxMenu}" Tag="{Binding RelativeSource={RelativeSource Self}}" Text="text in box 3"/>
</Grid>

Here is the command code from a ViewModel:

public class MainViewModel : ViewModelBase
{
  public ICommand AffirmCommand { get; set; }

  public MainViewModel()
  {
     AffirmCommand = new DelegateCommand<object>(OnAffirmCommand, CanAffirmCommand);
  }

  private void OnAffirmCommand(object obj)
  {
  }

  private bool CanAffirmCommand(object obj)
  {
     return true;
  }
}
Zamboni
Thanks Zamboni. I have implemented this in slightly different way. It seems that the command parameter is bound only once. what i did was bind the CommandTarget to the placement target as you did and bound the CommandTarget to the CommandParameter. Since the CommandTarget is updated each time the context menu opens, the command parameter returns the Target Control. I think what you have suggested is some what similar to this approach.
Deshan
Thanks for the comment, please post your code/solution as an answer for others in the future.
Zamboni