如何在初始化期间访问UserControl的XAML设置属性?

Mar*_*eIV 5 c# wpf user-controls initialization

我们正在编写自定义UserControl(而不是无形控件),我们需要根据消费者在XAML中控制的属性执行一些初始化.

现在在大多数情况下,您将使用Initialized事件(或OnInitialized覆盖),因为在触发时,已应用所有XAML集属性,但在UserControl的情况下,情况并非如此.触发Initialized事件时,所有属性仍处于默认值.

我没有注意到其他控件,只是UserControls,不同之处在于它们在构造函数中调用InitializeComponent(),所以作为测试,我评论说该行并运行代码,果然,这次是在Initialized期间事件,属性进行设置.

以下是一些代码和测试结果,证明了这一点......

在构造函数中调用InitializeComponent的结果:(
注意:值仍未设置)

TestValue (Pre-OnInitialized): Original Value
TestValue (Initialized Event): Original Value
TestValue (Post-OnInitialized): Original Value
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

所有这些说,我可以使用什么来初始化我的控件基于XAML中的用户设置属性?(注意:加载已经太晚了,因为到那时控件应该已经初始化了.)

XAML片段

<local:TestControl TestValue="New Value!" />
Run Code Online (Sandbox Code Playgroud)

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(
        "TestValue",
        typeof(string),
        typeof(TestControl),
        new UIPropertyMetadata("Original Value"));

    public string TestValue
    {
        get { return (string)GetValue(TestValueProperty); }
        set { SetValue(TestValueProperty, value); }
    }

}
Run Code Online (Sandbox Code Playgroud)

Mar*_*eIV 8

Awesomesausage!我想到了!

通常,当您收到Initialized事件(或在OnInitialized覆盖内)时,您可以访问XAML设置的属性值.但是,UserControl类的工作方式略有不同,因为它们依赖于InitializeComponent调用UI以及设置相关的成员变量等.

问题是调用是在构造函数中,而构造函数最终会调用OnInitialized(从而引发Initialized事件)但是在应用XAML集属性之前发生了这种情况,这意味着你还没有访问它们,这就是我需要.

有人可能认为这对Loaded事件很有用- 根据这些属性完成初始化 - 但是如果你在那里进行额外的初始化,那么如果他们订阅了你的Loaded事件,你就会与你的消费者创造潜在的竞争条件然后在你的处理程序中尝试访问你的控件,他们将访问一个未初始化的控件.

然后我发生了一些事情......正如我在上面所示,如果你InitializeComponent从构造函数中删除了调用,那么Initialized事件现在可以正常运行,但是当然你的UI还没有被充实,因为你尚未调用InitializeComponent.

那么,如果您在调用OnInitialized之前将该调用移动到覆盖的开头base.OnInitialized,并且因此在Initialized事件被引发之前,会发生什么?

是的!那很有效!:)

这样,您不仅可以拥有XAML设置属性,而且还可以在任何人获取Initialized事件之前完全加载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(
        "TestValue",
        typeof(string),
        typeof(TestControl),
        new UIPropertyMetadata("Original Value"));

    public string TestValue
    {
        get { return (string)GetValue(TestValueProperty); }
        set { SetValue(TestValueProperty, value); }
    }

}
Run Code Online (Sandbox Code Playgroud)
  • 注意:除非您特别需要在那里做其他事情,否则不再需要构造函数.如果你这样做,只记得在InitializeComponent调用之前你不能按名称访问组成控件,但这只是意味着你必须计划在InitializeComponent这个调用之间移动这种基于名称的初始化,base.OnInitialize并且事情会正常工作.