以下自定义控件
public class DummyControl : FrameworkElement
{
private Visual visual;
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override int VisualChildrenCount { get; } = 1;
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
var pt = hitTestParameters.HitPoint;
return new PointHitTestResult(visual, pt);
}
public DummyControl()
{
var dv = new DrawingVisual();
using (var ctx = dv.RenderOpen())
{
var penTransparent = new Pen(Brushes.Transparent, 0);
ctx.DrawRectangle(Brushes.Green, penTransparent, new Rect(0, 0, 1000, 1000));
ctx.DrawLine(new Pen(Brushes.Red, 3), new Point(0, 500), new Point(1000, 500));
ctx.DrawLine(new Pen(Brushes.Red, 3), new Point(500, 0), new Point(500, 1000));
}
var m = new Matrix();
m.Scale(0.5, 0.5);
RenderTransform = new MatrixTransform(m);
//Does work; but only the left top quater enters hit test
//var hv = new HostVisual();
//var vt = new VisualTarget(hv);
//vt.RootVisual = dv;
//visual = hv;
//Never enters hit test
visual = dv;
}
}
xaml
<Window x:Class="MyNamespace.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyNamespace"
mc:Ignorable="d">
<Border Width="500" Height="500">
<local:DummyControl />
</Border>
</Window>
显示绿色区域,中心有两条红色坐标线。但它的命中测试行为对我来说是不可理解的。
我在方法HitTestCore
中放了一个断点,但它从未命中过。
如果我取消注释代码以使用HostVisual
和VisualTarget
代替它,它只会在鼠标位于左上方的四分之一(由上面给出的红线表示)时命中
如何解释上述内容以及如何使其按预期工作(在全范围内进行命中测试)?
(最初,我只是想在自定义控件上处理鼠标事件。一些现有的解决方案指出我要覆盖HitTestCore
方法。所以,如果你能提供任何可以让我处理鼠标事件的想法,我就不要不必使HitTestCore
方法有效。)
更新
如果我决定使用DrawingVisual
,克莱门的回答是好的。但是,当我使用HostVisual
和VisualTarget
时,如果没有覆盖HitTestCore
则不能正常工作,即使我这样做,仍然只有左上角的quater会接收鼠标事件。
最初的问题还包括解释。此外,使用HostVisual
允许我在另一个线程中运行渲染(在我的实际情况下耗费时间)。
(让我使用上面的HostVisual
高亮显示代码)
//Does work; but only the left top quater enters hit test
//var hv = new HostVisual();
//var vt = new VisualTarget(hv);
//vt.RootVisual = dv;
//visual = hv;
有什么想法吗?
更新#2
克莱门的新答案仍不适用于我的目的。是的,所有视觉区域都接受了热门测试。但是,我想要的是让完整的视口接收命中测试。在他的情况下,这是空白区域,因为他将视觉区域的整个视觉比例缩放。答案 0 :(得分:3)
为了建立可视树(默认情况下使命中测试工作),您还必须调用AddVisualChild
。来自MSDN:
AddVisualChild方法设置父子关系 在两个视觉对象之间。必要时必须使用此方法 对底层存储实现的更低级别控制 视觉子对象。 VisualCollection可以用作默认值 用于存储子对象的实现。
除此之外,您的控件应在其大小更改时重新呈现:
public class DummyControl : FrameworkElement
{
private readonly DrawingVisual visual = new DrawingVisual();
public DummyControl()
{
AddVisualChild(visual);
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return visual;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
using (var dc = visual.RenderOpen())
{
var width = sizeInfo.NewSize.Width;
var height = sizeInfo.NewSize.Height;
var linePen = new Pen(Brushes.Red, 3);
dc.DrawRectangle(Brushes.Green, null, new Rect(0, 0, width, height));
dc.DrawLine(linePen, new Point(0, height / 2), new Point(width, height / 2));
dc.DrawLine(linePen, new Point(width / 2, 0), new Point(width / 2, height));
}
base.OnRenderSizeChanged(sizeInfo);
}
}
当您的控件使用HostVisual和VisualTarget时,它仍然必须在其大小更改时重新呈现自身,并且还调用AddVisualChild
来建立可视树。
public class DummyControl : FrameworkElement
{
private readonly DrawingVisual drawingVisual = new DrawingVisual();
private readonly HostVisual hostVisual = new HostVisual();
public DummyControl()
{
var visualTarget = new VisualTarget(hostVisual);
visualTarget.RootVisual = drawingVisual;
AddVisualChild(hostVisual);
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return hostVisual;
}
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParams)
{
return new PointHitTestResult(hostVisual, hitTestParams.HitPoint);
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
using (var dc = drawingVisual.RenderOpen())
{
var width = sizeInfo.NewSize.Width;
var height = sizeInfo.NewSize.Height;
var linePen = new Pen(Brushes.Red, 3);
dc.DrawRectangle(Brushes.Green, null, new Rect(0, 0, width, height));
dc.DrawLine(linePen, new Point(0, height / 2), new Point(width, height / 2));
dc.DrawLine(linePen, new Point(width / 2, 0), new Point(width / 2, height));
}
base.OnRenderSizeChanged(sizeInfo);
}
}
您现在可以设置RenderTransform并仍然获得正确的点击测试:
<Border>
<local:DummyControl MouseDown="DummyControl_MouseDown">
<local:DummyControl.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</local:DummyControl.RenderTransform>
</local:DummyControl>
</Border>
答案 1 :(得分:0)
这对你有用。
public class DummyControl : FrameworkElement
{
protected override void OnRender(DrawingContext ctx)
{
Pen penTransparent = new Pen(Brushes.Transparent, 0);
ctx.DrawGeometry(Brushes.Green, null, rectGeo);
ctx.DrawGeometry(Brushes.Red, new Pen(Brushes.Red, 3), line1Geo);
ctx.DrawGeometry(Brushes.Red, new Pen(Brushes.Red, 3), line2Geo);
base.OnRender(ctx);
}
RectangleGeometry rectGeo;
LineGeometry line1Geo, line2Geo;
public DummyControl()
{
rectGeo = new RectangleGeometry(new Rect(0, 0, 1000, 1000));
line1Geo = new LineGeometry(new Point(0, 500), new Point(1000, 500));
line2Geo = new LineGeometry(new Point(500, 0), new Point(500, 1000));
this.MouseDown += DummyControl_MouseDown;
}
void DummyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
}
}