using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Linq; using System.Reflection; using Xamarin.Forms.Internals; using System.Collections.ObjectModel; using System.Collections.Specialized; namespace Xamarin.Forms { public class ResourceDictionary : IResourceDictionary, IDictionary { static ConditionalWeakTable s_instances = new ConditionalWeakTable(); readonly Dictionary _innerDictionary = new Dictionary(); ResourceDictionary _mergedInstance; Type _mergedWith; [TypeConverter (typeof(TypeTypeConverter))] public Type MergedWith { get { return _mergedWith; } set { if (_mergedWith == value) return; if (!typeof(ResourceDictionary).GetTypeInfo().IsAssignableFrom(value.GetTypeInfo())) throw new ArgumentException("MergedWith should inherit from ResourceDictionary"); _mergedWith = value; if (_mergedWith == null) return; _mergedInstance = s_instances.GetValue(_mergedWith, (key) => (ResourceDictionary)Activator.CreateInstance(key)); OnValuesChanged(_mergedInstance.ToArray()); } } ICollection _mergedDictionaries; public ICollection MergedDictionaries { get { if (_mergedDictionaries == null) { var col = new ObservableCollection(); col.CollectionChanged += MergedDictionaries_CollectionChanged; _mergedDictionaries = col; } return _mergedDictionaries; } } IList _collectionTrack; void MergedDictionaries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Move() isn't exposed by ICollection if (e.Action == NotifyCollectionChangedAction.Move) return; _collectionTrack = _collectionTrack ?? new List(); // Collection has been cleared if (e.Action == NotifyCollectionChangedAction.Reset) { foreach (var dictionary in _collectionTrack) dictionary.ValuesChanged -= Item_ValuesChanged; _collectionTrack.Clear(); return; } // New Items if (e.NewItems != null) { foreach (var item in e.NewItems) { var rd = (ResourceDictionary)item; _collectionTrack.Add(rd); rd.ValuesChanged += Item_ValuesChanged; OnValuesChanged(rd.ToArray()); } } // Old Items if (e.OldItems != null) { foreach (var item in e.OldItems) { var rd = (ResourceDictionary)item; rd.ValuesChanged -= Item_ValuesChanged; _collectionTrack.Remove(rd); } } } void Item_ValuesChanged(object sender, ResourcesChangedEventArgs e) { OnValuesChanged(e.Values.ToArray()); } void ICollection>.Add(KeyValuePair item) { ((ICollection>)_innerDictionary).Add(item); OnValuesChanged(item); } public void Clear() { _innerDictionary.Clear(); } bool ICollection>.Contains(KeyValuePair item) { return ((ICollection>)_innerDictionary).Contains(item) || (_mergedInstance != null && _mergedInstance.Contains(item)); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { ((ICollection>)_innerDictionary).CopyTo(array, arrayIndex); } public int Count { get { return _innerDictionary.Count; } } bool ICollection>.IsReadOnly { get { return ((ICollection>)_innerDictionary).IsReadOnly; } } bool ICollection>.Remove(KeyValuePair item) { return ((ICollection>)_innerDictionary).Remove(item); } public void Add(string key, object value) { if (ContainsKey(key)) throw new ArgumentException($"A resource with the key '{key}' is already present in the ResourceDictionary."); _innerDictionary.Add(key, value); OnValueChanged(key, value); } public bool ContainsKey(string key) { return _innerDictionary.ContainsKey(key); } [IndexerName("Item")] public object this[string index] { get { if (_innerDictionary.ContainsKey(index)) return _innerDictionary[index]; if (_mergedInstance != null && _mergedInstance.ContainsKey(index)) return _mergedInstance[index]; if (MergedDictionaries != null) foreach (var dict in MergedDictionaries.Reverse()) if (dict.ContainsKey(index)) return dict[index]; throw new KeyNotFoundException($"The resource '{index}' is not present in the dictionary."); } set { _innerDictionary[index] = value; OnValueChanged(index, value); } } public ICollection Keys { get { return _innerDictionary.Keys; } } public bool Remove(string key) { return _innerDictionary.Remove(key); } public ICollection Values { get { return _innerDictionary.Values; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator> GetEnumerator() { return _innerDictionary.GetEnumerator(); } internal IEnumerable> MergedResources { get { if (MergedDictionaries != null) foreach (var r in MergedDictionaries.Reverse().SelectMany(x => x.MergedResources)) yield return r; if (_mergedInstance != null) foreach (var r in _mergedInstance.MergedResources) yield return r; foreach (var r in _innerDictionary) yield return r; } } public bool TryGetValue(string key, out object value) { return _innerDictionary.TryGetValue(key, out value) || (_mergedInstance != null && _mergedInstance.TryGetValue(key, out value)) || (MergedDictionaries != null && TryGetMergedDictionaryValue(key, out value)); } bool TryGetMergedDictionaryValue(string key, out object value) { foreach (var dictionary in MergedDictionaries.Reverse()) if (dictionary.TryGetValue(key, out value)) return true; value = null; return false; } event EventHandler IResourceDictionary.ValuesChanged { add { ValuesChanged += value; } remove { ValuesChanged -= value; } } public void Add(Style style) { if (string.IsNullOrEmpty(style.Class)) Add(style.TargetType.FullName, style); else { IList