简体   繁体   English

特别是对于用户控件,是否在构造函数之后有一个位置,但在Loaded事件之前,您可以访问在XAML中设置的属性?

[英]Specifically for a User Control, is there a place after the constructor, but before the Loaded event where you can access properties set in XAML?

We have an issue where our user control is initialized based on what the user sets in XAML when utilizing our control. 我们遇到一个问题,我们的用户控件是根据用户在使用我们的控件时在XAML中设置的内容进行初始化的。 Currently we were using the 'Loaded' event to act on what the user had set or not. 目前我们使用'Loaded'事件来对用户设置或未设置的内容采取行动。

However, the issue with using the Loaded event is another sibling of this control is using their loaded event to set something on ours, which isn't yet fully initialized since our Loaded event hasn't yet fired. 但是,使用Loaded事件的另一个问题是这个控件的另一个兄弟是使用它们加载的事件来设置我们的东西,因为我们的 Loaded事件尚未触发,所以尚未完全初始化。 (It's a UI race condition if you will.) (如果你愿意的话,这是一个UI竞争条件。)

Again, we can't move our code to the constructor as the WPF system hasn't yet set the properties specified by the XAML of the consumer of our control. 同样,我们无法将代码移动到构造函数,因为WPF系统尚未设置我们控件的使用者的XAML指定的属性。 We can't use the Loaded event for the reasons stated above. 出于上述原因,我们无法使用Loaded事件。 Initialized doesn't seem to work either. 初始化似乎也不起作用。

I've also looked into ISupportsInitialize, but that's where we would be batch-setting the control's properties, not something externally, so that doesn't seem to be a fit either. 我也研究了ISupportsInitialize,但这就是我们批量设置控件属性的地方,而不是外部的东西,所以这似乎也不合适。

Thoughts? 思考?

Update 更新

I've since found out this is an anomaly specifically with UserControls. 我发现这是一个异常的UserControls异常。 They handle initialization differently. 它们以不同的方式处理 You can find more details in my follow-up question here... 您可以在我的后续问题中找到更多详细信息......

...but the short version is calling InitializeComponent in the constructor actually raises the Initialized event, but does so before the XAML-defined properties have actually been set. ...但是短版本在构造函数中调用InitializeComponent实际上引发了Initialized事件,但是在实际设置了XAML定义的属性之前这样做了。 Comment it out and the properties are now set when Initialized fires, but of course your control's UI isn't loaded! 注释掉它,现在在Initialized触发时设置属性,但当然你的控件的UI没有被加载! Kinda frustrating actually. 实际上有点令人沮丧。

Still looking for a solution. 仍在寻找解决方案。 Code examples and more details can be found there. 可以在那里找到代码示例和更多详细信息。

[Copying my answer from my other question here .] [ 在这里从我的其他问题复制我的答案。]

Awesomesausage! Awesomesausage! I figured it out! 我想到了!

Normally when you receive the Initialized event (or are inside the OnInitialized override) you have access to XAML-set property values. 通常,当您收到Initialized事件(或在OnInitialized覆盖内)时,您可以访问XAML设置的属性值。 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. 但是, UserControl类的工作方式略有不同,因为它们依赖于调用InitializeComponent来水合UI并设置相关的成员变量等。

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

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

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 . 然后我发生了一些事情......正如我在上面所示,如果你从构造函数中删除了InitializeComponent调用,那么Initialized事件现在可以正常工作了,但是当然你的UI还没有被水合,因为你还没有调用InitializeComponent

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? 那么如果在调用base.OnInitialized之前将调用移动到OnInitialized覆盖的开头,从而在Initialized事件被引发之前,会发生什么?

Yep! 是的! That worked! 那很有效! :) :)

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. 这样你不仅拥有XAML集属性,而且在任何人获得Initialized事件(更不用说Loaded事件)之前你也会完全加载UI,这就是应该使用Initialized事件的方式。

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

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

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

There is the Window.Initialized event which comes after the constructor is called and before the Window.Loaded event. Window.Initialized事件是在调用构造函数之后和Window.Loaded事件之前发生的。 Properties will be set by then, but DynamicResource and Binding values won't. 然后将设置属性,但DynamicResourceBinding值不会。 From the Object Lifetime Events page on the MSDN website: 从MSDN网站上的对象生命周期事件页面:

Initialized is raised first, and roughly corresponds to the initialization of the object by the call to its constructor. 首先引发Initialized,并通过调用其构造函数粗略地对应于对象的初始化。 Because the event happens in response to initialization, you are guaranteed that all properties of the object are set. 因为事件是在响应初始化时发生的,所以可以保证设置了对象的所有属性。 (An exception is expression usages such as dynamic resources or binding; these will be unevaluated expressions.) As a consequence of the requirement that all properties are set, the sequence of Initialized being raised by nested elements that are defined in markup appears to occur in order of deepest elements in the element tree first, then parent elements toward the root. (一个例外是表达用法,例如动态资源或绑定;这些将是未评估的表达式。)由于要求设置所有属性,标记中定义的嵌套元素引发的Initialized序列似乎发生在首先是元素树中最深元素的顺序,然后是父元素朝向根。 This order is because the parent-child relationships and containment are properties, and therefore the parent cannot report initialization until the child elements that fill the property are also completely initialized. 此顺序是因为父子关系和包含是属性,因此父级无法报告初始化,直到填充属性的子元素也已完全初始化。

When you are writing handlers in response to the Initialized event, you must consider that there is no guarantee that all other elements in the element tree (either logical tree or visual tree) around where the handler is attached have been created, particularly parent elements. 在编写处理程序以响应Initialized事件时,必须考虑到无法保证已创建元素树(逻辑树或可视树)中所有其他元素(包括处理程序附加的位置),尤其是父元素。 Member variables may be null, or data sources might not yet be populated by the underlying binding (even at the expression level). 成员变量可能为null,或者数据源可能尚未由底层绑定填充(即使在表达式级别)。

How coincidental that an 8 month old post is answered 2 days ago with the exact problem I am currently having (but with WinRT XAML, not WPF). 一个8个月大的帖子在2天前得到了解答我遇到的确切问题(但是使用的是WinRT XAML,而不是WPF)。

Ran into this same issue with a UserControl that contains an Image control. 使用包含Image控件的UserControl进入同样的问题。 UserControl has a custom integer dependency property whose change handler sets the Image.Source to a new BitmapImage (the integer value determine which image to show). UserControl具有自定义整数依赖项属性,其更改处理程序将Image.Source设置为新的BitmapImage(整数值确定要显示的图像)。

Had a race condition where the image was not always being displayed properly. 有一个竞争条件,图像并不总是正确显示。 Very intermittent and unfortunately I published a Win 8.1 and WinPhone 8.1 app with the bug. 很间歇,不幸的是我发布了一个带有bug的Win 8.1和WinPhone 8.1应用程序。 Ugh. 啊。

Thank you for your contributions MarqueIV and Sheridanless. 感谢您对MarqueIV和Sheridanless的贡献。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM