From ad02cc7d9d2c669e814251d7b62d75ce153acc70 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 26 Mar 2016 21:30:01 +0100 Subject: [PATCH] Ensure logical tree (de)attachment doesn't break... ...if a derived class fails to call base.OnAttachedToLogicalTree etc. --- src/Perspex.Controls/Control.cs | 132 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index 8d612abcf0..1841c27c76 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -317,7 +317,7 @@ namespace Perspex.Controls /// void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { - this.OnDetachedFromLogicalTree(e); + this.OnDetachedFromLogicalTreeCore(e); } /// @@ -403,7 +403,7 @@ namespace Perspex.Controls } var e = new LogicalTreeAttachmentEventArgs(oldRoot); - OnDetachedFromLogicalTree(e); + OnDetachedFromLogicalTreeCore(e); } InheritanceParent = parent as PerspexObject; @@ -419,7 +419,7 @@ namespace Perspex.Controls } var e = new LogicalTreeAttachmentEventArgs(newRoot); - OnAttachedToLogicalTree(e); + OnAttachedToLogicalTreeCore(e); } RaisePropertyChanged(ParentProperty, old, _parent, BindingPriority.LocalValue); @@ -482,79 +482,21 @@ namespace Perspex.Controls } /// - /// Called when the control is added to a logical tree. + /// Called when the control is added to a rooted logical tree. /// /// The event args. - /// - /// It is vital that if you override this method you call the base implementation; - /// failing to do so will cause numerous features to not work as expected. - /// protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { - // This method can be called when a control is already attached to the logical tree - // in the following scenario: - // - ListBox gets assigned Items containing ListBoxItem - // - ListBox makes ListBoxItem a logical child - // - ListBox template gets applied; making its Panel get attached to logical tree - // - That AttachedToLogicalTree signal travels down to the ListBoxItem - if (!_isAttachedToLogicalTree) - { - _isAttachedToLogicalTree = true; - - if (_initCount == 0) - { - RegisterWithNameScope(); - ApplyStyling(); - _styled = true; - } - - AttachedToLogicalTree?.Invoke(this, e); - } - - foreach (var child in LogicalChildren.OfType()) - { - child.OnAttachedToLogicalTree(e); - } + AttachedToLogicalTree?.Invoke(this, e); } /// - /// Called when the control is removed from a logical tree. + /// Called when the control is removed from a rooted logical tree. /// /// The event args. - /// - /// It is vital that if you override this method you call the base implementation; - /// failing to do so will cause numerous features to not work as expected. - /// protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { - if (_isAttachedToLogicalTree) - { - if (Name != null) - { - _nameScope?.Unregister(Name); - } - - _isAttachedToLogicalTree = false; - _styleDetach.OnNext(this); - this.TemplatedParent = null; - DetachedFromLogicalTree?.Invoke(this, e); - - foreach (var child in LogicalChildren.OfType()) - { - child.OnDetachedFromLogicalTree(e); - } - -#if DEBUG - if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0) - { - Logger.Warning( - LogArea.Control, - this, - "{Type} detached from logical tree but still has class listeners", - this.GetType()); - } -#endif - } + DetachedFromLogicalTree?.Invoke(this, e); } /// @@ -679,6 +621,66 @@ namespace Perspex.Controls } } + private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e) + { + // This method can be called when a control is already attached to the logical tree + // in the following scenario: + // - ListBox gets assigned Items containing ListBoxItem + // - ListBox makes ListBoxItem a logical child + // - ListBox template gets applied; making its Panel get attached to logical tree + // - That AttachedToLogicalTree signal travels down to the ListBoxItem + if (!_isAttachedToLogicalTree) + { + _isAttachedToLogicalTree = true; + + if (_initCount == 0) + { + RegisterWithNameScope(); + ApplyStyling(); + _styled = true; + } + + OnAttachedToLogicalTree(e); + } + + foreach (var child in LogicalChildren.OfType()) + { + child.OnAttachedToLogicalTreeCore(e); + } + } + + private void OnDetachedFromLogicalTreeCore(LogicalTreeAttachmentEventArgs e) + { + if (_isAttachedToLogicalTree) + { + if (Name != null) + { + _nameScope?.Unregister(Name); + } + + _isAttachedToLogicalTree = false; + _styleDetach.OnNext(this); + this.TemplatedParent = null; + OnDetachedFromLogicalTree(e); + + foreach (var child in LogicalChildren.OfType()) + { + child.OnDetachedFromLogicalTreeCore(e); + } + +#if DEBUG + if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0) + { + Logger.Warning( + LogArea.Control, + this, + "{Type} detached from logical tree but still has class listeners", + this.GetType()); + } +#endif + } + } + private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action)