我尝试了解一下列表框视图与上下文菜单相结合的工作原理,因此我制作了以下 XAML 代码:
<UserControl>
...
<ListView
x:Name="level1Lister"
Grid.Row="1"
behaviours:AutoScrollListViewBehaviour.ScrollOnNewItem="True"
ItemsSource="{Binding LogBuffer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="1" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding Path=DataContext.ValidateAllCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, FallbackValue=9999999999}" Header="Copy" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Foreground="{Binding Color, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
我的主要问题是由于某种原因,我无法访问我的"验证所有命令"功能......我认为这与"相对来源={相对来源查找祖先..."部分,但我不知道如何。
ContextMenu
的"问题"在于它不是可视化树的一部分。原因是MenuItem
使用Popup
来托管内容。虽然Popup
本身是可视化树的一部分,但其子内容在新Window
实例中呈现时会断开连接。我们知道树中只能有一个Window
,这个Window
必须是 root.
由于Binding.RelativeSource
遍历可视化树,从Popup
的分离树开始,找到绑定源,因此Bindig
不会解析。
Popup.Child
内容继承Popup
的DataContext
。如果是ContextMenu
,这意味着MenuItem
继承DataContext
从ContextMenu
或ContextMenu
的父级继承 ,更准确地说。在您的场景中,DataContext
是ListBoxItem
的数据模型,即DataTemplate
的DataContext
。
这意味着,一种解决方案可能是在物料模型中实现命令。
第二种解决方案是使用路由命令。在您的方案中,此解决方案可能更合理。路由的命令/事件可以跨越两个树之间的边界。
在以下示例中,我使用ApplicationCommands.Copy
命令,这是预定义的路由命令之一。MenuItem.Parameter
绑定到DataContext
,即项目数据模型(继承自ContextMenu
,如前所述)。这样,命令处理程序就可以知道数据源.
事件处理程序可以使用UIElement.CommandBindings
属性附加到任何父元素。在示例中,处理程序附加到ListBox
元素:
MainWindow.xaml
<ListBox>
<ListBox.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Copy}"
Executed="CopyCommand_Executed" />
</ListBox.CommandBindings>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<!--
Here we are in a detached visual tree.
The DataContext is inherited from the ContextMenu element.
The framework allows routed events to cross the boundaries between the trees.
-->
<MenuItem Command="{x:Static ApplicationCommands.Copy}"
CommandParameter="{Binding}"
Header="Copy" />
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainWindow.xaml.cs
private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
var listBoxItemModel = e.Parameter as LogBufferItem;
// TODO::Handle Copy command. For example:
var commandTarget = this.DataContext as ICommandModel;
commandTarget.ValidateAllCommand.Execute(listBoxItemModel);
}
第三种但不推荐的解决方案是将ContextMenu
父级的DataContext
绑定到感兴趣的上下文:
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel>
<!-- DataTemplate DataContext -->
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext}">
<!-- ListBox DataContext -->
<Grid.ContextMenu>
<ContextMenu>
<!--
Here we are in a detached visual tree.
The DataContext is inherited from the ContextMenu/Grid element.
-->
<MenuItem Command="{Binding ValidateAllCommand}"
Header="Copy" />
</ContextMenu>
</Grid.ContextMenu>
</Grid>
<!-- DataTemplate DataContext -->
<TextBlock />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox