渲染到自定义DrawingContext中
本文关键字:DrawingContext 自定义 | 更新日期: 2023-09-27 18:27:26
我想劫持通常的WPF渲染,将控件拆分为基元,进行布局管理,应用绑定等。
据我所知,WPF中的整个渲染可以归结为在布局管理器使用依赖属性系统定义的值计算的位置渲染基元(文本、图像、直线、曲线)。如果我能提供我自己的基元渲染逻辑,我将能够渲染,例如,自定义文档类型,通过网络传输基元进行真实渲染等。
我的计划如下:
- 实现自定义
DrawingContext
。DrawingContext
是一个抽象类,它定义了一堆方法,如DrawEllipse
、DrawText
、DrawImage
等。我需要为这个功能提供自己的实现 - 创建一个WPF
UserControl
并强制其渲染为给定的DrawingContext
然而,我遇到了以下问题:
DrawingContext
包含抽象的内部方法void PushGuidelineY1(double coordinate)
和void PushGuidelineY2(double leadingCoordinate, double offsetToDrivenCoordinate)
,我无法轻易覆盖它们。(也许有什么技巧可以克服这一点?)- 似乎没有方法在
DrawingContext
上渲染整体视觉效果?为什么
我可以做一些类似的事情
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);
}
--但我想知道是否有一种直接的方法来呈现CCD_ 12。(当然,这个问题很小,但由于没有基础设施,我想知道这是否是正确的方法。)
那么,DrawingContext
不是用来继承的吗?提供定制DrawingContext
的整个想法是朝着正确的方向迈出的一步,还是我需要重新思考策略?WPF中是否支持绘制自定义上下文,或者我需要寻找不同的截取点?
您可能需要从相反的方向来处理这个问题。您可以要求WPF为您提供Drawing
,而不是旨在提供自己的DrawingContext
。因此,它更像是一种"拉"方法,而不是你想要的"推"方法,但它应该可以达到相同的目的:如果你有一个Drawing
,它是视觉树的一部分外观的完整表示,那就是一个数据结构,你可以走着走着,发现从对自定义DrawingContext
的调用中你会发现的一切。
我相信这与Sebastian提到的XPS文档导出内部使用的基本方法相同。但是,与通过XPS API 使用它相比,自己直接使用它是一种更直接的方法
核心是一些相当简单的东西:VisualTreeHelper.GetDrawing
。这将返回一个DrawingGroup
。(Drawing
是一个抽象基类。)该文档页面向您展示了如何遍历返回的树。不幸的是,这并不能完成全部工作:它只是为你碰巧调用它的任何节点提供视觉效果,如果该节点有子节点,它们就不会被包括在内。
所以,不幸的是,你仍然需要写一些递归视觉树的东西,就像你已经在计划的那样。您还需要处理任何不透明度遮罩、基于非遮罩的不透明度、剪辑区域、效果和附加到视觉的变换,以获得正确的结果;为了使您提出的方法正确工作,您还必须做所有这些,所以这里没有什么真正的变化。(正如Sebastian所建议的那样,使用XPS API的一个潜在优势是,它可以为您完成所有这些。然而,以您想要的形式从XPS文档中提取信息是您的问题,这可能最终会丢失您可能想要保存的信息。)
我认为您的方法不起作用,因为(正如其他人所提到的)您无法提供自己的DrawingContext
实现。
我建议如下:为了"压平"WPF渲染,让WPF将您的视觉效果导出到XPS文档中。在这个过程中,所有渲染基本上都被枚举为简单的渲染基元,剩下的只是Canvas
、基本形状、字形和其他绘图基元。
然后对文档页面中的视觉效果进行迭代。据我所知,生成的视觉将仅由基元组成,因此不需要调用OnRender
。相反,这使您能够从外部内省视觉实例(使用instanceof
-级联并读取/解释属性)。这仍然是相当多的工作,因为您需要像WPF一样解释属性,但据我所见,这至少应该适用于许多主要用例。
我尝试过做类似的事情,为winRT创建一个FlowDocumentViewer。但与WPF相比,WinRT远没有那么成熟,它也向本地层委派了太多(通过渲染线程),我在任何地方都做不到。但这是我学到的,我希望我能很好地解释。
WPF使用硬件加速的图形渲染。因此,简单地说,WPF布局引擎构建逻辑视觉树,然后将其转换为渲染指令,再将其发送到图形硬件执行或渲染。
DrawingContext是一个非平凡的类,它与底层图形系统交互以进行渲染、管理缩放、缓存等。WPF运行时带有默认实现,用于渲染所有视觉效果。IMO,它被制作成一个抽象类的原因是微软可以提供不同的实现,比如Silverlight等。但它意味着被我们覆盖。
如果必须替换WPF呈现,那么最好的方法是创建一个UserControl,覆盖Arrange和Measure调用,并使用DrawingVisual.RenderOpen()将每个元素呈现到DrawingVisual,然后从代码中排列它们等。管理DataBinding通知将是您必须自己做的另一件事。
看起来是一个非常有趣的项目。祝你好运
您可以创建一个派生自FrameworkElement
、UIElement
甚至Visual
的类,该类在其OnRender
方法中执行您的活动,而不是尝试编写自己的DrawingContext
。您仍然必须使用给定的Draw[Something]
实现,但您将控制参数和操作顺序。您仍然可以解析来自辅助源的基元和指令,并且您的一个UIElement/FrameworkElement可以在运行时编写指令。