From 45b5ec5d8f438850babdaf4cc453658a5d6d136d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Mar 2016 21:50:59 +0100 Subject: [PATCH] Added Control.Initialized. --- src/Perspex.Controls/Control.cs | 54 ++++++++- src/Perspex.Controls/IControl.cs | 14 +-- src/Perspex.SceneGraph/Visual.cs | 108 +++++++++--------- .../ControlTests.cs | 28 +++++ 4 files changed, 139 insertions(+), 65 deletions(-) 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;