7

I'm trying to bind MenuItem's Command to command contained in UserControl.DataContext. I've found couple of similar question, but solution according to them is failing to me:

<UserControl ...>
<UserControl.Resources>
    <DataTemplate x:Key="TileItemStye">
        <Grid Width="100" Height="100">
            <Grid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Remove" 
                              Command="{Binding DataContext.RemoveItem, 
                              RelativeSource={RelativeSource FindAncestor,
                                             AncestorType=UserControl}}">
                    </MenuItem>
                </ContextMenu>
            </Grid.ContextMenu>
        </Grid>
    </DataTemplate>
</UserControl.Resources>
<Grid>
    <ListView ItemsSource="{Binding Path=Files}" 
              ItemTemplate="{DynamicResource TileItemStye}"  >
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>
</Grid>

UserControl's DataContext is ViewModel with ICommand RemoveItem and ObservableCollection<FileViewModel> Files.

Naser Asadi
  • 1,153
  • 18
  • 35
bkovacic
  • 371
  • 1
  • 5
  • 16

2 Answers2

13

If you are on .NET 4 there indeed is a more elegant solution:

<UserControl Name="uc" ...>
<!-- ... -->
    <MenuItem Header="Remove"
              Command="{Binding DataContext.RemoveItem,
                                Source={x:Reference uc}}"/>

(This requires that the template stays in the Resources, otherwise there will be a cyclical dependency error)

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • can you elaborate on the dependency error? i have a similar situation where I'm using ElementName=LayoutRoot to get to the DataContext (my ViewModel). but it's leaking the viewmodel. would this be the cyclical depency error you're talking about? any way to resolve this? (windows store app btw) – Joris Weimar Jan 22 '13 at 11:35
  • @JorisWeimar: Dependency errors only occur for `x:Reference` in case you reference a control from within its tree, e.g. if a `Grid` contains a `TextBlock` and you try to reference the `Grid` in a binding on the `TextBlock` that would cause such an error. I only use `x:Reference` here because `ElementName` does not work in the context of disconnected trees. Regarding your problem, i don't understand what "leaking the viewmodel" is supposed to mean. – H.B. Jan 22 '13 at 14:03
  • with leaking the viewmodel i mean that the viewmodel is not finalized because there there is a refence to it somewhere from within the COM layer (what i've gathered from PerfView). it almost seems that this is an inherent problem with this type of construction. – Joris Weimar Jan 22 '13 at 14:57
4

Menus are not drawn in the same Visual Tree as your Controls, which is why the RelativeSource binding does not work

You need to bind to the PlacementTarget of your ContextMenu to access the main Visual Tree

<MenuItem Header="Remove" 
          Command="{Binding PlacementTarget.DataContext.RemoveItem, 
              RelativeSource={RelativeSource FindAncestor, 
              AncestorType={x:Type ContextMenu}}}" />
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Hm... I'm still not able to call a command. I see what's your point but binding doesn't work. – bkovacic Sep 29 '11 at 15:22
  • @Pedala I could have the syntax wrong. I know you want to bind to `PlacementTarget`, which should return the Control that the `ContextMenu` is attached to, but you may need to use a different syntax to navigate to the Command – Rachel Sep 29 '11 at 15:36
  • Binding you wrote goes up to Grid (the one inside DataTemplate), but not up to the UserControl's DataContext(where command is). It's question whether is possible to nest Bindings, so I can go up from this grid? – bkovacic Sep 29 '11 at 15:42
  • @pedala I don't think it is, but you might be able to bind `ContextMenu.DataContext` to the UserControl's `DataContext` using a `RelativeSource` binding. – Rachel Sep 29 '11 at 15:47
  • I've bound Grid's Tag attribute to `{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=DataContext}` and MenuItem to `{Binding PlacementTarget.Tag.RemoveItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}` It's working now, but still I belive there's some more elegant solution... – bkovacic Sep 29 '11 at 16:40