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

时间:2013-09-03 02:28:03

标签: 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

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); }
    }

}

2 个答案:

答案 0 :(得分:8)

Awesomesausage!我想通了!

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

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

有人可能会认为这对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,事情就会好起来的。

答案 1 :(得分:0)

请在调用InitializeComponent之前注册初始化事件。每当调用EndInit或OnVisualParentChanges方法时,即在InitializeComponent之后,将引发此事件。

  public TestControl()
    {

        this.Initialized += TestControl_Initialized;
        InitializeComponent();
    }

由于