WPF UserControl如何在所有传入属性赋值完成后执行初始化?
WPF UserControl XAML传值初始化时机问题解决方案
根本原因
你遇到的现象本质是WPF的控件初始化顺序问题:XAML解析器创建控件实例时,会先执行控件的构造函数,完成后才会给XAML中声明的自定义属性赋值。所以你在构造函数中调用Start()时,Subject还没被赋值为XAML中设置的Bar,仍然是默认值Foo。你用Task.Delay(1)的本质是把后续逻辑投递到UI线程消息队列末尾,等待XAML属性赋值流程完成后再执行,属于不可靠的取巧方案。
可行解决方案
方案1:重写OnInitialized方法(最适配当前场景)
OnInitialized方法会在所有XAML属性赋值完成后触发,此时所有传入的属性值都已经生效,直接在这里执行初始化逻辑即可:
public partial class TestControl : UserControl { public string Subject { get; set; } = "Foo"; public TestControl() { InitializeComponent(); DataContext = this; // 不要在构造函数中调用Start } // 重写OnInitialized方法 protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); // 此时Subject已经是XAML传入的Bar,直接执行初始化 Start(); } public async Task Start() { Debug.WriteLine("Subject is: " + Subject); await Task.Delay(1); Debug.WriteLine("Subject is: " + Subject); } }
如果你的初始化逻辑需要依赖控件的渲染属性(如ActualWidth/ActualHeight),可以换成监听Loaded事件,Loaded会在控件完全渲染到视觉树后触发:
public TestControl() { InitializeComponent(); DataContext = this; Loaded += (sender, args) => Start(); }
方案2:将属性改为依赖属性,监听属性变更
如果你的属性值可能在运行时动态修改,需要每次变更后都执行对应的逻辑,可以把普通CLR属性改为依赖属性,在属性变更回调中处理初始化:
public partial class TestControl : UserControl { public static readonly DependencyProperty SubjectProperty = DependencyProperty.Register( nameof(Subject), typeof(string), typeof(TestControl), new PropertyMetadata("Foo", OnSubjectChanged)); public string Subject { get => (string)GetValue(SubjectProperty); set => SetValue(SubjectProperty, value); } private static void OnSubjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = (TestControl)d; // 每次Subject变更都会触发,可在这里执行逻辑 control.Start(); // 如果只需初始化一次,可以加个布尔标志位判断是否已经执行过 } public TestControl() { InitializeComponent(); DataContext = this; } public async Task Start() { Debug.WriteLine("Subject is: " + Subject); await Task.Delay(1); Debug.WriteLine("Subject is: " + Subject); } }
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

