简体   繁体   English

渲染到自定义DrawingContext

[英]Rendering into a custom DrawingContext

I'd like to hijack the usual WPF rendering to split the controls into primitives, do the layout management, apply the bindings etc. for me. 我想劫持通常的WPF渲染,将控件拆分为基元,进行布局管理,为我应用绑定等。

As far as I understand, the whole rendering in WPF boils down to rendering of primitives (text, image, line, curve) at the locations calculated by the layout manager with values defined by the dependency property system. 据我所知,WPF中的整个渲染归结为在布局管理器使用依赖属性系统定义的值计算的位置处渲染基元(文本,图像,线,曲线)。 If I could supply my own primitive-rendering logic, I would be able to render eg to a custom document type, transfer the primitives for real rendering over the network etc. 如果我可以提供自己的原始渲染逻辑,我将能够渲染到例如自定义文档类型,传输基元以通过网络进行实际渲染等。

My plan is following: 我的计划如下:

  1. Implement a custom DrawingContext . 实现自定义DrawingContext The DrawingContext is an abstract class, which defines a bunch of methods like DrawEllipse , DrawText , DrawImage etc. — I'll need to supply my own implementation for this functionality. DrawingContext是一个抽象类,它定义了一堆方法,如DrawEllipseDrawTextDrawImage等。 - 我需要为此功能提供我自己的实现。
  2. Create a WPF UserControl and force it to render into a given DrawingContext . 创建一个WPF UserControl并强制它渲染到给定的DrawingContext

However I've encountered the following problems: 但是我遇到了以下问题:

  1. DrawingContext contains abstract internal methods void PushGuidelineY1(double coordinate) and void PushGuidelineY2(double leadingCoordinate, double offsetToDrivenCoordinate) , which I cannot override easily. DrawingContext包含抽象的内部方法void PushGuidelineY1(double coordinate)void PushGuidelineY2(double leadingCoordinate, double offsetToDrivenCoordinate) ,我无法轻易覆盖。 (Perhaps there is some trick to overcome this?) (也许有一些技巧可以解决这个问题?)
  2. There seems to be no method to render the whole visual on a DrawingContext ? 似乎没有方法在DrawingContext上渲染整个视觉效果? Why? 为什么?

I can do something like 我可以做点什么

void RenderRecursively(UIElement e, DrawingContext ctx)
{
    e.OnRender(ctx);
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(e); i++)
        RenderRecursively((UIElement)VisualTreeHelper.GetChild(e, i), ctx);
}

— but I wonder if there is a direct way to render an UIElement . - 但我想知道是否有直接的方式来呈现UIElement (Of course, this problem is a minor one, but seeing no infrastructure for it makes me wonder if this is the proper way.) (当然,这个问题很小,但看不到它的基础设施让我想知道这是不是正确的方法。)

So, is the DrawingContext not intended for inheriting from? 那么, DrawingContext不是用于继承吗? Is the whole idea of supplying a custom DrawingContext a step in the right direction, or I need to rethink the strategy? 提供自定义DrawingContext的整个想法是朝着正确方向迈出的一步,还是我需要重新考虑策略? Is drawing onto a custom context supported in WPF, or I need to look for a different interception point? 是否正在绘制WPF支持的自定义上下文,或者我需要寻找不同的拦截点?

I think your approach will not work, because (as others have mentioned) you cannot provide your own DrawingContext implementation. 我认为你的方法不起作用,因为(正如其他人提到的那样)你无法提供自己的DrawingContext实现。

I suggest the following instead: In order to "flatten" the WPF rendering, have WPF export your visuals to an XPS document. 我建议如下:为了“展平”WPF渲染,让WPF将您的视觉效果导出到XPS文档。 During that process, all rendering is basically enumerated as simple rendering primitives and all that you are left with is Canvas s, basic shapes, glyphs, and other drawing primitives. 在此过程中,所有渲染基本上都被枚举为简单的渲染基元,而您剩下的所有渲染都是Canvas ,基本形状,字形和其他绘图基元。

Then iterate over the visuals in the pages of the document. 然后迭代文档页面中的视觉效果。 As far as I know the resulting visual will consist of primitives, only, so there is no need to call OnRender . 据我所知,由此产生的视觉效果仅包含原语,因此无需调用OnRender Instead this enables you to externally introspect the visual instances (using instanceof -cascades and reading/interpreting the properties). 相反,这使您能够从外部内省视觉实例(使用instanceof -cascades和读取/解释属性)。 That's still quite a lot of work, because you need to interpret the properties just like WPF does, but as far as I could see, this should work at least for the many major use-cases. 这仍然是相当多的工作,因为你需要像WPF那样解释属性,但据我所知,这至少应该适用于许多主要的用例。

You may need to approach this problem from the opposite direction. 您可能需要从相反的方向处理此问题。 Instead of aiming to provide your own DrawingContext , you can instead ask WPF to provide you with a Drawing . 您可以改为让WPF为您提供Drawing ,而不是旨在提供您自己的DrawingContext So it's more of a 'pull' approach' than the 'push' approach you're aiming for, but it should make it possible to get to the same place: if you have a Drawing that is a complete representation of the appearance of part of the visual tree, that's a data structure you can walk and discover everything that you would have discovered from calls to a custom DrawingContext . 所以它更像是一种“拉动”方法,而不是你所追求的“推动”方法,但它应该能够到达同一个地方:如果你有一个完全代表部件外观的Drawing对于可视化树,这是一个数据结构,您可以走过并发现从调用自定义DrawingContext发现的所有内容。

