2013-08-12 25 views
16

通常のWPFレンダリングをハイジャックして、コントロールをプリミティブに分割し、レイアウト管理を行い、バインディングなどを適用したいと思います。カスタムDrawingContextへのレンダリング

私の知る限り、WPFのレンダリング全体は、依存関係プロパティシステムによって定義された値を持つレイアウトマネージャによって計算された場所にプリミティブ(テキスト、イメージ、ライン、カーブ)をレンダリングすることになっています。私自身のプリミティブ・レンダリング・ロジックを供給できるなら、私はレンダリングすることができます。カスタムドキュメントタイプに、ネットワーク上で実際のレンダリングのためのプリミティブを転送するなど

私の計画は以下の通りです:

  1. 、カスタムDrawingContextを実装します。 DrawingContextは抽象クラスであり、DrawEllipse,DrawTextDrawImageなどのメソッドを定義しています。この機能のために独自の実装を用意する必要があります。
  2. WPF UserControlを作成し、指定した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でサポートされているカスタムコンテキストに描画しているのですか、または別のインターセプトポイントを探す必要がありますか?

+6

DrawingContextの注釈から: 'DrawingContextを直接インスタンス化することはありません。 DrawingGroup.OpenやDrawingVisual.RenderOpenなどの特定のメソッドから描画コンテキストを取得することができます。これは、実際に*カスタムDrawingContextをどこかに提供する方法がないことを意味します。 – Clemens

+0

@クレメンス:はい、私はこの発言を見ましたが、「自分で作成するのではなく、内部的にやって、DrawingVisualに描画するだけで適切に初期化させてください」と理解しました。とにかく面白いのは、描画に有効なインターセプトポイントがある場合です。 – Vlad

+2

DrawingVisualに描画する唯一の方法は、DrawingVisual.RenderOpenによって提供されるDrawingContextに描画することです。カスタムDrawingContextをVisualに関連付ける方法はありません。アイデアは無意味です。 – Clemens

答えて

3

逆方向からこの問題にアプローチする必要があります。独自のDrawingContextを提供することを目指す代わりに、代わりにDrawingを提供するようにWPFに依頼することができます。だから、あなたが目指している「プッシュ」アプローチよりも「プル」アプローチのほうがいいですが、同じ場所に到達できるようにする必要があります。Drawingがあると、パーツの外観が完全に表現されています視覚的なツリーのそれは、あなたが歩くことができ、あなたがカスタムからDrawingContextへの呼び出しから発見したであろうすべてを発見することができるデータ構造です。

これは、セバスチャンが内部で使用しているXPSドキュメントのエクスポートと同じ基本的なアプローチだと思います。しかし、それを直接自分で使うのは、XPS APIを使うよりも直接的なアプローチです。

心にはかなり簡単なものがあります:VisualTreeHelper.GetDrawingです。これはDrawingGroupを返します。 (Drawingは抽象基本クラスです。)このドキュメンテーションページでは、戻ってくるツリーをどのように歩くかを示しています。残念ながら、これはすべての仕事を行うわけではありません。ノードを呼び出すことができるノードにビジュアルを提供するだけで、ノードに子がある場合は含まれません。

あなたは残念ながら、既に計画していたのと同じように、ビジュアルツリーを再帰的に書き出すものを作成する必要があります。また、正しい結果を得るためには、ビジュアルに添付されている不透明マスク、非マスクベースの不透明度、クリップ領域、エフェクト、変換も処理する必要があります。あなたは提案されたアプローチが正しく機能するようにすべてをやらなければなりませんでしたので、ここでは何も変わりません。セバスチャンが示唆するようにXPS APIを使用するメリットの1つは、すべてがこれを行うということですが、XPSドキュメントから必要な形式で情報を抽出することは問題です。保存したい場合があります。)

+0

私のテストではこれはうまくいかなかった。 'OnRender'の間に描画されるビジュアルはそのヘルパーメソッドでは列挙されないので、' GetDrawing'はほとんどの 'Control'に対して' null'を返すので、私が提案したアプローチを使用する必要があると思います。あなたは 'ヌル'を得るでしょう。 – Sebastian

+0

nullでない 'BorderBrush'と非ゼロの' BorderThickness'を持つ 'Border'で' GetDrawing'を呼び出すと、同じGeometryDrawingを含む非nullの 'DrawingGroup'が返されますサイズは 'Border'、' RectangleGeometry'は 'Geometry'、' Pen'は 'Thickness'と' BorderBrush'の設定にマッチします。万が一あなたがa)実際に何かを塗っていない国境、またはb)その視覚をまだ実現していなかった国境のいずれかであなたのテストを行ったことがありますか? –

