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

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

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

我没有注意到其他控件,只是UserControls,它们在构造函数中调用InitializeComponent()是不同的,所以作为测试,我注释掉了行并运行代码,果然,这次是在Initialized期间事件,属性设置。

以下是一些代码和测试结果,certificate了这一点……

在构造函数中调用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片段

  

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

Awesomesausage! 我想到了!

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

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

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

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

那么如果在调用base.OnInitialized之前将调用移动到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调用之前你不能按名称访问组成控件,但这只是意味着你必须计划在InitializeComponentbase.OnInitialize之间调用这样的基于名称的初始化,事情会起作用正好。

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

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

谢谢