I believe this is the same basic approach that the XPS document export Sebastian mentions uses internally. 我相信这与Sebastian提到的XPS文档导出内部使用的基本方法相同。 But using it directly yourself is a more direct approach than using it through the XPS APIs 但是直接使用它比通过XPS API使用它更直接

At the heart is something fairly simple: VisualTreeHelper.GetDrawing . 内心很简单: VisualTreeHelper.GetDrawing This returns a DrawingGroup . 这将返回一个DrawingGroup ( Drawing is an abstract base class.) That documentation page shows you how to walk through the tree that you get back. Drawing是一个抽象的基类。)该文档页面向您展示如何遍历您返回的树。 Unfortunately, this doesn't do the whole job: it just provides the visuals for whichever node you happen to call it in, and if that node has children, they won't be included. 不幸的是,这并没有完成整个工作:它只是为您恰好调用它的节点提供视觉效果,如果该节点有子节点,则不会包含它们。

So you will, unfortunately, still have to write something that recurses the visual tree, much like you were already planning. 因此,遗憾的是,您仍然必须编写一些可以追溯视觉树的内容,就像您已经在计划一样。 And you will also need to handle any opacity masks, non-mask-based opacity, clip regions, effects, and transformations that are attached to the visual to get the correct results; 您还需要处理任何不透明蒙版,基于非蒙版的不透明度,剪辑区域,效果和附加到视觉上的变换以获得正确的结果; you'd have had to do all that too to make your proposed approach work correctly, so nothing really changes here. 你必须做所有这些,以使你提出的方法正常工作,所以这里没有任何真正的改变。 (One potential advantage of using the XPS API as Sebastian suggests is that it does all this for you. However, it's then your problem to extract the information from the XPS document in the form you want, and that may end up losing information that you might want to preserve.) (使用XPS API的一个潜在优势就像塞巴斯蒂安所说的那样,它会为你完成所有这些。但是,你需要以你想要的形式从XPS文档中提取信息,这可能最终会丢失你的信息可能想保留。)

I tried to do similar thing to create a FlowDocumentViewer for winRT. 我试图为winRT创建一个FlowDocumentViewer。 But as WinRT far less matured compared to WPF, also it delegates too much to the native layer (via render thread) I could not get anywhere. 但是由于WinRT与WPF相比还不成熟,所以它也过多地委托原生层(通过渲染线程),我无法在任何地方获得。 But this is what I've learned and I hope I'm explaining it well. 但这就是我所学到的,我希望我能很好地解释它。

WPF uses hardware accelerated graphics rendering. WPF使用硬件加速图形渲染。 So in simplistic terms, the WPF LayoutEngine constructs logical visual tree which is then translated to rendering instructions which are then sent to the Graphics hardware to execute or render. 因此,在简单化的术语中,WPF LayoutEngine构造逻辑可视树,然后将其转换为渲染指令,然后将指令发送到图形硬件以执行或渲染。

DrawingContext is a non trivial class, it interacts with the underlying graphics system for rendering, manages scaling, caching and so forth. DrawingContext是一个非平凡的类,它与底层图形系统交互以进行渲染,管理缩放,缓存等。 WPF runtime comes with default implementation which does the rendering of all visuals. WPF运行时带有默认实现,可以渲染所有视觉效果。 IMO, the reason it's made into an abstract class so Microsoft can provide different implementations say for Silverlight etc. But it's meant to be overridden by us. IMO,它被制作成一个抽象类的原因,因此微软可以为Silverlight等提供不同的实现。但它意味着被我们覆盖。

If you must replace the WPF rendering then your best bet is to create a UserControl, override Arrange and Measure calls and render each element to DrawingVisual using DrawingVisual.RenderOpen() and arrange them etc. from your code. 如果必须替换WPF渲染,那么最好的办法是创建一个UserControl,覆盖Arrange和Measure调用,并使用DrawingVisual.RenderOpen()将每个元素渲染到DrawingVisual,并从代码中排列它们等。 Managing the DataBinding notifications will be another thing you will have to do yourself. 管理DataBinding通知将是您必须自己完成的另一件事。

Seems like very interesting project. 似乎非常有趣的项目。 Good luck! 祝好运!

Instead of trying to write your own DrawingContext maybe you could create a class that derives from FrameworkElement or UIElement or even Visual that performs your activities in its OnRender method. 您可以创建一个派生自FrameworkElementUIElement的类,甚至可以创建一个在其OnRender方法中执行活动的Visual ,而不是尝试编写自己的DrawingContext You still have to use the given implementations of Draw[Something] but you will be in control of the arguments and order of operations. 您仍然必须使用Draw[Something]的给定实现,但您将控制参数和操作顺序。 You could still parse primitives and instructions from a secondary source and your one UIElement/FrameworkElement could compose the instructions at runtime. 您仍然可以从辅助源解析原语和指令,并且您的一个UIElement / FrameworkElement可以在运行时编写指令。

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

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