diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs
index 1841c27c76..78368ba11b 100644
--- a/src/Perspex.Controls/Control.cs
+++ b/src/Perspex.Controls/Control.cs
@@ -137,6 +137,16 @@ namespace Perspex.Controls
///
public event EventHandler DataContextChanged;
+ ///
+ /// Occurs when the control has finished initialization.
+ ///
+ ///
+ /// The Initialized event indicates that all property values on the control have been set.
+ /// It occurs when is called when loading the
+ /// control from markup, or when the control is attached to the visual tree otherwise.
+ ///
+ public event EventHandler Initialized;
+
///
/// Gets or sets the name of the control.
///
@@ -233,6 +243,15 @@ namespace Perspex.Controls
set { _dataTemplates = value; }
}
+ ///
+ /// Gets a value that indicates whether the element has finished initialization.
+ ///
+ ///
+ /// For more information about when IsInitialized is set, see the
+ /// event.
+ ///
+ public bool IsInitialized { get; private set; }
+
///
/// Gets or sets the styles for the control.
///
@@ -334,11 +353,20 @@ namespace Perspex.Controls
throw new InvalidOperationException("BeginInit was not called.");
}
- if (--_initCount == 0 && _isAttachedToLogicalTree && !_styled)
+ if (--_initCount == 0)
{
- RegisterWithNameScope();
- ApplyStyling();
- _styled = true;
+ if (_isAttachedToLogicalTree && !_styled)
+ {
+ RegisterWithNameScope();
+ ApplyStyling();
+ _styled = true;
+ }
+
+ if (!IsInitialized)
+ {
+ IsInitialized = true;
+ Initialized?.Invoke(this, EventArgs.Empty);
+ }
}
}
@@ -499,6 +527,24 @@ namespace Perspex.Controls
DetachedFromLogicalTree?.Invoke(this, e);
}
+ ///
+ protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTreeCore(e);
+
+ if (!IsInitialized)
+ {
+ IsInitialized = true;
+ Initialized?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTreeCore(e);
+ }
+
///
protected override void OnGotFocus(GotFocusEventArgs e)
{
diff --git a/src/Perspex.Controls/IControl.cs b/src/Perspex.Controls/IControl.cs
index 32f23b3e8f..c0e34f9fa4 100644
--- a/src/Perspex.Controls/IControl.cs
+++ b/src/Perspex.Controls/IControl.cs
@@ -17,14 +17,9 @@ namespace Perspex.Controls
public interface IControl : IVisual, ILogical, ILayoutable, IInputElement, INamed, IStyleable, IStyleHost
{
///
- /// Raised when the control is attached to a rooted logical tree.
+ /// Occurs when the control has finished initialization.
///
- event EventHandler AttachedToLogicalTree;
-
- ///
- /// Raised when the control is detached from a rooted logical tree.
- ///
- event EventHandler DetachedFromLogicalTree;
+ event EventHandler Initialized;
///
/// Gets or sets the control's styling classes.
@@ -41,6 +36,11 @@ namespace Perspex.Controls
///
DataTemplates DataTemplates { get; }
+ ///
+ /// Gets a value that indicates whether the element has finished initialization.
+ ///
+ bool IsInitialized { get; }
+
///
/// Gets the control's logical parent.
///
diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs
index 160212355f..3dfe3412c8 100644
--- a/src/Perspex.SceneGraph/Visual.cs
+++ b/src/Perspex.SceneGraph/Visual.cs
@@ -281,6 +281,60 @@ namespace Perspex
}
}
+ ///
+ /// Calls the method
+ /// for this control and all of its visual descendents.
+ ///
+ /// The event args.
+ protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
+ {
+ Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
+
+ VisualRoot = e.Root;
+
+ if (RenderTransform != null)
+ {
+ RenderTransform.Changed += RenderTransformChanged;
+ }
+
+ OnAttachedToVisualTree(e);
+
+ if (VisualChildren != null)
+ {
+ foreach (Visual child in VisualChildren.OfType())
+ {
+ child.OnAttachedToVisualTreeCore(e);
+ }
+ }
+ }
+
+ ///
+ /// Calls the method
+ /// for this control and all of its visual descendents.
+ ///
+ /// The event args.
+ protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
+ {
+ Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
+
+ VisualRoot = null;
+
+ if (RenderTransform != null)
+ {
+ RenderTransform.Changed -= RenderTransformChanged;
+ }
+
+ OnDetachedFromVisualTree(e);
+
+ if (VisualChildren != null)
+ {
+ foreach (Visual child in VisualChildren.OfType())
+ {
+ child.OnDetachedFromVisualTreeCore(e);
+ }
+ }
+ }
+
///
/// Called when the control is added to a visual tree.
///
@@ -438,59 +492,5 @@ namespace Perspex
break;
}
}
-
- ///
- /// Calls the method
- /// for this control and all of its visual descendents.
- ///
- /// The event args.
- private void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
- {
- Logger.Verbose(LogArea.Visual, this, "Attached to visual tree");
-
- VisualRoot = e.Root;
-
- if (RenderTransform != null)
- {
- RenderTransform.Changed += RenderTransformChanged;
- }
-
- OnAttachedToVisualTree(e);
-
- if (VisualChildren != null)
- {
- foreach (Visual child in VisualChildren.OfType())
- {
- child.OnAttachedToVisualTreeCore(e);
- }
- }
- }
-
- ///
- /// Calls the method
- /// for this control and all of its visual descendents.
- ///
- /// The event args.
- private void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
- {
- Logger.Verbose(LogArea.Visual, this, "Detached from visual tree");
-
- VisualRoot = null;
-
- if (RenderTransform != null)
- {
- RenderTransform.Changed -= RenderTransformChanged;
- }
-
- OnDetachedFromVisualTree(e);
-
- if (VisualChildren != null)
- {
- foreach (Visual child in VisualChildren.OfType())
- {
- child.OnDetachedFromVisualTreeCore(e);
- }
- }
- }
}
}
diff --git a/tests/Perspex.Controls.UnitTests/ControlTests.cs b/tests/Perspex.Controls.UnitTests/ControlTests.cs
index 2d09da00d5..ed78cc4afc 100644
--- a/tests/Perspex.Controls.UnitTests/ControlTests.cs
+++ b/tests/Perspex.Controls.UnitTests/ControlTests.cs
@@ -230,6 +230,34 @@ namespace Perspex.Controls.UnitTests
}
}
+ [Fact]
+ public void EndInit_Should_Raise_Initialized()
+ {
+ var target = new Border();
+ var called = false;
+
+ target.Initialized += (s, e) => called = true;
+ ((ISupportInitialize)target).BeginInit();
+ ((ISupportInitialize)target).EndInit();
+
+ Assert.True(called);
+ Assert.True(target.IsInitialized);
+ }
+
+ [Fact]
+ public void Attaching_To_Visual_Tree_Should_Raise_Initialized()
+ {
+ var root = new TestRoot();
+ var target = new Border();
+ var called = false;
+
+ target.Initialized += (s, e) => called = true;
+ root.Child = target;
+
+ Assert.True(called);
+ Assert.True(target.IsInitialized);
+ }
+
private class TestControl : Control
{
public new PerspexObject InheritanceParent => base.InheritanceParent;