ser*_*hio 2 wpf user-controls hittest
我有以下用户控件:一个点及其名称:
<UserControl x:Class="ShapeTester.StopPoint"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="100">
<StackPanel>
<Ellipse Stroke="DarkBlue" Fill="LightBlue" Height="10" Width="10"/>
<TextBlock Text="Eiffel Tower"/>
</StackPanel>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
这很酷。
现在,我有一个面板,在女巫中,我需要恢复我用鼠标击中的停止点:
public partial class StopsPanel : UserControl
{
private List<StopPoint> hitList = new List<StopPoint>();
private EllipseGeometry hitArea = new EllipseGeometry();
public StopsPanel()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
foreach (StopPoint point in StopsCanvas.Children)
{
point.Background = Brushes.LightBlue;
}
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Initialization:
Initialize();
// Get mouse click point:
Point pt = e.GetPosition(StopsCanvas);
// Define hit-testing area:
hitArea = new EllipseGeometry(pt, 1.0, 1.0);
hitList.Clear();
// Call HitTest method:
VisualTreeHelper.HitTest(StopsCanvas, null,
new HitTestResultCallback(HitTestCallback),
new GeometryHitTestParameters(hitArea));
if (hitList.Count > 0)
{
foreach (StopPoint point in hitList)
{
// Change rectangle fill color if it is hit:
point.Background = Brushes.LightCoral;
}
MessageBox.Show(string.Format(
"You hit {0} StopPoint(s)", hitList.Count));
}
}
public HitTestResultBehavior HitTestCallback(HitTestResult result)
{
if (result.VisualHit is StopPoint)
{
//
//-------- NEVER ENTER HERE!!! :(
//
// Retrieve the results of the hit test.
IntersectionDetail intersectionDetail =
((GeometryHitTestResult)result).IntersectionDetail;
switch (intersectionDetail)
{
case IntersectionDetail.FullyContains:
// Add the hit test result to the list:
hitList.Add((StopPoint)result.VisualHit);
return HitTestResultBehavior.Continue;
case IntersectionDetail.Intersects:
// Set the behavior to return visuals at all z-order levels:
return HitTestResultBehavior.Continue;
case IntersectionDetail.FullyInside:
// Set the behavior to return visuals at all z-order levels:
return HitTestResultBehavior.Continue;
default:
return HitTestResultBehavior.Stop;
}
}
else
{
return HitTestResultBehavior.Continue;
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,正如您所看到的,问题是HitTest 从未识别 UserControl(StopPoint) 原样,而是识别其组件(TextBlock、Ellipse甚至Border)。
当我将业务对象与 StopPoint 元素相关联时,我需要在 MouseHitting 时获取它,而不是它的组成元素。
有没有办法做到这一点?
编辑:
使用过滤器(现在,它根本不进入 HitTestCallback):
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace ShapeTester
{
/// <summary>
/// Interaction logic for StopsPanel.xaml
/// </summary>
public partial class StopsPanel : UserControl
{
private List<StopPoint> hitList = new List<StopPoint>();
private EllipseGeometry hitArea = new EllipseGeometry();
public StopsPanel()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
foreach (StopPoint point in StopsCanvas.Children)
{
point.Background = Brushes.LightBlue;
}
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Initialization:
Initialize();
// Get mouse click point:
Point pt = e.GetPosition(StopsCanvas);
// Define hit-testing area:
hitArea = new EllipseGeometry(pt, 1.0, 1.0);
hitList.Clear();
// Call HitTest method:
VisualTreeHelper.HitTest(StopsCanvas,
new HitTestFilterCallback(MyHitTestFilter),
new HitTestResultCallback(HitTestCallback),
new GeometryHitTestParameters(hitArea));
if (hitList.Count > 0)
{
foreach (StopPoint point in hitList)
{
// Change rectangle fill color if it is hit:
point.Background = Brushes.LightCoral;
}
MessageBox.Show(string.Format(
"You hit {0} StopPoint(s)", hitList.Count));
}
}
public HitTestResultBehavior HitTestCallback(HitTestResult result)
{
if (result.VisualHit is StopPoint)
{
//
//-------- NEVER ENTER HERE!!! :(
//
// Retrieve the results of the hit test.
IntersectionDetail intersectionDetail =
((GeometryHitTestResult)result).IntersectionDetail;
switch (intersectionDetail)
{
case IntersectionDetail.FullyContains:
// Add the hit test result to the list:
hitList.Add((StopPoint)result.VisualHit);
return HitTestResultBehavior.Continue;
case IntersectionDetail.Intersects:
// Set the behavior to return visuals at all z-order levels:
return HitTestResultBehavior.Continue;
case IntersectionDetail.FullyInside:
// Set the behavior to return visuals at all z-order levels:
return HitTestResultBehavior.Continue;
default:
return HitTestResultBehavior.Stop;
}
}
else
{
return HitTestResultBehavior.Continue;
}
}
// Filter the hit test values for each object in the enumeration.
public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
// Test for the object value you want to filter.
if (o.GetType() == typeof(StopPoint))
{
// Visual object's descendants are
// NOT part of hit test results enumeration.
return HitTestFilterBehavior.ContinueSkipChildren;
}
else
{
// Visual object is part of hit test results enumeration.
return HitTestFilterBehavior.Continue;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想写一个解释,但我已经找到了一个不错的解释:
重点是:
您UserControl.HitTestCore()将使用可能返回 NULL 的默认实现,这会导致跳过 UC 而不是传递给 resultCallback。
默认行为不是错误。这是一个不明显、聪明的设计——总而言之,你的控件没有视觉效果,它只是一些有形状的孩子的容器,所以一般来说,UC 中的可测试性和使步行路径变得混乱是没有意义的。您可能会将其视为一个缺点,因为您的代码的简洁性可以从 UC 的可测试性中受益。然而,这里的目标不是简洁——而是速度。事实上,这是一个重要的功能,因为它确实减少了树行者必须执行实际交叉的项目数量!
所以 - 要么实现 HitTestCore 并返回一些非空的东西,要么为 UserControl 的孩子做 hittest,然后当有一个正确的结果但等于它的孩子时,使用 VisualTreeHelper.GetParent 直到你走到你想要的 UserControl。
| 归档时间: |
|
| 查看次数: |
9843 次 |
| 最近记录: |