我们正在编写一个自定义UserControl(而不是一个无外观的控件),并且需要根据使用者在XAML中的控件上设置的属性来执行一些初始化。

现在,在大多数情况下,您将使用Initialized事件(或OnInitialized覆盖),因为在触发时,所有XAML设置的属性均已应用,但对于UserControl,情况并非如此。当初始化事件触发时,所有属性仍为默认值。

我没有注意到其他控件,只是UserControl,它们的不同之处在于它们在其构造函数中调用了InitializeComponent(),因此作为测试,我注释了这一行并运行了代码,这是确定的,这次是在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中的用户设置属性使用什么来初始化控件? (注意:加载已太迟,因为届时控件应该已经初始化了。)

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

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

}

最佳答案

很棒的香肠!我想到了!

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

问题在于调用是在构造函数中进行的,而该构造函数最终会调用OnInitialized(从而引发Initialized事件),但这是在应用XAML-set属性之前发生的,这意味着您尚无权访问它们,我需要的。

可能有人认为Loaded事件是一种很好的用法-根据这些属性完成初始化-但如果您在此处执行其他初始化,则可能会与消费者产生潜在的竞争条件,因为如果消费者订阅了您的Loaded事件并在您之前获取它,然后在其处理程序中尝试访问您的控件,他们将访问未初始化的控件。

然后发生了一些事情……如上所示,如果您从构造函数中删除了InitializeComponent调用,则Initialized事件现在可以按您期望的那样工作了,但是您的UI当然还没有水化,因为您尚未调用InitializeComponent

那么,如果将调用移到OnInitialized覆盖的开头,调用base.OnInitialized之前,进而引发Initialized事件之前,将会发生什么情况呢?

是的!奏效了! :)

这样,您不仅可以拥有XAML设置的属性,而且还可以在任何人获得Initialized事件(更不用说Loaded事件)之前都已完全加载UI,这就是应该使用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); }
    }

}
  • 注意:除非您特别需要在那里执行其他操作,否则您不再需要构造函数。而且,如果这样做了,请记住您必须在InitializeComponent调用之后才能按名称访问组成控件,但这仅意味着您必须计划在InitializeComponent之间移动此类基于名称的初始化,并且对base.OnInitialize的调用将正常进行。
  • 关于c# - 在初始化期间,如何访问UserControl的XAML设置属性?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18583196/

    10-13 06:06