[Copying my answer from my other question here.]
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. 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.
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.
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.
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
.
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?
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.
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 betweenInitializeComponent
and that call tobase.OnInitialize
and things will work just fine.