Explorar o código

Don't use ResourcesChanged to propagate.

Instead of each control listening to its parent `ResourcesChanged` event, use the logical tree to propagate `ResourcesChanged` events:

- When attaching to the logical tree, piggyback on the `AttachedToLogicalTree` traversal
- At other times, propagate by calling `NotifyResourcesChanged`
Steven Kirk %!s(int64=5) %!d(string=hai) anos
pai
achega
eec2a05a26

+ 32 - 31
src/Avalonia.Styling/StyledElement.cs

@@ -205,19 +205,7 @@ namespace Avalonia
         /// each styled element may in addition define its own styles which are applied to the styled element
         /// itself and its children.
         /// </remarks>
-        public Styles Styles
-        {
-            get
-            {
-                if (_styles is null)
-                {
-                    _styles = new Styles(this);
-                    NotifyResourcesChanged(new ResourcesChangedEventArgs());
-                }
-
-                return _styles;
-            }
-        }
+        public Styles Styles => _styles ??= new Styles(this);
 
         /// <summary>
         /// Gets or sets the styled element's resource dictionary.
@@ -423,18 +411,6 @@ namespace Avalonia
                     OnDetachedFromLogicalTreeCore(e);
                 }
 
-                if (old != null)
-                {
-                    old.ResourcesChanged -= ThisResourcesChanged;
-                }
-
-                if (Parent != null)
-                {
-                    Parent.ResourcesChanged += ThisResourcesChanged;
-                }
-                
-                NotifyResourcesChanged(new ResourcesChangedEventArgs());
-
                 var newRoot = FindLogicalRoot(this);
 
                 if (newRoot is object)
@@ -442,6 +418,14 @@ namespace Avalonia
                     var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent);
                     OnAttachedToLogicalTreeCore(e);
                 }
+                else
+                {
+                    // If we were attached to the logical tree, we piggyback on the tree traversal
+                    // there to raise resources changed notifications. If we're being removed from
+                    // the logical tree or attached to a control which is not rooted, then traverse
+                    // the tree raising notifications now.
+                    NotifyResourcesChanged();
+                }
 
 #nullable disable
                 RaisePropertyChanged(
@@ -638,6 +622,7 @@ namespace Avalonia
                 _logicalRoot = e.Root;
 
                 ApplyStyling();
+                NotifyResourcesChanged(propagate: false);
 
                 OnAttachedToLogicalTree(e);
                 AttachedToLogicalTree?.Invoke(this, e);
@@ -786,14 +771,30 @@ namespace Avalonia
             }
         }
 
-        private void NotifyResourcesChanged(ResourcesChangedEventArgs? e = null)
+        private void NotifyResourcesChanged(
+            ResourcesChangedEventArgs? e = null,
+            bool propagate = true)
         {
-            ResourcesChanged?.Invoke(this, e ?? new ResourcesChangedEventArgs());
-        }
+            if (ResourcesChanged is object)
+            {
+                e ??= new ResourcesChangedEventArgs();
+                ResourcesChanged(this, e);
+            }
 
-        private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
-        {
-            NotifyResourcesChanged(e);
+            if (propagate && _logicalChildren is object)
+            {
+                var count = _logicalChildren.Count;
+
+                if (count > 0)
+                {
+                    e ??= new ResourcesChangedEventArgs();
+
+                    for (var i = 0; i < count; ++i)
+                    {
+                        _logicalChildren[i].NotifyResourcesChanged(e);
+                    }
+                }
+            }
         }
 
         private static IReadOnlyList<IStyle> RecurseStyles(IReadOnlyList<IStyle> styles)

+ 24 - 19
tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs

@@ -154,21 +154,42 @@ namespace Avalonia.Controls.UnitTests
             };
 
             var raisedOnTarget = false;
-            var raisedOnPresenter = false;
             var raisedOnChild = false;
 
             target.Measure(Size.Infinity);
             target.ResourcesChanged += (_, __) => raisedOnTarget = true;
-            target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
             child.ResourcesChanged += (_, __) => raisedOnChild = true;
 
             target.Resources.Add("foo", "bar");
 
             Assert.True(raisedOnTarget);
-            Assert.True(raisedOnPresenter);
             Assert.True(raisedOnChild);
         }
 
+        [Fact]
+        public void Adding_Resource_Should_Call_Not_Raise_ResourceChanged_On_Template_Children()
+        {
+            Border child;
+
+            var target = new ContentControl
+            {
+                Content = child = new Border(),
+                Template = ContentControlTemplate(),
+            };
+
+            var raisedOnTarget = false;
+            var raisedOnPresenter = false;
+
+            target.Measure(Size.Infinity);
+            target.ResourcesChanged += (_, __) => raisedOnTarget = true;
+            target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
+
+            target.Resources.Add("foo", "bar");
+
+            Assert.True(raisedOnTarget);
+            Assert.False(raisedOnPresenter);
+        }
+
         [Fact]
         public void Adding_Resource_To_Styles_Should_Raise_ResourceChanged()
         {
@@ -201,22 +222,6 @@ namespace Avalonia.Controls.UnitTests
             Assert.True(raised);
         }
 
-        [Fact]
-        public void Setting_Logical_Parent_Raises_Child_ResourcesChanged()
-        {
-            var parent = new ContentControl();
-            var child = new StyledElement();
-
-            ((ISetLogicalParent)child).SetParent(parent);
-            var raisedOnChild = false;
-
-            child.ResourcesChanged += (_, __) => raisedOnChild = true;
-
-            parent.Resources.Add("foo", "bar");
-
-            Assert.True(raisedOnChild);
-        }
-
         private IControlTemplate ContentControlTemplate()
         {
             return new FuncControlTemplate<ContentControl>((x, scope) =>