渲染到自定义DrawingContext

时间:2013-08-12 17:32:16

标签: c# wpf drawingcontext

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

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

我的计划如下:

  1. 实施自定义DrawingContextDrawingContext是一个抽象类,它定义了一堆方法,如DrawEllipseDrawTextDrawImage等。 - 我需要为此功能提供我自己的实现
  2. 创建一个WPF UserControl并强制它呈现给定的DrawingContext
  3. 但是我遇到了以下问题:

    1. DrawingContext包含抽象的内部方法void PushGuidelineY1(double coordinate)void PushGuidelineY2(double leadingCoordinate, double offsetToDrivenCoordinate),我无法轻易覆盖。 (也许有一些技巧可以克服这个问题?)
    2. 似乎没有方法在DrawingContext上呈现整个视觉效果?为什么?
    3. 我可以做类似

      的事情
      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 个答案:

答案 0 :(得分:4)

我认为您的方法不起作用,因为(正如其他人提到的那样)您无法提供自己的DrawingContext实施。

我建议如下:为了“展平”WPF渲染,让WPF将您的视觉效果导出到XPS文档。在此过程中,所有渲染基本上都被枚举为简单的渲染基元,而您剩下的所有渲染都是Canvas s,基本形状,字形和其他绘图基元。

然后迭代文档页面中的视觉效果。据我所知,由此产生的视觉效果只包含基元,因此无需调用OnRender。相反,这使您能够从外部内省视觉实例(使用instanceof - 级联和读取/解释属性)。这仍然是相当多的工作,因为您需要像WPF一样解释属性,但据我所知,这至少应该适用于许多主要的用例。

答案 1 :(得分:3)

您可能需要从相反的方向处理此问题。您可以改为要求WPF为您提供DrawingContext,而不是旨在提供您自己的Drawing。所以它更像是一种“拉”方法而不是你想要的'推'方法,但它应该能够到达同一个地方:如果你有一个Drawing,那就完全代表了视觉树的一部分的外观,这是一个数据结构,您可以走路并发现从调用自定义DrawingContext时发现的所有内容。

我认为这与Sebastian提到的XPS文档导出内部使用的基本方法相同。但是直接使用它比使用XPS API更直接的方法

内心很简单:VisualTreeHelper.GetDrawing。这将返回DrawingGroup。 (Drawing是一个抽象基类。)该文档页面向您展示如何遍历您获得的树。不幸的是,这并没有完成整个工作:它只是为您碰巧调用的节点提供视觉效果,如果该节点有子节点,则不会包含它们。

所以,不幸的是,你仍然需要写一些可以追溯视觉树的东西,就像你已经在计划的那样。您还需要处理任何不透明蒙版,基于非蒙版的不透明度,剪辑区域,效果和附加到视觉上的变换以获得正确的结果;你必须做所有这些,以使你提出的方法正常工作,所以这里没有任何真正的改变。 (使用XPS API的一个潜在优势就像塞巴斯蒂安所说的那样,它会为你完成所有这些。但是,你需要以你想要的形式从XPS文档中提取信息,这可能最终会丢失你的信息可能想保留。)

答案 2 :(得分:2)

我尝试为winRT创建一个FlowDocumentViewer。但是由于WinRT与WPF相比还不成熟,所以它也过多地委托原生层(通过渲染线程),我无法在任何地方获得。但这就是我所学到的,我希望我能很好地解释它。

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

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

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

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

答案 3 :(得分:1)

您可以创建一个源自DrawingContextFrameworkElement甚至UIElement的类,而不是尝试编写您自己的Visual。 1}}方法。您仍然必须使用OnRender的给定实现,但您将控制参数和操作顺序。您仍然可以从辅助源解析基元和指令,并且您的一个UIElement / FrameworkElement可以在运行时编写指令。