1

I try to get my foot on the ground with WPF and MVVM for almost a year now but every time I think I understood how it's working or at least how it's supposed to work, something comes up that's not working and I can't understand or figure out why. Maybe you can help me with this one.

For the sake of simplicity I have a very basic ViewModel:

Public Class MainViewModel
    Private _testCommand As ICommand = New RelayCommand(AddressOf Me.Test)
    Private _items As IEnumerable(Of String) = New String() {"Item 1", "Item 2", "Item 3"}

    Public ReadOnly Property TestCommand As ICommand
        Get
            Return _testCommand
        End Get
    End Property

    Private Sub Test()
        MsgBox("Test")
    End Sub

    Public ReadOnly Property Items As IEnumerable(Of String)
        Get
            Return _items
        End Get
    End Property

End Class

This view model I use as DataContext for my equally basic window with two ListViews.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContextMenuTest"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <Window.Resources>
        <ContextMenu x:Key="ItemContextMenu">
            <MenuItem Header="Test Left" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.TestCommand}" />
        </ContextMenu>

        <Style x:Key="TestItemStyle_Left" TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
        </Style>

        <Style x:Key="TestItemStyle_Right" TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Header="Test Right" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.TestCommand}" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <ListView Grid.Column="0" ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource TestItemStyle_Left}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Test" Width="100" DisplayMemberBinding="{Binding}" />
                </GridView>
            </ListView.View>
        </ListView>

        <ListView Grid.Column="1" ItemsSource="{Binding Items}" ItemContainerStyle="{StaticResource TestItemStyle_Right}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Test" Width="100" DisplayMemberBinding="{Binding}" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

Both ListViews are the same, they only differ in the ItemContainerStyle they use. And both ItemContainerStyles only differ in the way the ContextMenu is worked in. On the left side it's implemented and used as a resource, on the right side it's implemented directly.

But why does the command binding of the context menu item only work on the left side and not on the right side?

Thank you for your explanations :-)

Nostromo
  • 1,177
  • 10
  • 28
  • [*How to bind to data when the DataContext is not inherited*](http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/) – slugster Mar 16 '16 at 10:41
  • But why is the DataContext not inherited on the right side when it's working on the left side? – Nostromo Mar 16 '16 at 11:49
  • Huh? That comment made no sense to me? The DataContext is not inherited because a context menu is not part of the visual or logical tree. This means you also cannot search for ancestors. This doco from MSDN might help: https://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.datacontext(v=vs.110).aspx – slugster Mar 16 '16 at 11:58
  • But the DataContext IS inherited on the left side, otherwise it wouldn't work, would it? The only difference between left and right is, that on the left side the ContextMenu is taken from a resource and on the right side it is defined directly, the binding part is completely the same... – Nostromo Mar 17 '16 at 06:54

1 Answers1

-1

If you look at the Binding Error in output window you will find this.

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

You can find the explanation from here

Community
  • 1
  • 1
Davy
  • 134
  • 5
  • -1 for suggesting something that uses the Tag property - it isn't needed, and it's a hack technique that needs to die along with the old Visual basic. – slugster Mar 16 '16 at 10:37
  • @slugster, I am not suggesting to use Tag property, it is the explanation that he wants and the explanation for the binding error its there. – Davy Mar 16 '16 at 10:42
  • Yeah I guess you are right in that respect... however all you have done is link to the answer without any supplementary information (including a binding error as a sample doesn't explain *what* and *why* and *how to fix*). Can I suggest you flesh your answer out a bit - at the moment it is little more than a link to someone else's answer. – slugster Mar 16 '16 at 11:09