下午好,
我有一个看似非常普遍的问题。我有一个用户控件,它有一个View Model作为其数据上下文。然后,此用户控件中的元素将此ViewModel用于绑定目的等。
ViewModel
public class TicketDetailsViewModel : ViewModelBase
{
public DelegateCommand<object> HideSelectedText { get; private set; }
private Ticket _ticket;
public Ticket Ticket
{
get { return _ticket; }
set
{
_ticket = value;
this.RaisePropertyChanged(p => p.Ticket);
}
}
}
我的ViewModel包含一个Ticket对象。此票证对象附有一组注释,并使用ItemsControl将其呈现给票证显示用户控件
<ItemsControl ItemsSource="{Binding Path=Ticket.Comments}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="15" Background="{Binding Path=CommentType, ConverterParameter=CommentType, Converter={StaticResource ResourceKey=commentColorConverter}}" Padding="10" Margin="40,10,40,0">
<TextBox x:Name="tbComment" Text="{Binding CommentText}" IsReadOnly="True">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Spam" Command="{Binding Path=DataContext.HideSelectedText,RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl} }}">
</MenuItem>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
您会注意到此ItemsControl呈现的每个TextBox都附加了一个ContextMenu。我想要做的是将此ContextMenuItem的命令绑定到我的ViewModel中的DelegateCommand。当然,只需使用;
<MenuItem Header="Spam" Command="{Binding HideSelectedText}">
我们没有得到任何有用的东西,因为这个上下文中的'Binding'等于Ticket.Comment,因此不知道HideSelectedText究竟是什么。
似乎有许多类似的问题,并且所有答案似乎都转向了RelativeSource解决方案。正如你在我原来的XAML代码中看到的那样,我已经尝试了这个以及很多其他版本(使用和不使用AncestorLevel设置,使用AncestorType = {x:Type ItemsControl},AncestorType = {x :类型Window},AncestorType = {x:Type DataTemplate}等)和ALL产生类似于的输出错误;
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=DataContext.HideSelectedText; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
或
System.Windows.Data Error: 40 : BindingExpression path error: 'HideSelectedText' property not found on 'object' ''TicketComment' (HashCode=49290260)'. BindingExpression:Path=DataContext.HideSelectedText; DataItem='ContextMenu' (Name=''); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
那么,为什么这个解决方案似乎适用于这么多人,但对我来说,只需输入{Binding HideSelectedText}
就没有什么区别?
答案 0 :(得分:3)
ContextMenus实际上并不是WPF的VisualTree的一部分,因此Bindings不会像被驱逐的那样工作。作为替代方案,请尝试此绑定:
<MenuItem Header="Spam" Command="{Binding PlacementTarget.DataContext.HideSelectedText,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" />
答案 1 :(得分:1)
最好的解决方案是通过资源完成。 如果将在资源中创建ContextMenu,它将自动从父级继承DataContext。 然后,只需要创建一个静态资源传递值的样式。
示例:强>
<DataGrid.Resources>
<ContextMenu x:Key="test_ContextMenu" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<MenuItem Header="Test Header" Command="{Binding Path=TestCommand, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu" Value="{StaticResource test_ContextMenu}"/>
</Style>
</DataGrid.ItemContainerStyle>
此代码在.NET 4.5上进行了测试,但我认为它也适用于早期版本。
答案 2 :(得分:0)
由于Context Menu不是可视化树的一部分,因此默认情况下它不会继承DataContext。您必须将上下文菜单的DataContext设置为PlacementTarget,如下所示 -
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext}">
<ContextMenuItem Header="Item1" Command="{Binding YourCommand}" />
</ContextMenu>
希望这有帮助。
-VJ