7

(lookless コントロールではなく) カスタム UserControl を作成しており、コンシューマーが XAML でコントロールに設定したプロパティに基づいて初期化を実行する必要があります。

現在、ほとんどの場合、Initialized イベント (または OnInitialized オーバーライド) を使用します。これは、発生するまでにすべての XAML 設定プロパティが適用されているためですが、UserControl の場合はそうではありません。Initialized イベントが発生すると、すべてのプロパティはデフォルト値のままです。

コンストラクターで InitializeComponent() を呼び出すという点で異なる UserControls だけで、他のコントロールではこれに気付かなかったので、テストとして、その行をコメントアウトしてコードを実行し、今回は Initializedイベント、プロパティ設定されました。

これを示すコードとテスト結果を次に示します...

コンストラクターで呼び出された InitializeComponent の結果:
(注: 値はまだ設定されていません)

TestValue (Pre-OnInitialized): Original Value
TestValue (Initialized Event): Original Value
TestValue (Post-OnInitialized): Original Value

InitializeComponent を完全にコメントアウトした結果:
(注: 値が設定されている間、InitializeComponent が必要なため、コントロールは読み込まれません)

TestValue (Pre-OnInitialized): New Value!
TestValue (Initialized Event): New Value!
TestValue (Post-OnInitialized): New Value! // Event *was* called and the property has been changed

とはいえ、XAML でユーザーが設定したプロパティに基づいてコントロールを初期化するには、何を使用すればよいでしょうか? (注: コントロールはそれまでに初期化されているはずなので、Loaded では遅すぎます。)

XAML スニペット

<local:TestControl TestValue="New Value!" />

TestControl.cs

public partial class TestControl : UserControl {

    public TestControl() {
        this.Initialized += TestControl_Initialized;
        InitializeComponent();
    }

    protected override void OnInitialized(EventArgs e) {
        Console.WriteLine("TestValue (Pre-OnInitialized): " + TestValue);
        base.OnInitialized(e);
        Console.WriteLine("TestValue (Post-OnInitialized): " + TestValue);
    }

    void TestControl_Initialized(object sender, EventArgs e) {
        Console.WriteLine("TestValue (Initialized Event): " + TestValue);
    }

    public static readonly DependencyProperty TestValueProperty = DependencyProperty.Register(
        nameof(TestValue),
        typeof(string),
        typeof(TestControl),
        new UIPropertyMetadata("Original Value"));

    public string TestValue {
        get => (string)GetValue(TestValueProperty);
        set => SetValue(TestValueProperty, value);
    }
}
4

2 に答える 2

10

すごいソーセージ!私はそれを考え出した!

通常、Initializedイベントを受け取る (またはオーバーライド内にいるOnInitialized) 場合、XAML 設定のプロパティ値にアクセスできます。ただし、UI をハイドレートし、関連するメンバー変数を設定するために呼び出されるUserControlことに依存するため、クラスの動作は少し異なります。InitializeComponent

問題は、呼び出しがコンストラクター内にあることです。これにより、呼び出しが終了しOnInitialized(したがってInitializedイベントが発生します)、XAML セットのプロパティが適用される前に発生します。つまり、まだそれらにアクセスできないことを意味します。必要です。

Loadedこれらのプロパティに基づいて初期化を完了するために、イベントの良い使い方だと思うかもしれませんが、そこで追加の初期化を実行している場合、消費者がLoadedイベントにサブスクライブする場合に、消費者との潜在的な競合状態を作成しています。あなたの前にそれを取得し、ハンドラーであなたのコントロールにアクセスしようとすると、初期化されていないコントロールにアクセスします。

その後、何かが起こりました...上で示したようにInitializeComponent、コンストラクターから呼び出しを削除すると、Initializedイベントは期待どおりに機能しますが、まだ呼び出していないため、もちろん UI はまだハイドレートされていませんInitializeComponent

OnInitializedでは、その呼び出しをオーバーライドの先頭、つまり への呼び出しのbase.OnInitialized前、つまりInitializedイベントが発生する前に移動するとどうなるでしょうか?

うん!それはうまくいきました!:)

Initializedこの方法では、XAML で設定されたプロパティを取得できるだけでなく、(イベントは言うまでもなく)誰かがイベントを取得する前に UI を完全に読み込むこともできますLoaded。これが、イベントのInitialized使用方法です。

以下は修正されたコードです...

public partial class TestControl : UserControl {

    protected override void OnInitialized(EventArgs e) {
        InitializeComponent();
        base.OnInitialized(e);
    }

    public static readonly DependencyProperty TestValueProperty = DependencyProperty.Register(
        nameof(TestValue),
        typeof(string),
        typeof(TestControl),
        new UIPropertyMetadata("Original Value"));

    public string TestValue {
        get => (string)GetValue(TestValueProperty);
        set => SetValue(TestValueProperty, value);
    }
}
  • 注: そこで他のことを行う必要がある場合を除き、コンストラクターはもう必要ありません。その場合は、呼び出しの後まで名前で構成要素のコントロールにアクセスできないことを覚えておいてくださいInitializeComponent。ただし、これは、そのような名前ベースの初期化をInitializeComponentとの間で移動することを計画する必要があることを意味し、問題なくbase.OnInitialize動作します。
于 2013-09-03T03:26:14.083 に答える