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

How can you access XAML-set properties of a UserControl during initialization?(如何在初始化期间访问 UserControl 的 XAML 集属性?)
本文介绍了如何在初始化期间访问 UserControl 的 XAML 集属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在编写一个自定义的 UserControl(与无外观的控件相反),我们需要根据我们的消费者在 XAML 中为我们的控件设置的属性执行一些初始化.

We're writing a custom UserControl (as opposed to a lookless control) and we need to perform some initialization based on what properties our consumers set on our control in XAML.

现在,在大多数情况下,您将使用 Initialized 事件(或 OnInitialized 覆盖),因为在触发时,所有 XAML 设置的属性都已应用,但对于 UserControl,则不是案子.当 Initialized 事件触发时,所有属性仍为其默认值.

Now in most cases, you'd use the Initialized event (or the OnInitialized override) since by the time that fires, all the XAML-set properties have been applied, but in the case of a UserControl, that isn't the case. When the Initialized event fires, all properties are still at their default values.

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

I didn't notice this for other controls, just UserControls, which are different in that they call InitializeComponent() in their constructor, so as a test, I commented that line out and ran the code and sure enough, this time during the Initialized event, the properties were set.

这里有一些代码和测试结果证明了这一点......

Here is some code and test results demonstrating this...

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

Result with InitializeComponent called in the constructor:
(Note: The values still have not been set)

TestValue (Pre-OnInitialized): Original Value
TestValue (Initialized Event): Original Value
TestValue (Post-OnInitialized): Original Value

InitializeComponent 的结果完全被注释掉了:
(注意:虽然设置了值,但控件并未加载,因为它需要 InitializeComponent)

Result with InitializeComponent completely commented out:
(Note: While the values have been set, the control isn't loaded as it needs 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 中的用户设置属性来初始化我的控件?(注意:加载为时已晚,因为那时控件应该已经初始化了.)

All this said, what can I use to initialize my control based on user-set properties in the XAML? (Note: Loaded is too late as the control should already have been initialized by then.)

XAML 代码段

<local:TestControl TestValue="New Value!" />

TestControl.cs

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

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

推荐答案

Awesomesausage!我想通了!

Awesomesausage! I figured it out!

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

Normally when you receive the Initialized event (or are inside the OnInitialized override) you have access to XAML-set property values. However, UserControl classes work a little differently as they depend on InitializeComponent being called to hydrate the UI and set the related member variables, etc.

问题是调用在构造函数中,它最终调用 OnInitialized (并因此引发 Initialized 事件),但这发生在 XAML 之前 -set 属性已被应用,这意味着您还无法访问它们,这是我需要的.

The problem is that call is in the constructor, which in turn ends up calling OnInitialized (and thus raising the Initialized event) but that happens way before the XAML-set properties have been applied, meaning you don’t have access to them yet, which I needed.

有人可能认为 Loaded 事件很有用——根据这些属性完成初始化——但如果你在那里执行额外的初始化,你正在创造一个潜在的竞争条件与您的消费者一起,如果他们订阅您的 Loaded 事件并在您之前获取它,那么在他们的处理程序中尝试访问您的控件,他们将访问未初始化的控件.

One may think that's a good use for the Loaded event--to finish initialization based on those properties--but if you're performing additional initialization there, you're creating a potential race condition with your consumers in that if they subscribe to your Loaded event and get it before you, then in their handler try to access your control, they will be accessing an uninitialized control.

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

Then something occurred to me... As I showed above, if you remove the InitializeComponent call from the constructor, the Initialized event now works as you would expect, but of course your UI isn't hydrated yet since you haven't yet called InitializeComponent.

如果您将该调用移到 OnInitialized 覆盖的开头,在调用 base.OnInitialized 之前,因此会发生在 Initialized 之前 事件已引发?

So what would happen if you moved that call to the beginning of the OnInitialized override, before the call to base.OnInitialized, and thus before the Initialized event was raised?

是的!那行得通!:)

这样,您不仅拥有 XAML 设置的属性,而且还可以在任何人获得 Initialized 事件之前完全加载 UI(更不用说 Loaded 事件),这就是 Initialized 事件应该被使用的方式.

This way not only do you have the XAML-set properties, but you’d also have the UI fully loaded before anyone gets the Initialized event (let alone the Loaded event), which is how the Initialized event is supposed to be used.

下面是修改后的代码...

Below is the revised code...

public partial class TestControl : UserControl {

    protected override void OnInitialized(EventArgs e) {
        InitializeComponent();
        base.OnInitialized(e);
    }

    public static readonly DependencyProperty TestValueProperty = DependencyProperty.Register(
        nameof(TestValue),
        typeof(string),
        typeof(TestControl),
        new UIPropertyMetadata("Original Value"));

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

  • 注意:您不再需要构造函数,除非您有特殊需要在那里做其他事情.如果你这样做了,请记住在调用 InitializeComponent 之前你不能按名称访问组成控件,但这只是意味着你必须计划在 InitializeComponent<之间移动这种基于名称的初始化/code> 以及对 base.OnInitialize 的调用,一切都会正常工作.
    • Note: You don't need the constructor anymore unless you have a specific need to do other things there. And if you do, just remember you can't access constituent controls by name until after the InitializeComponent call, but that just means you have to plan to move such name-based initialization between InitializeComponent and that call to base.OnInitialize and things will work just fine.
    • 这篇关于如何在初始化期间访问 UserControl 的 XAML 集属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Can I initialize a BCL immutable collection using braces?(我可以使用大括号初始化BCL不可变集合吗?)
The type initializer for #39;System.Data.Entity.Internal.AppConfig#39; threw an exception on a Sub Website(“System.Data.Entity.Internal.AppConfig的类型初始化程序在子网站上引发异常)
Possible to initialize multiple variables from a tuple?(可以从一个元组初始化多个变量吗?)
error loading database initializer with EF6(使用 EF6 加载数据库初始化程序时出错)
How to force fire OnModelCreating every DataContext initialized up(如何强制触发 OnModelCreating 每个初始化的 DataContext)
The type appear in two structurally incompatible initializations within a single LINQ to Entities query(该类型出现在单个 LINQ to Entities 查询中的两个结构不兼容的初始化中)