和上篇在WPF的TreeView中实现右键选定一样,这仍然是一个右键菜单的问题:
这个需求是在一个实现剪贴板的功能的时候遇到的:在弹出右键菜单时,如果菜单弹出位置在ListViewItem中时,我们认为这项已经被选中,可以使用剪贴板功能。
当菜单弹出位置在ListView的空白处时,我们一般认为没有项被选中,此时是不应该使能剪贴板功能的。
但是这个时候,该项仍然是选中的。不能通过Item的IsSelected的属性来区分这两种情况。这样,就需要我们加一个判断鼠标是否在所选的节点上的函数。实现这个功能的方式有如下两种:
方法1:响应ListView的PreviewMouseRightButtonDown事件,在其中判断是否有节点被选中。还是直接上代码吧:
bool isItemSelected = false;
private void ListView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
object item = GetElementFromPoint((ItemsControl)sender, e.GetPosition((ItemsControl)sender));
isItemSelected = (item != null);
}
private object GetElementFromPoint(ItemsControl itemsControl, Point point)
{
UIElement element = itemsControl.InputHitTest(point) as UIElement;
while (element != null)
{
if (element == itemsControl)
return null;
object item = itemsControl.ItemContainerGenerator.ItemFromContainer(element);
if (!item.Equals(DependencyProperty.UnsetValue))
return item;
element = (UIElement)VisualTreeHelper.GetParent(element);
}
return null;
}
这个其实就是一个命中测试的方面的问题,代码还是比较基础的,也没有什么需要解释的地方,但写起来还是有点麻烦。
方法2:同时响应ListView的PreviewMouseRightButtonDown事件和TreeViewItem的PreviewMouseRightButtonDown事件。
- 当鼠标点击在ListViewItem上时:发生的事件顺序为 ListView_MouseDown 、ListViewItem_MouseDown。
- 当鼠标点击在ListView外时:发生的事件仅为 ListView_MouseDown 。
因此,只要在ListView_MouseDown 中设置isItemSelected = false,在ListViewItem_MouseDown中设置isItemSelected = true即可。由于非常简单,就不附代码了。
使用上面的两种方法后,就可以直接在剪切复制命令的CanExecute函数中通过isItemSelected来判断是否该使能剪切复制命令了。
另外,这两方法是针对ItemsControl类型的控件的,也就是说,在TreeView或ListBox等控件中也可以使用,如果把它们封装一下就更方便了。