WPF、XAML記述オブジェクトの生成順序

業務多忙によりネタの新鮮味が無くなってきた気がするこの頃。WPFアプリケーションの開始・初期化順について確認しておきます。

さっそくですが、いくつかログを埋め込んだだけのクラスを用意します。

DummyObject.cs

namespace WpfAppStarting
{
    using System;
    public class DummyObject
    {
        public DummyObject()
        {
            Console.WriteLine("DummyObject-Constructor called.");
        }
        public DummyProperty Property { get; set; }
    }
}

DummyProperty.cs

namespace WpfAppStarting
{
    using System;
    using System.ComponentModel;
    [TypeConverter(typeof(DummyTypeConverter))]
    public class DummyProperty
    {
        public DummyProperty()
        {
            Console.WriteLine("DummyPropety-Constructor called.");
        }
    }
}

DummyValueConverter.cs(string->DummyPropertyのIValueConverter)

namespace WpfAppStarting
{
    using System;
    using System.Windows.Data;
    public class DummyValueConverter : IValueConverter
    {
        public DummyValueConverter()
        {
            Console.WriteLine("DummyValueConverter-Constructor called.");
        }
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Console.WriteLine("DummyValueConverter-Convert called.");
            DummyProperty obj = value as DummyProperty;
            return obj.ToString();
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

DummyTypeConverter.cs(string->DummyPropertyのTypeConverter)

namespace WpfAppStarting
{
    using System;
    using System.ComponentModel;
    public class DummyTypeConverter : TypeConverter
    {
        public DummyTypeConverter()
        {
            Console.WriteLine("TypeConverter-Constructor called.");
        }
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            Console.WriteLine("TypeConverter-CanConvertFrom called.");
            if (sourceType == typeof(string)){
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            Console.WriteLine("TypeConverter-ConvertFrom called.");
            if (value is string){
                return new DummyProperty();
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
}

これらはバインドの様子を確認するためのものです。XAMLで使うであろうIValueConverterとTypeConverterを定義していますがこれらが何者かはこちらを参照ください。これらに加えて、WPFコントロールのコンストラクタでログを出力するようにしたクラスも追加し、構成は以下にしておきました。

project_kousei

そしてようやくメインとなる、MainWindowはこちら。

MainWindow.xaml.cs

window (←より↓が重要)

<Window x:Class="WpfAppStarting.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:me="clr-namespace:WpfAppStarting"
        Title="MainWindow" Height="240" Width="525">
    <Window.Resources>
        <!--↓ここでPropertyにstringを設定してTypeConverterが働きます,以下同じ-->
        <me:DummyObject x:Key="TestResourceObject1" Property="TestString1"/>
        <me:DummyValueConverter x:Key="TestValueConverter"/>
        <me:DummyObject x:Key="TestResourceObject2" Property="TestString2"/>
        <me:DummyObject x:Key="TestResourceObject3" Property="TestString3"/>
    </Window.Resources>
    <me:ExtendedGrid>
        <me:ExtendedStackPanel>
            <me:ExtendedTextBox>
                <!--↓ここでConverterに指定してDummyValueConverterが働きます,以下同じ-->
                <Binding Source="{StaticResource TestResourceObject1}" Path="Property" Converter="{StaticResource TestValueConverter}"/>
            </me:ExtendedTextBox>
            <me:ExtendedTextBox>
                <Binding Source="{StaticResource TestResourceObject1}" Path="Property" Converter="{StaticResource TestValueConverter}"/>
            </me:ExtendedTextBox>
        </me:ExtendedStackPanel>
        <me:ExtendedStackPanel Margin="0,50,0,0">
            <me:ExtendedTextBox>
                <Binding Source="{StaticResource TestResourceObject3}" Path="Property" Converter="{StaticResource TestValueConverter}"/>
            </me:ExtendedTextBox>
        </me:ExtendedStackPanel>
    </me:ExtendedGrid>
</Window>

これを実行すると以下のようなログが出ます。見やすいかと思って勝手にインデント付けていますが順序は当然入れ替えていません。

'WpfAppStarting.vshost.exe' (CLR v4.0.30319: WpfAppStarting.vshost.exe): 'C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll' が読み込まれました。シンボルが読み込まれました。
<<...途中省略>>
'WpfAppStarting.vshost.exe' (CLR v4.0.30319: WpfAppStarting.vshost.exe): 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\mscorlib.resources\v4.0_4.0.0.0_ja_b77a5c561934e089\mscorlib.resources.dll' が読み込まれました。モジュールがシンボルなしでビルドされました。
'WpfAppStarting.vshost.exe' (CLR v4.0.30319: WpfAppStarting.vshost.exe): 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll' が読み込まれました。シンボルが読み込まれました。
<<ここからユーザーログが出だす>>
   MainWindow-Constructor called(before InitializeComponent).
      ExtendedGrid-Constructor called.
         ExtendedStackPanel-Constructor called.
            ExtendedTextBox-Constructor called.
               DummyObject-Constructor called.
               TypeConverter-Constructor called.
               TypeConverter-ConvertFrom called.
               DummyPropety-Constructor called.
               DummyValueConverter-Constructor called.
               DummyValueConverter-Convert called.
            ExtendedTextBox-Constructor called.
               DummyValueConverter-Convert called.
         ExtendedStackPanel-Constructor called.
            ExtendedTextBox-Constructor called.
               DummyObject-Constructor called.
               TypeConverter-ConvertFrom called.
               DummyPropety-Constructor called.
               DummyValueConverter-Convert called.
MainWindow-Initialized event occured.
   MainWindow-Constructor called(after InitializeComponent).
MainWindow-SourceInitialized event occured.
MainWindow-Activated event occured.
MainWindow-Loaded event occured.
MainWindow-ContentRendered event occured.
MainWindow-Activated event occured.

これからわかることとして、

  1. WPFのXAMLの解釈は上から順に行われている、リソースで定義されたオブジェクトが優先して作成されている訳ではない。
  2. オブジェクト参照は必要になったタイミングで解決している、よってリソースとして定義しても使われなければオブジェクトは作成されない。

があります。MainWindowのコンストラクタで呼んでいるInitializeComponentでは、

  1. MainWindow.xamlの解釈&オブジェクト参照を解決
  2. XAML構文エラーにならなければInitializedイベントを発砲して終了

しています、改めて見返すとちょっとスッキリ。

ちなみにMainWindowのコンストラクタを呼ぶのは自動生成されるソースのapp.i.csですね。またこのMainWindowの生成後に発生するイベントの順序はMSDNに記載がありますので、そちらを参照ください。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中