17

我想劫持通常的 WPF 渲染来将控件拆分为基元,进行布局管理,为我应用绑定等。

据我了解,WPF 中的整个渲染归结为在布局管理器计算的位置处使用依赖属性系统定义的值渲染图元(文本、图像、线条、曲线)。如果我可以提供自己的原始渲染逻辑,我将能够渲染例如自定义文档类型,传输原始渲染以通过网络进行真实渲染等。

我的计划如下:

  1. 实现一个自定义DrawingContext. TheDrawingContext是一个抽象类,它定义了一堆方法,如,DrawEllipse等。我需要为这个功能提供我自己的实现。DrawTextDrawImage
  2. 创建一个 WPFUserControl并强制它渲染到给定的DrawingContext.

但是我遇到了以下问题:

  1. DrawingContext包含抽象的内部方法void PushGuidelineY1(double coordinate)void PushGuidelineY2(double leadingCoordinate, double offsetToDrivenCoordinate),我不能轻易覆盖。(也许有一些技巧可以克服这个问题?)
  2. 似乎没有方法可以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);
}

- 但我想知道是否有直接的方法来渲染UIElement. (当然,这个问题是一个小问题,但没有看到它的基础设施让我怀疑这是否是正确的方法。)

那么,DrawingContext不是为了继承吗?提供定制的整个想法是DrawingContext朝着正确方向迈出的一步,还是我需要重新考虑策略?是在 WPF 支持的自定义上下文上绘图,还是我需要寻找不同的拦截点?

4

4 回答 4

4

我认为您的方法行不通,因为(正如其他人提到的)您无法提供自己的DrawingContext实现。

我建议改为:为了“扁平化” WPF 渲染,让 WPF 将您的视觉效果导出到 XPS 文档。在这个过程中,所有的渲染基本上都被列举为简单的渲染图元,剩下的就是Canvass、基本形状、字形和其他绘图图元。

然后迭代文档页面中的视觉效果。据我所知,生成的视觉效果将仅由基元组成,因此无需调用OnRender. 相反,这使您能够从外部自省视觉实例(使用instanceof-cascades 和读取/解释属性)。这仍然需要大量工作,因为您需要像 WPF 那样解释属性,但据我所知,这至少适用于许多主要用例。

于 2013-08-21T13:43:22.123 回答
4

您可能需要从相反的方向解决这个问题。您可以要求WPFDrawingContext为您提供一个Drawing. 因此,它更像是一种“拉动”方法,而不是您所针对的“推动”方法,但它应该可以到达同一个地方:如果您有Drawing一个完整表示部分外观的方法可视化树,这是一种数据结构,您可以浏览并发现从调用自定义DrawingContext.

我相信这与塞巴斯蒂安提到的 XPS 文档导出内部使用的基本方法相同。但是自己直接使用它是比通过 XPS API 使用它更直接的方法

核心是相当简单的事情:VisualTreeHelper.GetDrawing. 这会返回一个DrawingGroup. (Drawing是一个抽象基类。)该文档页面向您展示了如何遍历您返回的树。不幸的是,这并不能完成全部工作:它只是为您碰巧调用它的任何节点提供视觉效果,如果该节点有子节点,则不会包含它们。

因此,不幸的是,您仍然必须编写一些递归可视化树的东西,就像您已经计划好的一样。您还需要处理附加到视觉对象的任何不透明蒙版、非基于蒙版的不透明度、剪辑区域、效果和转换,以获得正确的结果;您也必须做所有这些事情才能使您提出的方法正常工作,所以这里没有什么真正改变。(正如 Sebastian 所建议的那样,使用 XPS API 的一个潜在优势是它可以为您完成所有这些工作。但是,以您想要的形式从 XPS 文档中提取信息是您的问题,而这最终可能会丢失您的信息可能想保留。)

于 2013-08-21T14:18:20.333 回答
2

我试图做类似的事情来为 winRT 创建一个 FlowDocumentViewer。但是与 WPF 相比,WinRT 远没有那么成熟,它也将太多的委托给本地层(通过渲染线程),我无处可去。但这是我学到的,我希望我能很好地解释它。

WPF 使用硬件加速图形渲染。因此,简单来说,WPF LayoutEngine 构建逻辑视觉树,然后将其转换为渲染指令,然后将其发送到图形硬件以执行或渲染。

DrawingContext 是一个重要的类,它与底层图形系统交互以进行渲染、管理缩放、缓存等。WPF 运行时带有默认实现,可以渲染所有视觉效果。IMO,它被制成抽象类的原因是微软可以为 Silverlight 等提供不同的实现。但它意味着被我们覆盖。

如果您必须替换 WPF 渲染,那么最好的办法是创建一个 UserControl,覆盖 Arrange 和 Measure 调用,并使用 DrawingVisual.RenderOpen() 将每个元素渲染到 DrawingVisual,并从您的代码中排列它们等。管理 DataBinding 通知将是您必须自己做的另一件事。

似乎是一个非常有趣的项目。祝你好运!

于 2013-08-21T12:10:10.007 回答
1

DrawingContext您可以创建一个派生自或什至在其方法中执行您的活动的类,FrameworkElementUIElement不是Visual尝试OnRender编写您自己的类。您仍然必须使用给定的实现,Draw[Something]但您将控制参数和操作顺序。您仍然可以从辅助来源解析原语和指令,并且您的一个 UIElement/FrameworkElement 可以在运行时编写指令。

于 2013-08-22T16:33:43.817 回答