Search code examples
c#wpfxamldata-binding

Binding command - Ancestor


Okay, guys. I've been trying this for about 3 days now, and no amount of Googling is helping. Below is a snippet of my XAML (should be enough to follow along). My problem is the command for the "ContextMenu". As you can see I have DeleteTagCommand. Now that command works if I throw it in the position of CheckBoxCommand, which is great.. But it just will be called in it's current location, and it's driving me insane.

        <ScrollViewer Grid.Column="0">
            <StackPanel Orientation="Vertical">
                <ItemsControl ItemsSource="{Binding Tags, UpdateSourceTrigger=PropertyChanged}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <CheckBox Content="{Binding Value}" Margin="10,5,10,5" Command="{Binding DataContext.CheckBoxCommand,
                                                                                           RelativeSource={RelativeSource FindAncestor,
                                                                                           AncestorType={x:Type Grid}}}"
                                  CommandParameter="{Binding }">
                                <CheckBox.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="Delete" Command="{Binding DataContext.DeleteTagCommand,
                                                                                    RelativeSource={RelativeSource FindAncestor,
                                                                                    AncestorType={x:Type Grid}}}"
                                                  CommandParameter="{Binding}" />
                                    </ContextMenu>
                                </CheckBox.ContextMenu>
                            </CheckBox>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </ScrollViewer>

I've tried:

  • Calling 'ElementName' from all over the show, but it's never being picked up
  • Changing the 'AncestorLevel' to obscene numbers, hoping that was the problem
  • And more..

Not sure what would be useful for you guys, but below is the output message I get

Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Grid', AncestorLevel='1''. BindingExpression:Path=DataContext.DeleteTagCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

Thanks


Solution

  • ContextMenus aren't actually part of the same visual tree as their parents so they can't directly bind to any elements within it. They can, however, still bind to StaticResources. The trick is to thus use an intermediate proxy such as the BindingProxy class shown on this page. Start by adding an instance to your ItemsControl resource block:

    <ItemsControl.Resources>
        <local:BindingProxy x:Key="Proxy" Data="{Binding}" />
    </ItemsControl.Resources>
    

    Then use it to bind your ContextMenu command:

    <ContextMenu>
        <MenuItem Header="Delete" Command="{Binding Data.DeleteTagCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
    </ContextMenu>