Browse Source

Started adding MergedDictionaries.

Steven Kirk 8 years ago
parent
commit
ac8cc99f16

+ 4 - 9
src/Avalonia.Controls/Control.cs

@@ -284,7 +284,7 @@ namespace Avalonia.Controls
                     if (_styles != null)
                     {
                         (_styles as ISetStyleParent)?.SetParent(null);
-                        _styles.ResourcesChanged -= StyleResourcesChanged;
+                        _styles.ResourcesChanged -= ThisResourcesChanged;
                     }
 
                     _styles = value;
@@ -294,7 +294,7 @@ namespace Avalonia.Controls
                         setParent.SetParent(this);
                     } 
 
-                    _styles.ResourcesChanged += StyleResourcesChanged;
+                    _styles.ResourcesChanged += ThisResourcesChanged;
                 }
             }
         }
@@ -323,7 +323,7 @@ namespace Avalonia.Controls
                 if (_resources == null)
                 {
                     _resources = new ResourceDictionary();
-                    _resources.CollectionChanged += ResourceDictionaryChanged;
+                    _resources.ResourcesChanged += ThisResourcesChanged;
                 }
 
                 return _resources;
@@ -914,12 +914,7 @@ namespace Avalonia.Controls
             }
         }
 
-        private void ResourceDictionaryChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
-        }
-
-        private void StyleResourcesChanged(object sender, ResourcesChangedEventArgs e)
+        private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
         {
             ((ILogical)this).NotifyResourcesChanged(e);
         }

+ 10 - 0
src/Avalonia.Styling/Controls/IResourceDictionary.cs

@@ -11,6 +11,16 @@ namespace Avalonia.Controls
     /// </summary>
     public interface IResourceDictionary : IDictionary<string, object>
     {
+        /// <summary>
+        /// Raised when resources in the dictionary are changed.
+        /// </summary>
+        event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        /// <summary>
+        /// Gets a collection of child resource dictionaries.
+        /// </summary>
+        IList<IResourceDictionary> MergedDictionaries { get; }
+
         /// <summary>
         /// Tries to find a resource within the dictionary.
         /// </summary>

+ 1 - 1
src/Avalonia.Styling/Controls/IResourceProvider.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Controls
     public interface IResourceProvider
     {
         /// <summary>
-        /// Raised when the resources in the element are changed.
+        /// Raised when resources in the element are changed.
         /// </summary>
         event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
 

+ 74 - 3
src/Avalonia.Styling/Controls/ResourceDictionary.cs

@@ -2,7 +2,8 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
 using Avalonia.Collections;
 
 namespace Avalonia.Controls
@@ -10,9 +11,79 @@ namespace Avalonia.Controls
     /// <summary>
     /// An indexed dictionary of resources.
     /// </summary>
-    public class ResourceDictionary : AvaloniaDictionary<string, object>, IResourceDictionary, IDictionary
+    public class ResourceDictionary : AvaloniaDictionary<string, object>, IResourceDictionary
     {
+        private AvaloniaList<IResourceDictionary> _mergedDictionaries;
+
+        public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
+
+        public ResourceDictionary()
+        {
+            CollectionChanged += OnCollectionChanged;
+        }
+
+        public IList<IResourceDictionary> MergedDictionaries
+        {
+            get
+            {
+                if (_mergedDictionaries == null)
+                {
+                    _mergedDictionaries = new AvaloniaList<IResourceDictionary>();
+                    _mergedDictionaries.ResetBehavior = ResetBehavior.Remove;
+                    _mergedDictionaries.ForEachItem(
+                        x =>
+                        {
+                            if (x.Count > 0)
+                            {
+                                OnResourcesChanged();
+                            }
+
+                            x.ResourcesChanged += MergedDictionaryResourcesChanged;
+                        },
+                        x =>
+                        {
+                            if (x.Count > 0)
+                            {
+                                OnResourcesChanged();
+                            }
+
+                            x.ResourcesChanged -= MergedDictionaryResourcesChanged;
+                        },
+                        () => { });
+                }
+
+                return _mergedDictionaries;
+            }
+        }
+
         /// <inheritdoc/>
-        public bool TryGetResource(string key, out object value) => TryGetValue(key, out value);
+        public bool TryGetResource(string key, out object value)
+        {
+            if (TryGetValue(key, out value))
+            {
+                return true;
+            }
+
+            if (_mergedDictionaries != null)
+            {
+                for (var i = _mergedDictionaries.Count - 1; i >= 0; --i)
+                {
+                    if (_mergedDictionaries[i].TryGetResource(key, out value))
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private void OnResourcesChanged()
+        {
+            ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+        }
+
+        private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged();
+        private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged();
     }
 }

+ 175 - 0
tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs

@@ -0,0 +1,175 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Controls;
+using Xunit;
+
+namespace Avalonia.Styling.UnitTests
+{
+    public class ResourceDictionaryTests
+    {
+        [Fact]
+        public void TryGetResource_Should_Find_Resource()
+        {
+            var target = new ResourceDictionary
+            {
+                { "foo", "bar" },
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary
+                    {
+                        { "foo", "bar" },
+                    }
+                }
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Itself_Before_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                { "foo", "bar" },
+            };
+
+            target.MergedDictionaries.Add(new ResourceDictionary
+            {
+                { "foo", "baz" },
+            });
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("bar", result);
+        }
+
+        [Fact]
+        public void TryGetResource_Should_Find_Resource_From_Later_Merged_Dictionary()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary
+                    {
+                        { "foo", "bar" },
+                    },
+                    new ResourceDictionary
+                    {
+                        { "foo", "baz" },
+                    }
+                }
+            };
+
+            Assert.True(target.TryGetResource("foo", out var result));
+            Assert.Equal("baz", result);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_Resource_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.Add(new ResourceDictionary
+            {
+                { "foo", "bar" },
+            });
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Add()
+        {
+            var target = new ResourceDictionary();
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.Add(new ResourceDictionary());
+
+            Assert.False(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Remove()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary { { "foo", "bar" } },
+                }
+            };
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.RemoveAt(0);
+
+            Assert.True(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Remove()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary(),
+                }
+            };
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries.RemoveAt(0);
+
+            Assert.False(raised);
+        }
+
+        [Fact]
+        public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Resource_Add()
+        {
+            var target = new ResourceDictionary
+            {
+                MergedDictionaries =
+                {
+                    new ResourceDictionary(),
+                }
+            };
+
+            var raised = false;
+
+            target.ResourcesChanged += (_, __) => raised = true;
+            target.MergedDictionaries[0].Add("foo", "bar");
+
+            Assert.True(raised);
+        }
+    }
+}