Windows Presentation Foundation (WPF) の初期化の一部の側面は、通常、論理ツリーまたはビジュアル ツリーに接続されているその要素に依存するプロセスに遅延されます。 このトピックでは、どちらのツリーにも接続されていない要素を初期化するために必要な手順について説明します。
要素と論理ツリー
コードで Windows Presentation Foundation (WPF) クラスのインスタンスを作成する場合、Windows Presentation Foundation (WPF) クラスのオブジェクト初期化のいくつかの側面は、クラス コンストラクターを呼び出すときに実行されるコードの一部ではないことに注意する必要があります。 特にコントロール クラスの場合、そのコントロールの視覚的表現のほとんどはコンストラクターによって定義されません。 代わりに、ビジュアル表現はコントロールのテンプレートによって定義されます。 テンプレートはさまざまなソースから取得される可能性がありますが、ほとんどの場合、テンプレートはテーマ スタイルから取得されます。 テンプレートは実質的に遅延バインディングです。必要なテンプレートは、コントロールがレイアウトの準備ができるまで、該当するコントロールにアタッチされません。 コントロールは、ルートでレンダリングするための表面に接続される論理ツリーに適用されるまで、レイアウトが可能になりません。 論理ツリーで定義されているすべての子要素のレンダリングを開始するのは、ルート レベルの要素です。
ビジュアル ツリーもこのプロセスに参加します。 テンプレートを介してビジュアル ツリーの一部である要素も、接続されるまで完全にはインスタンス化されません。
この動作の結果として、要素の完成した視覚的特性に依存する特定の操作には、追加の手順が必要になります。 たとえば、構築されたが、ツリーにまだアタッチされていないクラスの視覚的特性を取得しようとしている場合です。 たとえば、RenderTargetBitmap で Render を呼び出し、渡すビジュアルがツリーに接続されていない要素である場合、追加の初期化手順が完了するまで、その要素は視覚的に完了しません。
BeginInit と EndInit を使用して要素を初期化する
WPF のさまざまなクラスは、ISupportInitialize インターフェイスを実装します。 インターフェイスの BeginInit メソッドと EndInit メソッドを使用して、初期化手順 (レンダリングに影響するプロパティ値の設定など) を含むコード内の領域を示します。 シーケンスで EndInit が呼び出されると、レイアウト システムは要素を処理し、暗黙的なスタイルの検索を開始できます。
プロパティを設定する要素が FrameworkElement または FrameworkContentElement 派生クラスである場合は、ISupportInitializeにキャストするのではなく、BeginInit と EndInit のクラス バージョンを呼び出すことができます。
サンプル コード
次の例は、レンダリング API と緩い XAML ファイルの XamlReader.Load(Stream) を使用して、レンダリングに影響するプロパティを調整する他の API 呼び出しに関する BeginInit と EndInit の適切な配置を示すコンソール アプリケーションのサンプル コードです。
この例では、main 関数のみを示しています。 Rasterize
関数と Save
関数 (図示せず) は、画像処理と IO を処理するユーティリティ関数です。
[STAThread]
static void Main(string[] args)
{
UIElement e;
string file = Directory.GetCurrentDirectory() + "\\starting.xaml";
using (Stream stream = File.Open(file, FileMode.Open))
{
// loading files from current directory, project settings take care of copying the file
ParserContext pc = new ParserContext();
pc.BaseUri = new Uri(file, UriKind.Absolute);
e = (UIElement)XamlReader.Load(stream, pc);
}
Size paperSize = new Size(8.5 * 96, 11 * 96);
e.Measure(paperSize);
e.Arrange(new Rect(paperSize));
e.UpdateLayout();
/*
* Render effect at normal dpi, indicator is the original RED rectangle
*/
RenderTargetBitmap image1 = Rasterize(e, paperSize.Width, paperSize.Height, 96, 96);
Save(image1, "render1.png");
Button b = new Button();
b.BeginInit();
b.Background = Brushes.Blue;
b.Width = b.Height = 200;
b.EndInit();
b.Measure(paperSize);
b.Arrange(new Rect(paperSize));
b.UpdateLayout();
// now render the altered version, with the element built up and initialized
RenderTargetBitmap image2 = Rasterize(b, paperSize.Width, paperSize.Height, 96, 96);
Save(image2, "render2.png");
}
<STAThread>
Shared Sub Main(ByVal args() As String)
Dim e As UIElement
Dim _file As String = Directory.GetCurrentDirectory() & "\starting.xaml"
Using stream As Stream = File.Open(_file, FileMode.Open)
' loading files from current directory, project settings take care of copying the file
Dim pc As New ParserContext()
pc.BaseUri = New Uri(_file, UriKind.Absolute)
e = CType(XamlReader.Load(stream, pc), UIElement)
End Using
Dim paperSize As New Size(8.5 * 96, 11 * 96)
e.Measure(paperSize)
e.Arrange(New Rect(paperSize))
e.UpdateLayout()
'
' * Render effect at normal dpi, indicator is the original RED rectangle
'
Dim image1 As RenderTargetBitmap = Rasterize(e, paperSize.Width, paperSize.Height, 96, 96)
Save(image1, "render1.png")
Dim b As New Button()
b.BeginInit()
b.Background = Brushes.Blue
b.Height = 200
b.Width = b.Height
b.EndInit()
b.Measure(paperSize)
b.Arrange(New Rect(paperSize))
b.UpdateLayout()
' now render the altered version, with the element built up and initialized
Dim image2 As RenderTargetBitmap = Rasterize(b, paperSize.Width, paperSize.Height, 96, 96)
Save(image2, "render2.png")
End Sub
関連項目
.NET Desktop feedback