简体   繁体   English

使用DrawingContext相对于中心进行绘制

[英]Draw relative to center with DrawingContext

I've been wondering how to draw stuff in WPF with a DrawingContext relative to something that's not the top left corner of the control. 我一直想知道如何在WPF中使用DrawingContext相对于不在控件左上角的东西进行绘制。 My problem is that I want to draw some shapes by connecting various dots, and those dots have to be positionned relative to the center of the host control, with Y pointing upwards. 我的问题是我想通过连接各种点来绘制一些形状,并且这些点必须相对于主机控件的中心定位,Y指向上方。

My elements are rendered using a tree of custom DrawingVisual subclasses, with the root being a Border subclass that contains a VisualCollection . 我的元素是使用自定义DrawingVisual子类的树呈现的,其根是包含VisualCollectionBorder子类。 I solved the Y direction problem by specifying a ScaleTransform as the RenderTransform of that Border , essentially flipping the whole control vertically. 我通过指定ScaleTransform作为BorderRenderTransform来解决Y方向问题,本质上是垂直翻转整个控件。

No such luck for the other issue, though. 但是,在其他问题上没有这样的运气。 Any idea for how to center my origin? 关于如何使我的血统居中的想法吗?

Instead of the ScaleTransform you could use a MatrixTransform that scales by -1 in y direction and translates the coordinate origin to the control's center. 可以使用MatrixTransform代替ScaleTransform,该MatrixTransform在y方向上按-1缩放并将坐标原点平移到控件的中心。 This transform must however be updated whenever the control's size changes. 但是,每当控件大小更改时,都必须更新此转换。 Hence you would override OnRenderSizeChanged like below (assuming that you set the RenderTransform property of your control): 因此,您将像下面那样重写OnRenderSizeChanged(假设您设置了控件的RenderTransform属性):

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);

    RenderTransform = new MatrixTransform(1d, 0d, 0d, -1d,
        sizeInfo.NewSize.Width / 2d, sizeInfo.NewSize.Height / 2d);
}

EDIT: In case you don't want to transform the whole controls, you could also define a MatrixTransform as class member and apply it to every Visual in the visual children collection. 编辑:如果您不想转换整个控件,则还可以将MatrixTransform定义为类成员,并将其应用于视觉子级集合中的每个Visual。

private MatrixTransform transform = new MatrixTransform();

Assign to the Transform property of every new Visual: 分配给每个新Visual的Transform属性:

ContainerVisual visual = ...
visual.Transform = transform;

On size changes you would simply update that MatrixTransform: 关于大小更改,您只需更新该MatrixTransform:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);

    transform.Matrix = new Matrix(1d, 0d, 0d, -1d,
        sizeInfo.NewSize.Width / 2d, sizeInfo.NewSize.Height / 2d);
}

Of course you only need to apply the Transform to "top-level" visuals. 当然,您只需要将“变换”应用于“顶级”视觉效果即可。 Children of those visuals will be transformed by the Transform of their parents. 这些视觉效果的孩子将通过父母的改造而改变。 I do not exactly understand how you manage your visuals by a "Border subclass that contains a VisualCollection". 我不完全了解如何通过“包含VisualCollection的Border子类”管理视觉效果。 The typical approach would be to have one parent ContainerVisual as root of the visual tree. 典型的方法是将一个父ContainerVisual作为视觉树的根。 Transforms would then be applied to this root visual only. 然后,仅将变换应用于此基本视觉效果。

您尝试过TranslateTransform吗?

Alright, I got it. 好吧,我明白了。 Here's how I've done it. 这是我的方法。

First, I defined a GroupTransform and assigned to a WorldTransform property on my Border subclass. 首先,我定义了一个GroupTransform并分配给我的Border子类的WorldTransform属性。

var trans = new TranslateTransform();
var conv = new HalfConverter(); // a custom converter that halves whatever you pass to it
BindingOperations.SetBinding(trans, TranslateTransform.XProperty, new Binding { Path = new PropertyPath(ActualWidthProperty), Source = this, Converter = conv });
BindingOperations.SetBinding(trans, TranslateTransform.YProperty, new Binding { Path = new PropertyPath(ActualHeightProperty), Source = this, Converter = conv });

WorldTransform = new TransformGroup();
WorldTransform.Children.Add(new ScaleTransform { ScaleY = -1.0 });
WorldTransform.Children.Add(trans);
VisualTransform = WorldTransform;

Then, when I create a new instance of my custom DrawingVisual , I assign its Transform property to my WorldTransform . 然后,当我创建自定义DrawingVisual的新实例时,将其Transform属性分配给WorldTransform

// ZoneVisual is my DrawingVisual subclass
var vis = new ZoneVisual(zone) { Transform = WorldTransform };

When I add a new element (a Node , by the way), I simply need to transform it by the inverse of my WorldTransform . 当我添加一个新元素(顺便说一下,是一个Node )时,我只需要通过WorldTransform的逆来对其进行转换。

Voila. This might not be the best way, but it seems to work pretty well. 这可能不是最好的方法,但似乎效果很好。 I don't have remarkably high performance needs, so it will probably do the job. 我对高性能的需求不高,因此它可能会胜任。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM