2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
5 using System.Collections.Specialized;
6 using System.ComponentModel;
7 using System.Globalization;
9 using System.Reflection;
10 using System.Runtime.CompilerServices;
12 using Tizen.NUI.Binding.Internals;
15 namespace Tizen.NUI.Binding
17 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
18 [EditorBrowsable(EditorBrowsableState.Never)]
19 public class ResourceDictionary : IResourceDictionary, IDictionary<string, object>
21 static ConditionalWeakTable<Type, ResourceDictionary> s_instances = new ConditionalWeakTable<Type, ResourceDictionary>();
22 readonly Dictionary<string, object> _innerDictionary = new Dictionary<string, object>();
23 ResourceDictionary _mergedInstance;
27 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
28 [EditorBrowsable(EditorBrowsableState.Never)]
29 public ResourceDictionary()
31 DependencyService.Register<IResourcesLoader, ResourcesLoader>();
35 /// Gets or sets the type of object with which the resource dictionary is merged.
37 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
38 [EditorBrowsable(EditorBrowsableState.Never)]
39 [TypeConverter(typeof(TypeTypeConverter))]
40 [Obsolete("Use Source")]
41 public Type MergedWith
43 get { return _mergedWith; }
46 if (_mergedWith == value)
50 throw new ArgumentException("MergedWith can not be used with Source");
52 if (!typeof(ResourceDictionary).GetTypeInfo().IsAssignableFrom(value.GetTypeInfo()))
53 throw new ArgumentException("MergedWith should inherit from ResourceDictionary");
56 if (_mergedWith == null)
59 _mergedInstance = s_instances.GetValue(_mergedWith, (key) => (ResourceDictionary)Activator.CreateInstance(key));
60 OnValuesChanged(_mergedInstance.ToArray());
65 /// Gets or sets the URI of the merged resource dictionary.
67 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
68 [EditorBrowsable(EditorBrowsableState.Never)]
69 [TypeConverter(typeof(RDSourceTypeConverter))]
72 get { return _source; }
77 throw new InvalidOperationException("Source can only be set from XAML."); //through the RDSourceTypeConverter
82 /// To set and load source.
84 /// <param name="value">The source.</param>
85 /// <param name="resourcePath">The resource path.</param>
86 /// <param name="assembly">The assembly.</param>
87 /// <param name="lineInfo">The xml line info.</param>
88 /// Used by the XamlC compiled converter.
89 [EditorBrowsable(EditorBrowsableState.Never)]
90 public void SetAndLoadSource(Uri value, string resourcePath, Assembly assembly, System.Xml.IXmlLineInfo lineInfo)
93 if (_mergedWith != null)
94 throw new ArgumentException("Source can not be used with MergedWith");
96 //this will return a type if the RD as an x:Class element, and codebehind
97 var type = XamlResourceIdAttribute.GetTypeForPath(assembly, resourcePath);
99 _mergedInstance = s_instances.GetValue(type, (key) => (ResourceDictionary)Activator.CreateInstance(key));
101 _mergedInstance = DependencyService.Get<IResourcesLoader>()?.CreateFromResource<ResourceDictionary>(resourcePath, assembly, lineInfo);
102 OnValuesChanged(_mergedInstance.ToArray());
105 ICollection<ResourceDictionary> _mergedDictionaries;
107 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
108 [EditorBrowsable(EditorBrowsableState.Never)]
109 public ICollection<ResourceDictionary> MergedDictionaries
113 if (_mergedDictionaries == null)
115 var col = new ObservableCollection<ResourceDictionary>();
116 col.CollectionChanged += MergedDictionaries_CollectionChanged;
117 _mergedDictionaries = col;
119 return _mergedDictionaries;
123 IList<ResourceDictionary> _collectionTrack;
125 void MergedDictionaries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
127 // Move() isn't exposed by ICollection
128 if (e.Action == NotifyCollectionChangedAction.Move)
131 _collectionTrack = _collectionTrack ?? new List<ResourceDictionary>();
132 // Collection has been cleared
133 if (e.Action == NotifyCollectionChangedAction.Reset)
135 foreach (var dictionary in _collectionTrack)
136 dictionary.ValuesChanged -= Item_ValuesChanged;
138 _collectionTrack.Clear();
143 if (e.NewItems != null)
145 foreach (var item in e.NewItems)
147 var rd = (ResourceDictionary)item;
148 _collectionTrack.Add(rd);
149 rd.ValuesChanged += Item_ValuesChanged;
150 OnValuesChanged(rd.ToArray());
155 if (e.OldItems != null)
157 foreach (var item in e.OldItems)
159 var rd = (ResourceDictionary)item;
160 rd.ValuesChanged -= Item_ValuesChanged;
161 _collectionTrack.Remove(rd);
166 void Item_ValuesChanged(object sender, ResourcesChangedEventArgs e)
168 OnValuesChanged(e.Values.ToArray());
171 void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
173 ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Add(item);
174 OnValuesChanged(item);
177 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
178 [EditorBrowsable(EditorBrowsableState.Never)]
181 _innerDictionary.Clear();
184 bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
186 return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Contains(item)
187 || (_mergedInstance != null && _mergedInstance.Contains(item));
190 void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
192 ((ICollection<KeyValuePair<string, object>>)_innerDictionary).CopyTo(array, arrayIndex);
195 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
196 [EditorBrowsable(EditorBrowsableState.Never)]
199 get { return _innerDictionary.Count; }
202 bool ICollection<KeyValuePair<string, object>>.IsReadOnly
204 get { return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).IsReadOnly; }
207 bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
209 return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Remove(item);
212 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
213 [EditorBrowsable(EditorBrowsableState.Never)]
214 public void Add(string key, object value)
216 if (ContainsKey(key))
217 throw new ArgumentException($"A resource with the key '{key}' is already present in the ResourceDictionary.");
218 _innerDictionary.Add(key, value);
219 OnValueChanged(key, value);
222 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
223 [EditorBrowsable(EditorBrowsableState.Never)]
224 public bool ContainsKey(string key)
226 return _innerDictionary.ContainsKey(key);
230 /// Gets or sets the value according to index.
232 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
233 [EditorBrowsable(EditorBrowsableState.Never)]
234 [IndexerName("Item")]
235 public object this[string index]
239 if (_innerDictionary.ContainsKey(index))
240 return _innerDictionary[index];
241 if (_mergedInstance != null && _mergedInstance.ContainsKey(index))
242 return _mergedInstance[index];
243 if (MergedDictionaries != null)
244 foreach (var dict in MergedDictionaries.Reverse())
245 if (dict.ContainsKey(index))
247 throw new KeyNotFoundException($"The resource '{index}' is not present in the dictionary.");
251 _innerDictionary[index] = value;
252 OnValueChanged(index, value);
256 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
257 [EditorBrowsable(EditorBrowsableState.Never)]
258 public ICollection<string> Keys
260 get { return _innerDictionary.Keys; }
263 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
264 [EditorBrowsable(EditorBrowsableState.Never)]
265 public bool Remove(string key)
267 return _innerDictionary.Remove(key);
270 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
271 [EditorBrowsable(EditorBrowsableState.Never)]
272 public ICollection<object> Values
274 get { return _innerDictionary.Values; }
277 IEnumerator IEnumerable.GetEnumerator()
279 return GetEnumerator();
282 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
283 [EditorBrowsable(EditorBrowsableState.Never)]
284 public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
286 return _innerDictionary.GetEnumerator();
289 internal IEnumerable<KeyValuePair<string, object>> MergedResources
293 if (MergedDictionaries != null)
294 foreach (var r in MergedDictionaries.Reverse().SelectMany(x => x.MergedResources))
296 if (_mergedInstance != null)
297 foreach (var r in _mergedInstance.MergedResources)
299 foreach (var r in _innerDictionary)
304 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
305 [EditorBrowsable(EditorBrowsableState.Never)]
306 public bool TryGetValue(string key, out object value)
308 return _innerDictionary.TryGetValue(key, out value)
309 || (_mergedInstance != null && _mergedInstance.TryGetValue(key, out value))
310 || (MergedDictionaries != null && TryGetMergedDictionaryValue(key, out value));
313 bool TryGetMergedDictionaryValue(string key, out object value)
315 foreach (var dictionary in MergedDictionaries.Reverse())
316 if (dictionary.TryGetValue(key, out value))
323 event EventHandler<ResourcesChangedEventArgs> IResourceDictionary.ValuesChanged
325 add { ValuesChanged += value; }
326 remove { ValuesChanged -= value; }
329 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
330 [EditorBrowsable(EditorBrowsableState.Never)]
331 public void Add(ResourceDictionary mergedResourceDictionary)
333 MergedDictionaries.Add(mergedResourceDictionary);
336 void OnValueChanged(string key, object value)
338 OnValuesChanged(new KeyValuePair<string, object>(key, value));
341 void OnValuesChanged(params KeyValuePair<string, object>[] values)
343 if (values == null || values.Length == 0)
345 ValuesChanged?.Invoke(this, new ResourcesChangedEventArgs(values));
348 event EventHandler<ResourcesChangedEventArgs> ValuesChanged;
350 [Xaml.ProvideCompiled("Tizen.NUI.Xaml.Core.XamlC.RDSourceTypeConverter")]
351 internal class RDSourceTypeConverter : TypeConverter, IExtendedTypeConverter
353 object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
355 if (serviceProvider == null)
356 throw new ArgumentNullException(nameof(serviceProvider));
358 var targetRD = (serviceProvider.GetService(typeof(Xaml.IProvideValueTarget)) as Xaml.IProvideValueTarget)?.TargetObject as ResourceDictionary;
359 if (targetRD == null)
362 var rootObjectType = (serviceProvider.GetService(typeof(Xaml.IRootObjectProvider)) as Xaml.IRootObjectProvider)?.RootObject.GetType();
363 if (rootObjectType == null)
366 var lineInfo = (serviceProvider.GetService(typeof(Xaml.IXmlLineInfoProvider)) as Xaml.IXmlLineInfoProvider)?.XmlLineInfo;
367 var rootTargetPath = XamlResourceIdAttribute.GetPathForType(rootObjectType);
368 var uri = new Uri(value, UriKind.Relative); //we don't want file:// uris, even if they start with '/'
369 var resourcePath = GetResourcePath(uri, rootTargetPath);
371 targetRD.SetAndLoadSource(uri, resourcePath, rootObjectType.GetTypeInfo().Assembly, lineInfo);
375 internal static string GetResourcePath(Uri uri, string rootTargetPath)
377 //need a fake scheme so it's not seen as file:// uri, and the forward slashes are valid on all plats
378 var resourceUri = uri.OriginalString.StartsWith("/", StringComparison.Ordinal)
379 ? new Uri($"pack://{uri.OriginalString}", UriKind.Absolute)
380 : new Uri($"pack:///{rootTargetPath}/../{uri.OriginalString}", UriKind.Absolute);
382 //drop the leading '/'
383 return resourceUri.AbsolutePath.Substring(1);
386 object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
388 throw new NotImplementedException();
391 public override object ConvertFromInvariantString(string value)
393 throw new NotImplementedException();