当使用OnRender在屏幕上绘制内容时,有没有办法在绘制的图形上执行命中测试?
示例代码
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(Brushes.Black, null, new Rect(50, 50, 100, 100));
}
显然,没有人参考绘制的矩形,这对于执行命中测试是必要的,还是我错了?我知道我可以使用DrawingVisual,我只是好奇我的理解是否正确,使用OnRender绘制的东西你不能对绘制的东西进行任何命中测试?
答案 0 :(得分:0)
您理解正确,无法对使用DrawingContext方法绘制的图形进行测试,因为它们未在可视树中表示为对象。
答案 1 :(得分:0)
除非我完全误解了OP的问题,否则答案是肯定的,命中测试是基于在OnRender
期间绘制的内容。您可以通过在覆盖OnMouseEnter
的同一个类中覆盖OnRender
来判断。在覆盖OnMouseEnter
时尝试这个:
var htr = VisualTreeHelper.HitTest(this, e.GetPosition(this));
if (htr != null)
System.Diagnostics.Debug.WriteLine("It's a hit!");
现在,我注意到你仍在调用base.OnRender
,这表明你正试图绘制Visual
否则会呈现的内容。那么,你所能做的就是区分父类呈现的内容与你在覆盖中呈现的内容。但是,如果您需要,只需将您的绘图包含在自己的Visual
。
答案 2 :(得分:-1)
我的快速解决方案。它在许多方面都不理想,但它对我来说是一个开始。如果有必要,可以轻松地重构性能。
/// <summary>
/// Provides basic hit testing
/// </summary>
public class RadarHitTestUtility
{
public Dictionary<Tuple<int,int>, HitContainer> HitStorage = new Dictionary<Tuple<int, int>, HitContainer>();
public void AddEllipse(RadarObject radarObject, Point point, double x, double y)
{
// Geometry used for hit testing
var elipse = new EllipseGeometry
{
Center = point,
RadiusX = x,
RadiusY = y,
};
var key = new Tuple<int, int>((int)point.X, (int)point.Y);
var hit = new HitContainer
{
Geometry = elipse,
Bounds = elipse.Bounds,
RadarObject = radarObject
};
if(!HitStorage.ContainsKey(key))
HitStorage.Add(key, hit);
}
/// <summary>
/// Gets first radar object whose center point is within distance of point.
/// </summary>
public HitContainer GetSimpleHit(Point point, int distance = 5)
{
foreach (var hit in HitStorage)
{
if (Math.Abs(hit.Key.Item1 - point.X) <= distance && Math.Abs(hit.Key.Item2 - point.Y) <= distance)
{
hit.Value.Intersection = IntersectionDetail.NotCalculated;
return hit.Value;
}
}
return default(HitContainer);
}
/// <summary>
/// Gets first radar object that contains point
/// </summary>
public HitContainer GetHit(Point point)
{
var PreCheckDistance = 50;
foreach (var hit in HitStorage)
{
if (Math.Abs(hit.Key.Item1 - point.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - point.Y) <= PreCheckDistance)
{
if (hit.Value.Geometry.FillContains(point))
{
hit.Value.Intersection = IntersectionDetail.FullyInside;
return hit.Value;
}
}
}
return default(HitContainer);
}
/// <summary>
/// Gets first radar object that intersects with geometry
/// </summary>
public HitContainer GetHit(Geometry geometry)
{
var PreCheckDistance = 50;
var GeometryCheckTolerance = 1;
var center = geometry.Bounds.Center();
foreach (var hit in HitStorage)
{
if (Math.Abs(hit.Key.Item1 - center.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - center.Y) <= PreCheckDistance)
{
var result = hit.Value.Geometry.FillContainsWithDetail(geometry, GeometryCheckTolerance, ToleranceType.Absolute);
if (result != IntersectionDetail.Empty)
{
hit.Value.Intersection = result;
return hit.Value;
}
}
}
return default(HitContainer);
}
public class HitContainer
{
public RadarObject RadarObject { get; set; }
public Geometry Geometry { get; set; }
public Rect Bounds { get; set; }
public IntersectionDetail Intersection { get; set; }
}
public void Clear()
{
HitStorage.Clear();
}
}
在控件中声明一个实例
public RadarHitTestUtility HitTester = new RadarHitTestUtility();
您需要在某个时刻调用Clear(),以便命中测试集合中只存在当前对象。
HitTester.Clear();
在OnRender()中,如果要为记录测试目的记录绘图调用,可以使用AddEllipse()
进行记录。HitTester.AddEllipse(radarObject, radarObject.Point, actorRadius, actorRadius);
以下是我要求点击检查的方式
private void MouseDownHandler(object sender, MouseButtonEventArgs e)
{
IsMouseDown = true;
Cursor = Cursors.Hand;
DragInitialPosition = Mouse.GetPosition(this);
DragInitialPanOffset = CanvasData.PanOffset;
var hit = FindElementUnderClick(sender, e);
if (hit != null)
{
SelectedItem = hit.RadarObject.Actor;
}
}
private RadarHitTestUtility.HitContainer FindElementUnderClick(object sender, MouseEventArgs e)
{
var position = e.GetPosition((UIElement)sender);
return HitTester.GetHit(position);
}
如果你想测试椭圆以外的形状你只需要在RadarHitTestUtility中添加适当的记录方法并调用它,创建一个不同类型的几何来测试。