+0

あなたはここにいるかもしれません - ボーダーは以前は視覚化されていませんでした。それはおそらく問題ですが、私はまだ確認しませんでした。誤った告発をして申し訳ありません! – Sebastian

2

同様のことをして、winRT用のFlowDocumentViewerを作成しようとしました。しかし、WinRTはWPFに比べてはるかに成熟していないので、レンダリングスレッドを使用してネイティブレイヤーにあまりにも多くのデリゲートを行いました。しかし、これは私が学んだことであり、私はそれをうまく説明してくれることを願っています。

WPFは、ハードウェアアクセラレーションによるグラフィックスレンダリングを使用します。つまり、WPF LayoutEngineは論理的なビジュアルツリーを構築し、レンダリング命令に変換され、実行またはレンダリングするためにGraphicsハードウェアに送られます。

DrawingContextは、レンダリングのために基になるグラフィックスシステムとやり取りし、スケーリングやキャッシングなどを管理します。 WPFランタイムには、すべてのビジュアルのレンダリングを行うデフォルトの実装が付属しています。 IMO、それが抽象クラスに作られた理由は、MicrosoftがSilverlightなどに対してさまざまな実装を提供できるようにすることです。しかし、それは私たちによってオーバーライドされることを意味します。

WPFレンダリングを置き換える必要がある場合は、UserControlを作成し、アレンジとメジャーの呼び出しをオーバーライドし、DrawingVisual.RenderOpen()を使用して各要素をDrawingVisualにレンダリングし、コードから配置することをお勧めします。データバインディング通知を管理することは、自分で行う必要があります。

非常に興味深いプロジェクトのようです。がんばろう!

+0

実際、ヒットテストのような単純なものには、多くの内部の 'DrawingContext'があります。私の最終的な目標は、いくつかのドキュメントタイプにWPFをレンダリングするので、カスタムのUserControlは役に立ちません。とにかくあなたのアドバイスをありがとう! – Vlad

4

私はあなたのアプローチがうまくいかないと思います。他の人も触れたように、あなた自身のDrawingContext実装を提供できないからです。

代わりに以下をお勧めします。WPFレンダリングを「平坦化」するには、WPFでビジュアルをXPSドキュメントに書き出すようにしてください。このプロセス中、すべてのレンダリングは基本的に単純レンダリングプリミティブとして列挙され、残されたものはすべてCanvas、基本図形、グリフ、およびその他の描画プリミティブです。

次に、ドキュメントのページのビジュアルを繰り返します。私が知る限り、結果のビジュアルはプリミティブのみで構成されるため、OnRenderに電話する必要はありません。代わりに、これを使用すると、外部インスタンスを視覚的にイントロスペクションすることができます(instanceof - カスケードを使用してプロパティを読み取る/解釈する)。これはWPFと同じようにプロパティを解釈する必要があるため、まだかなり多くの作業ですが、私が見る限り、これは少なくとも多くの主要なユースケースで機能するはずです。

+0

XPSドキュメントを使ったアプローチは良い方法だと思われます。 XPSドキュメントの内容を迅速に確認すると、元のフォントは保存されずにドキュメントに埋め込まれますが、埋め込まれたフォント自体を使用できるため、問題はありません。このアプローチで考えられる問題は、WPFでは複雑さを制御できますが、XPSでは単純なWPFソースでもすべてのXPS機能を解析する必要があるかもしれません。 – Vlad

+0

お返事ありがとうございました。私があなたが提案する方法を選択することを選択しなかったが、それは明らかに非常に良いアイデアであり、より多くのupvotesに値する。 – Vlad

1

の代わりに多分あなたはそのOnRender方法であなたの活動を行ってFrameworkElementまたはUIElementあるいはVisualから派生クラスを作成することができ、独自のDrawingContextを作成しようとします。あなたはまだDraw[Something]の与えられた実装を使用する必要がありますが、あなたは引数と操作の順序を制御します。セカンダリソースからプリミティブと命令を解析することができ、実行時にUIElement/FrameworkElementで命令を構成できます。

+0

これは動作しますが、カスタムビジュアルのみです。私の問題は、 TextBlockのOnRenderがやっています。 – Vlad

+0

これは興味深い問題です。答えを見つけたら経験を共有することを検討してください。 :-) –

関連する問題