Abhishek Bhandari
Abhishek Bhandari

Reputation:

CommandParameters in ContextMenu in WPF

I have a scenario where I have a WPF TreeView control that has an HierarchicalDataTemplate for its items. Now inside the HierarchicalDataTemplate, I have a Label and the Label has a ContextMenu with a menuitem for Delete. The Delete menuitem is bound to a Command called DeleteCommand which is a part of the class that has been set as the DataType of the HierarchicalDataTemplate.

Now, I want to pass the TreeView control in the CommandParameters of the ContextMenu's Delete menuitem's DeleteCommand so that I can handle the selection of the TreeViewItems on the deletion of the currently selected item.

But if I bind the CommandParameters as the {Binding ElementName=TreeViewName} or whatever for that matter, it is always null unless the binded element is a property in the DataContext.

Can anyone help me with a solution because I think, I have tried all the possible things such as RelativeSource and AncestorType etc but its always null. To me, it looks like either a limitation or a bug in the framework.

Upvotes: 15

Views: 27290

Answers (6)

Zoggo
Zoggo

Reputation: 1

<TreeView  ItemsSource="{Binding EventTreeViewViewItems}">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">         
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    <Setter Property="Focusable" Value="False" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="White" />

                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
           
            <TreeView.Resources>
                <ui:BindingProxy x:Key="BindingProxy" Data="{Binding}" />
                <ContextMenu Name="test" x:Key="ContextMenuRouteIdNode">
                    <MenuItem Header="pan to" Command="{Binding Data.MenuCommand, Source={StaticResource BindingProxy}, Mode=OneWay}" CommandParameter="{Binding}"/>
                    <MenuItem Header="zoom to"/>
                    <MenuItem Header="select"/>
                    <MenuItem Header="alle grünen Events automatisch korrigieren"/>
                </ContextMenu>
                <ContextMenu x:Key="ContextMenuEventNode">
                    <MenuItem Header="pan to"/>
                    <MenuItem Header="zoom to"/>
                    <MenuItem Header="select"/>
                    <MenuItem Header="Event automatisch korrigieren"/>
                </ContextMenu>
                <HierarchicalDataTemplate x:Name="TreeViewRouteIdNode" DataType="{x:Type ui:RouteIdNode}" 
                              ItemsSource="{Binding EventNode}" >
                    <StackPanel ContextMenu="{StaticResource ContextMenuRouteIdNode}" Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=RouteId}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="{x:Type ui:EventNode}">
                    <StackPanel ContextMenu="{StaticResource ContextMenuEventNode}" VerticalAlignment="Stretch"  Orientation="Vertical">
                        <TextBlock Grid.Row="0"  Grid.Column="0"  Foreground="{Binding Path=Brush}" Text="{Binding Path=Id}" />
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="200" />
                            </Grid.ColumnDefinitions>
                            
                            <Label Grid.Row="0" Grid.Column="0" >von:</Label>
                            <TextBox Grid.Row="0"  Grid.Column="1" Text="{Binding Path=From}" />
                            <Label Grid.Row="1" Grid.Column="0" >bis:</Label>
                            <TextBox Grid.Row="1"  Grid.Column="1" Text="{Binding Path=To}" />
                        </Grid>
  
                        
                       
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>

This part did the job for me:

 <MenuItem Header="pan to" Command="{Binding Data.MenuCommand, Source={StaticResource BindingProxy}, Mode=OneWay}" CommandParameter="{Binding}"/>

Upvotes: 0

Michał R&#243;życki
Michał R&#243;życki

Reputation: 327

For each element in DataGrid

<ContextMenu>
    <MenuItem Header="Edit Item"
                   Command="{Binding EditItemCommand, Mode=OneWay}"
                   CommandParameter="{Binding Path=PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
<ContextMenu>

Upvotes: 0

user714307
user714307

Reputation: 11

<MenuItem Header="..." 
          Command="{Binding Path=...}" 
          CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type ContextMenu}}}">
</MenuItem>

ContextMenu.PlacementTarget, is Label, where the menuitem is hosted. From Lavel, its parent Treeview is accessable.

Upvotes: 0

Emil Nachev
Emil Nachev

Reputation: 1

<ContextMenu>
    <MenuItem Header="Edit Item"
                   Command="{Binding EditItemCommand, Mode=OneWay}"
                   CommandParameter="{Binding Path=UIElement.(views:DataGridView.SelectedItems), RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
<ContextMenu>

Upvotes: 1

Robert Macnee
Robert Macnee

Reputation: 11850

The problem is that the ContextMenu is at the root of its own visual tree, so any RelativeSource.FindAncestor bindings won't go past the ContextMenu.

One solution is to use the PlacementTarget property to set up a two-stage binding from your Label:

<Label Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={
    x:Type TreeView}}}">
    <Label.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Delete" Command="{x:Static local:Commands.DeleteCommand}"
                CommandParameter="{Binding PlacementTarget.Tag, RelativeSource={
                RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
        </ContextMenu>
    </Label.ContextMenu>
</Label>

This is quite hacky, however. You're better off setting the CommandTarget property of your MenuItem to the ContextMenu's PlacementTarget and having the command handler on your TreeView. This means you won't have to pass the TreeView around.

Upvotes: 21

Szymon Rozga
Szymon Rozga

Reputation: 18178

Take a look at WPF CommandParameter Binding Problem. Maybe it can provide some pointers as to what's going on.

Upvotes: 0

Related Questions