![](/img/trans.png)
[英]WinForms ContextMenu stays open when MenuItem is clicked
[英]Determining which ListViewItem was clicked on in a ListView when executing a ContextMenu MenuItem
我正在尝试使用列表视图中的上下文菜单来运行一些代码,这些代码需要数据来自它源自哪个项目。
我最初只是这样做:
XAML:
<ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.Resources>
<ContextMenu x:Key="resourceContextMenu">
<MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" />
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource resourceContextMenu}" />
</Style>
</ListView.ItemContainerStyle>
...
C#:
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
// code that needs item data here
}
但是我发现无法以这种方式访问原始listview项。
我已经阅读了一些有关如何解决此问题的策略,例如拦截MouseDown事件并为单击的listviewitem设置一个私有字段,但这种做法对我来说并不理想,因为围绕它传递数据似乎有点不明智道路。 WPF应该很容易,对吗? :)我已经阅读了这个SO问题和这个MSDN论坛问题 ,但是我仍然不确定如何真正做到这一点,因为这些文章似乎都不适合我。 是否有更好的方法将单击的项目传递到上下文菜单?
谢谢!
在cmMetadata_Click处理程序中,您可以只查询lvResources.SelectedItem属性,因为lvResources可以从单击处理程序所在的代码隐藏文件中访问。这不是很优雅,但是可以使用。
如果您想变得更加优雅,则可以更改设置ContextMenu的位置。 例如,您可以尝试如下操作:
<ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</ListView.Style>
<ListViewItem>One Item</ListViewItem>
<ListViewItem>Another item</ListViewItem>
</ListView>
这是为ListViewItem插入模板,然后可以使用方便的TemplatedParent快捷方式将ListViewItem分配给菜单项的DataContext。
现在,您的隐藏代码如下所示:
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
MenuItem menu = sender as MenuItem;
ListViewItem item = menu.DataContext as ListViewItem;
}
显然,缺点是您现在需要完成ListViewItem的模板,但是我敢肯定您会很快找到一个适合您需求的模板。
与Charlie的答案类似,但不要求更改XAML。
private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
MenuItem menu = sender as MenuItem;
ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
}
因此,我决定尝试实施命令解决方案。 我对它现在的工作方式感到非常满意。
首先,创建我的命令:
public static class CustomCommands
{
public static RoutedCommand DisplayMetadata = new RoutedCommand();
}
接下来,在自定义listview控件中,我向构造函数添加了新的命令绑定:
public SortableListView()
{
CommandBindings.Add(new CommandBinding(CustomCommands.DisplayMetadata, DisplayMetadataExecuted, DisplayMetadataCanExecute));
}
此外,还添加了事件处理程序:
public void DisplayMetadataExecuted(object sender, ExecutedRoutedEventArgs e)
{
var nbSelectedItem = (MyItem)e.Parameter;
// do stuff with selected item
}
public void DisplayMetadataCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = true;
}
我已经在使用样式选择器为列表视图项动态分配样式,因此,不必在xaml中执行此操作,而必须在后台代码中设置绑定。 您也可以在xaml中完成此操作:
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
MyItem selectedItem = (MyItem)item;
Style s = new Style();
var listMenuItems = new List<MenuItem>();
var mi = new MenuItem();
mi.Header= "Get Metadata";
mi.Name= "cmMetadata";
mi.Command = CustomCommands.DisplayMetadata;
mi.CommandParameter = selectedItem;
listMenuItems.Add(mi);
ContextMenu cm = new ContextMenu();
cm.ItemsSource = listMenuItems;
// Global styles
s.Setters.Add(new Setter(Control.ContextMenuProperty, cm));
// other style selection code
return s;
}
与尝试在鼠标单击上设置字段并尝试以这种方式访问所单击的内容相比,我更喜欢此解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.