wpf 特别是对于用户控件,在构造函数之后但在 Loaded 事件之前是否有可以访问 XAML 中设置的属性的地方?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18580352/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
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?
提问by Mark A. Donohoe
We have an issue where our user control is initialized based on what the user sets in XAML when utilizing our control. Currently we were using the 'Loaded' event to act on what the user had set or not.
我们有一个问题,我们的用户控件是根据用户在使用我们的控件时在 XAML 中设置的内容来初始化的。目前我们正在使用 'Loaded' 事件来处理用户设置或未设置的内容。
However, the issue with using the Loaded event is another sibling of this control is using theirloaded event to set something on ours, which isn't yet fully initialized since ourLoaded event hasn't yet fired. (It's a UI race condition if you will.)
然而,使用 Loaded 事件的问题是这个控件的另一个兄弟是使用它们的加载事件在我们的上设置一些东西,由于我们的Loaded 事件尚未触发,因此尚未完全初始化。(如果你愿意,这是一个 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. We can't use the Loaded event for the reasons stated above. Initialized doesn't seem to work either.
同样,我们无法将代码移至构造函数,因为 WPF 系统尚未设置控件使用者的 XAML 指定的属性。由于上述原因,我们不能使用 Loaded 事件。初始化似乎也不起作用。
I've also looked into ISupportsInitialize, but that's where wewould 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. They handle initialization differently. You can find more details in my follow-up question here...
我后来发现这是 UserControls 的一个异常现象。它们以不同的方式处理初始化。您可以在我的后续问题中找到更多详细信息...
...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. Comment it out and the properties are now set when Initialized fires, but of course your control's UI isn't loaded! Kinda frustrating actually.
...但简短版本在构造函数中调用 InitializeComponent 实际上引发了 Initialized 事件,但在实际设置 XAML 定义的属性之前这样做。注释掉它,属性现在在 Initialized 触发时设置,但当然你的控件的 UI 没有加载!实际上有点令人沮丧。
Still looking for a solution. Code examples and more details can be found there.
仍在寻找解决方案。可以在那里找到代码示例和更多详细信息。
采纳答案by Mark A. Donohoe
[Copying my answer from my other question here.]
[从这里复制我的另一个问题的答案。]
Awesomesausage! I figured it out!
棒棒的香肠!我想到了!
Normally when you receive the Initializedevent (or are inside the OnInitializedoverride) you have access to XAML-set property values. However, UserControlclasses work a little differently as they depend on InitializeComponentbeing called to hydrate the UI and set the related member variables, etc.
通常,当您收到Initialized事件(或在OnInitialized覆盖范围内)时,您可以访问 XAML 设置的属性值。但是,UserControl类的工作方式略有不同,因为它们依赖于InitializeComponent被调用来对 UI 进行水合并设置相关的成员变量等。
The problem is that call is in the constructor, which in turn ends up calling OnInitialized(and thus raising the Initializedevent) 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 Loadedevent--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 Loadedevent 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 InitializeComponentcall from the constructor, the Initializedevent 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 OnInitializedoverride, before the call to base.OnInitialized, and thus before the Initializedevent was raised?
那么,如果您将该调用移到OnInitialized覆盖的开头,在调用 之前base.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 Initializedevent (let alone the Loadedevent), which is how the Initializedevent 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
InitializeComponentcall, but that just means you have to plan to move such name-based initialization betweenInitializeComponentand that call tobase.OnInitializeand things will work just fine.
- 注意:除非您特别需要在那里做其他事情,否则您不再需要构造函数。如果你这样做了,请记住,在
InitializeComponent调用之后你不能按名称访问组成控件,但这只是意味着你必须计划在InitializeComponent调用base.OnInitialize和调用之间移动这种基于名称的初始化,并且事情会正常工作。
回答by Art Dumas
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).
2 天前回答了一个 8 个月大的帖子,我目前遇到的确切问题是多么巧合(但使用 WinRT XAML,而不是 WPF)。
Ran into this same issue with a UserControl that contains an Image control. 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).
使用包含 Image 控件的 UserControl 遇到了同样的问题。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. Ugh.
有一个竞争条件,其中图像并不总是正确显示。非常断断续续,不幸的是我发布了一个带有错误的 Win 8.1 和 WinPhone 8.1 应用程序。啊。
Thank you for your contributions MarqueIV and Sheridanless.
感谢您的贡献 MarqueIV 和 Sheridanless。
回答by Sheridan
There is the Window.Initializedevent which comes after the constructor is called and before the Window.Loadedevent. Properties will be set by then, but DynamicResourceand Bindingvalues won't. From the Object Lifetime Eventspage on the MSDN website:
有一个Window.Initialized事件发生在调用构造函数之后和Window.Loaded事件之前。属性将被然后进行设置,但DynamicResource和Binding值不会。从MSDN 网站上的对象生命周期事件页面:
Initialized is raised first, and roughly corresponds to the initialization of the object by the call to its constructor. 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. 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. Member variables may be null, or data sources might not yet be populated by the underlying binding (even at the expression level).
Initialized 首先引发,大致对应于通过调用其构造函数来初始化对象。因为该事件是在响应初始化时发生的,所以可以保证对象的所有属性都已设置。(一个例外是表达式用法,例如动态资源或绑定;这些将是未计算的表达式。)作为要求设置所有属性的结果,由标记中定义的嵌套元素引发的 Initialized 序列似乎发生在首先是元素树中最深元素的顺序,然后是父元素到根。这个顺序是因为父子关系和包含是属性,所以在填充属性的子元素也完全初始化之前,父级不能报告初始化。
当您编写处理程序以响应 Initialized 事件时,您必须考虑到不能保证在附加处理程序的位置周围的元素树(逻辑树或可视树)中的所有其他元素都已创建,尤其是父元素。成员变量可能为空,或者数据源可能尚未由底层绑定填充(即使在表达式级别)。

