WPF HitTest 获取 ListBoxItem
WPF HitTest to get ListBoxItem
第一件事:我在 Visual Studio 2010 年有一个使用 C# 和 MVVM (MVVM Light) 的 WPF 项目。
我有一个 Canvas 控件,里面是一个 ListBox - 每个 ListBoxItem 都可以通过鼠标拖动来回移动(每个 ListBoxItem 都有一个 DataTemplate,其中包含一个允许拖动每个项目的 Thumb 控件)。
看起来如下:
<DataTemplate>
<Grid Background="Transparent">
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplateRegular}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid> </DataTemplate>
如您所见,有一个处理拖动部分的命令 (DeltaDragCommand)。
None 这是个问题,但后来我想添加通过单击和拖动动作在 Canvas 控件周围平移的功能。这是与上述拖动 ListBoxItems 发生冲突的地方。
Canvas pan 内容在后面的代码中处理,事件的订阅如下所示:
NodeDragScrollViewer.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
NodeDragScrollViewer.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
NodeDragScrollViewer.MouseMove += OnMouseMove;
NodeDragScrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
NodeDragScrollViewer.ScrollChanged += OnScrollViewerScrollChanged;
我们在这里看到的事件是 运行 在 ScrollViewer(Canvas 控件所在的控件)上。问题是这些事件位于 'on top of' ListBoxItems - canvas 的平移点击将在点击拖动 ListBoxItem 之前激活。
现在我看到的一个解决方案是使用 VisualTreeHelper HitTest 方法找出被点击的内容。如果我可以查明是否单击了 ListBoxItem,那么我可以忽略 Canvas 平移操作并让事情像以前一样进行。
我面临的问题是我无法让它工作。我似乎有人解释了这应该如何工作并为此操作借用了代码。我在这里展示该代码:
Point pt = new Point();
VisualTreeHelper.HitTest(NodeDragCanvas, null,
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
var p = FindParent<ListBoxItem>(result.VisualHit);
//Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
如果您查看 HitTestResultBehaviour 的内容,我会在此处检查我单击的控件。我希望它是一个 ListBoxItem...但总是 returns 'null'。我知道该方法可以多次触发,但我从未见过 var p(在本例中)不是 null 值。但是,如果我尝试使用 FindParent<ListBox>,我将返回 ListBox。它有适量的物品,似乎符合我的预期。但这对我不起作用,因为我的列表框是 Canvas.
的大小
这似乎是关键点,但我不知道还能尝试什么,所有的研究途径似乎都回到了这种方法。
任何人都可以提供任何指导吗?
谢谢
是的,显然您需要在 Canvas 上使用 PreviewXXX 事件,否则您将无法获得 Canvas 中 ListBoxItems 的鼠标事件,因为 ListBoxItem 已经吃掉了它们。
您可能需要考虑重组您的代码,这样您就可以使用 MyListBox(或您想要的任何名称)来代替使用 ListBox,并在其中捕获 ListBoxItem 鼠标事件并仅处理 Canvas拖入Canvas。你现在拥有它的方式,当它们真正完全分开时,你将这两种情况混合在一起。 canvas 应该处理它的事情,列表框应该处理它的事情。
如果您想保留代码的现有状态,请查看 MouseEventArgs、原始来源、发件人等。其中之一可能有原始发件人,但它可能是 TextBlock 而不是然后是 ListBoxItem,因为这是顶级项目,所以...
Google 周围有一个众所周知的 class VisualTreeExtensions
并使用它。当您获得 TextBlock 或其他内容时,您可以执行类似 ((DependencyObject)e.OriginalSource).GetVisualAncestor<ListBoxItem>()
的操作,如果它不为空,则您知道您在 ListBox 中。
第一件事:我在 Visual Studio 2010 年有一个使用 C# 和 MVVM (MVVM Light) 的 WPF 项目。
我有一个 Canvas 控件,里面是一个 ListBox - 每个 ListBoxItem 都可以通过鼠标拖动来回移动(每个 ListBoxItem 都有一个 DataTemplate,其中包含一个允许拖动每个项目的 Thumb 控件)。
看起来如下:
<DataTemplate>
<Grid Background="Transparent">
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplateRegular}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid> </DataTemplate>
如您所见,有一个处理拖动部分的命令 (DeltaDragCommand)。
None 这是个问题,但后来我想添加通过单击和拖动动作在 Canvas 控件周围平移的功能。这是与上述拖动 ListBoxItems 发生冲突的地方。
Canvas pan 内容在后面的代码中处理,事件的订阅如下所示:
NodeDragScrollViewer.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
NodeDragScrollViewer.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
NodeDragScrollViewer.MouseMove += OnMouseMove;
NodeDragScrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
NodeDragScrollViewer.ScrollChanged += OnScrollViewerScrollChanged;
我们在这里看到的事件是 运行 在 ScrollViewer(Canvas 控件所在的控件)上。问题是这些事件位于 'on top of' ListBoxItems - canvas 的平移点击将在点击拖动 ListBoxItem 之前激活。
现在我看到的一个解决方案是使用 VisualTreeHelper HitTest 方法找出被点击的内容。如果我可以查明是否单击了 ListBoxItem,那么我可以忽略 Canvas 平移操作并让事情像以前一样进行。
我面临的问题是我无法让它工作。我似乎有人解释了这应该如何工作并为此操作借用了代码。我在这里展示该代码:
Point pt = new Point();
VisualTreeHelper.HitTest(NodeDragCanvas, null,
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
var p = FindParent<ListBoxItem>(result.VisualHit);
//Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
如果您查看 HitTestResultBehaviour 的内容,我会在此处检查我单击的控件。我希望它是一个 ListBoxItem...但总是 returns 'null'。我知道该方法可以多次触发,但我从未见过 var p(在本例中)不是 null 值。但是,如果我尝试使用 FindParent<ListBox>,我将返回 ListBox。它有适量的物品,似乎符合我的预期。但这对我不起作用,因为我的列表框是 Canvas.
的大小这似乎是关键点,但我不知道还能尝试什么,所有的研究途径似乎都回到了这种方法。
任何人都可以提供任何指导吗? 谢谢
是的,显然您需要在 Canvas 上使用 PreviewXXX 事件,否则您将无法获得 Canvas 中 ListBoxItems 的鼠标事件,因为 ListBoxItem 已经吃掉了它们。
您可能需要考虑重组您的代码,这样您就可以使用 MyListBox(或您想要的任何名称)来代替使用 ListBox,并在其中捕获 ListBoxItem 鼠标事件并仅处理 Canvas拖入Canvas。你现在拥有它的方式,当它们真正完全分开时,你将这两种情况混合在一起。 canvas 应该处理它的事情,列表框应该处理它的事情。
如果您想保留代码的现有状态,请查看 MouseEventArgs、原始来源、发件人等。其中之一可能有原始发件人,但它可能是 TextBlock 而不是然后是 ListBoxItem,因为这是顶级项目,所以...
Google 周围有一个众所周知的 class VisualTreeExtensions
并使用它。当您获得 TextBlock 或其他内容时,您可以执行类似 ((DependencyObject)e.OriginalSource).GetVisualAncestor<ListBoxItem>()
的操作,如果它不为空,则您知道您在 ListBox 中。