从上下文菜单访问数据上下文属性
Access Data Context properties from Context Menu
我试着了解一下 listboxview 与上下文菜单的结合方式,所以我编写了以下 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>
我的主要问题是我无法访问我的“ValidateAllCommand”函数,出于某种原因...我认为这与“RelativeSource={RelativeSource FindAncestor ...”部分有关,但我不知道怎么办。
ContextMenu
的“问题”是它不是可视化树的一部分。原因是 MenuItem
使用 Popup
来承载内容。虽然 Popup
本身是可视化树的一部分,但它的子内容在新的 Window
实例中呈现时会断开连接。我们知道树中只能有一个 Window
,而这个 Window
必须是根。
由于 Binding.RelativeSource
遍历可视化树,从 Popup
的分离树开始寻找绑定源,因此 Bindig
没有解析。
Popup.Child
内容继承了Popup
的DataContext
。在 ContextMenu
的情况下,它意味着 MenuItem
从 ContextMenu
或 ContextMenu
的父级继承 DataContext
,更准确地说。在您的场景中,DataContext
是 ListBoxItem
的数据模型,即 DataTemplate
的 DataContext
。
这意味着,一种解决方案是在项目模型中实施命令。
第二种解决方案是使用路由命令。此解决方案在您的场景中可能更合理。路由command/event可以越过两棵树之间的边界
在下面的示例中,我使用了 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
我试着了解一下 listboxview 与上下文菜单的结合方式,所以我编写了以下 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>
我的主要问题是我无法访问我的“ValidateAllCommand”函数,出于某种原因...我认为这与“RelativeSource={RelativeSource FindAncestor ...”部分有关,但我不知道怎么办。
ContextMenu
的“问题”是它不是可视化树的一部分。原因是 MenuItem
使用 Popup
来承载内容。虽然 Popup
本身是可视化树的一部分,但它的子内容在新的 Window
实例中呈现时会断开连接。我们知道树中只能有一个 Window
,而这个 Window
必须是根。
由于 Binding.RelativeSource
遍历可视化树,从 Popup
的分离树开始寻找绑定源,因此 Bindig
没有解析。
Popup.Child
内容继承了Popup
的DataContext
。在 ContextMenu
的情况下,它意味着 MenuItem
从 ContextMenu
或 ContextMenu
的父级继承 DataContext
,更准确地说。在您的场景中,DataContext
是 ListBoxItem
的数据模型,即 DataTemplate
的 DataContext
。
这意味着,一种解决方案是在项目模型中实施命令。
第二种解决方案是使用路由命令。此解决方案在您的场景中可能更合理。路由command/event可以越过两棵树之间的边界
在下面的示例中,我使用了 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