2

Is it possible to have a different Datacontext for a WPF Command and a CommandParameter?

<UserControl>
<UserControl.Resources>
    <viewmodels:ListViewGridBaseViewModel x:Key="vm" />
</UserControl.Resources>
<Grid>
    <ContentControl x:Name="currentContent" 
                    Content="{Binding Path=ListGrid}" >
        <ContentControl.ContextMenu>
            <ContextMenu >
                <MenuItem Command="{Binding Path=Save}" 
                          CommandParameter="{Binding ElementName=currentContent}"
                          DataContext="{StaticResource ResourceKey=vm}"
                          Header="Save">
                    <MenuItem.Icon>
                        <Image Source="{StaticResource ResourceKey=Save}"
                               Height="16"
                               Width="16"/>
                    </MenuItem.Icon>
                </MenuItem>
                <MenuItem Command="{Binding Path=Revert}" 
                          DataContext="{StaticResource ResourceKey=vm}"
                          Header="Revert">
                    <MenuItem.Icon>
                        <Image Source="{StaticResource ResourceKey=Revert}"
                               Height="16"
                               Width="16"/>
                    </MenuItem.Icon>
                </MenuItem>
            </ContextMenu>
        </ContentControl.ContextMenu>
    </ContentControl>
</Grid>
</UserControl>

I want the Binding for ListGrid bubbled up to another Viewmodel and the Command to a local ViewModel. But the CommandParameter should be the ContentControl. LOG is saying:

System.Windows.Data Error: 4 : Cannot find source for binding with 
reference 'ElementName=currentContent'. BindingExpression:(no path); 
DataItem=null; target element is 'MenuItem' (Name=''); 
target property is 'CommandParameter' (type 'Object')
akjoshi
  • 15,374
  • 13
  • 103
  • 121
Johannes Wanzek
  • 2,825
  • 2
  • 29
  • 47

2 Answers2

2

ContextMenu breaks the DataContext inheritance chain, that's why ElementName=currentContent cannot be found.

Look here for artificial inheritance context and use the class DataContextSpy

then do the following:

<UserControl>
<UserControl.Resources>
    <viewmodels:ListViewGridBaseViewModel x:Key="vm" />
    <local:DataContextSpy DataContext="{Binding ElementName=currentContent}" x:Key="Spy">
</UserControl.Resources>
<Grid>
    <ContentControl x:Name="currentContent" 
                    Content="{Binding Path=ListGrid}" >
        <ContentControl.ContextMenu>
            <ContextMenu >
                <MenuItem Command="{Binding Path=Save}" 
                          CommandParameter="{Binding DataContext,Source={StaticResource Spy}}"
                          DataContext="{StaticResource ResourceKey=vm}"
                          Header="Save">
                    <MenuItem.Icon>
                        <Image Source="{StaticResource ResourceKey=Save}"
                               Height="16"
                               Width="16"/>
                    </MenuItem.Icon>
                </MenuItem>
                <MenuItem Command="{Binding Path=Revert}" 
                          DataContext="{StaticResource ResourceKey=vm}"
                          Header="Revert">
                    <MenuItem.Icon>
                        <Image Source="{StaticResource ResourceKey=Revert}"
                               Height="16"
                               Width="16"/>
                    </MenuItem.Icon>
                </MenuItem>
            </ContextMenu>
        </ContentControl.ContextMenu>
    </ContentControl>
</Grid>
</UserControl>
akjoshi
  • 15,374
  • 13
  • 103
  • 121
Markus Hütter
  • 7,796
  • 1
  • 36
  • 63
1

The ContextMenu has a separate VisualTree and is not part of UserControl's VisualTree and that's why Elementname binding's won't work. A simple workaround to use ElementName bindings is to add this in your UserControl's code-behind

NameScope.SetNameScope(currentContent, NameScope.GetNameScope(this)); 

Or you can use Enable ElementName Bindings with ElementSpy

akjoshi
  • 15,374
  • 13
  • 103
  • 121
  • Wow thanks for the explanation... What whould now be the "best practice" solution? – Johannes Wanzek Jul 12 '12 at 08:49
  • It depends on you, what you find easier? all have their benefits and solves the problem. I would prefer to just add a single line of code though. – akjoshi Jul 12 '12 at 09:01
  • I directly implemented the Spy solution. It was just one new class in my Infrastructure Project and one new namespace in the xaml, but thats not the reason I ask. Im wondering which solution might be better in terms of performance etc. – Johannes Wanzek Jul 12 '12 at 09:11
  • you will barely notice any difference in performance in all three solutions provided (DataContextSpy, ElementSpy or SetNameScope). The reason I'm always suggesting DataContextSpy is, that it is more powerful and I usually use it throughout a project on many instances. – Markus Hütter Jul 12 '12 at 10:56
  • I agree with markus, using DataContextSpy/ElementSpy makes sense if you use them in multiple places in your application; but I would go for SetnameScope for first time and later change it if required. – akjoshi Jul 12 '12 at 14:45
  • Not sure about performnace as that will require some effort to be sure; but I think difference shouldn't be too much to be worried about. – akjoshi Jul 12 '12 at 14:47