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.XamlBinding.Internals;
15 namespace Tizen.NUI.XamlBinding
17 /// This will be public opened in tizen_5.5 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.5 after ACR done. Before ACR, need to be hidden as inhouse API.
28 [EditorBrowsable(EditorBrowsableState.Never)]
29 public ResourceDictionary()
31 DependencyService.Register<IResourcesLoader, ResourcesLoader>();
34 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
35 [EditorBrowsable(EditorBrowsableState.Never)]
36 [TypeConverter(typeof(TypeTypeConverter))]
37 [Obsolete("Use Source")]
38 public Type MergedWith {
39 get { return _mergedWith; }
41 if (_mergedWith == value)
45 throw new ArgumentException("MergedWith can not be used with Source");
47 if (!typeof(ResourceDictionary).GetTypeInfo().IsAssignableFrom(value.GetTypeInfo()))
48 throw new ArgumentException("MergedWith should inherit from ResourceDictionary");
51 if (_mergedWith == null)
54 _mergedInstance = s_instances.GetValue(_mergedWith, (key) => (ResourceDictionary)Activator.CreateInstance(key));
55 OnValuesChanged(_mergedInstance.ToArray());
59 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
60 [EditorBrowsable(EditorBrowsableState.Never)]
61 [TypeConverter(typeof(RDSourceTypeConverter))]
63 get { return _source; }
67 throw new InvalidOperationException("Source can only be set from XAML."); //through the RDSourceTypeConverter
71 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
72 [EditorBrowsable(EditorBrowsableState.Never)]
73 public void SetAndLoadSource(Uri value, string resourcePath, Assembly assembly, System.Xml.IXmlLineInfo lineInfo)
76 if (_mergedWith != null)
77 throw new ArgumentException("Source can not be used with MergedWith");
79 //this will return a type if the RD as an x:Class element, and codebehind
80 var type = XamlResourceIdAttribute.GetTypeForPath(assembly, resourcePath);
82 _mergedInstance = s_instances.GetValue(type, (key) => (ResourceDictionary)Activator.CreateInstance(key));
84 _mergedInstance = DependencyService.Get<IResourcesLoader>().CreateFromResource<ResourceDictionary>(resourcePath, assembly, lineInfo);
85 OnValuesChanged(_mergedInstance.ToArray());
88 ICollection<ResourceDictionary> _mergedDictionaries;
90 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
91 [EditorBrowsable(EditorBrowsableState.Never)]
92 public ICollection<ResourceDictionary> MergedDictionaries {
94 if (_mergedDictionaries == null) {
95 var col = new ObservableCollection<ResourceDictionary>();
96 col.CollectionChanged += MergedDictionaries_CollectionChanged;
97 _mergedDictionaries = col;
99 return _mergedDictionaries;
103 internal IList<StyleSheets.StyleSheet> StyleSheets { get; set; }
105 void StyleSheetsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
108 case NotifyCollectionChangedAction.Add:
109 ValuesChanged?.Invoke(this, ResourcesChangedEventArgs.StyleSheets);
113 IList<ResourceDictionary> _collectionTrack;
115 void MergedDictionaries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
117 // Move() isn't exposed by ICollection
118 if (e.Action == NotifyCollectionChangedAction.Move)
121 _collectionTrack = _collectionTrack ?? new List<ResourceDictionary>();
122 // Collection has been cleared
123 if (e.Action == NotifyCollectionChangedAction.Reset) {
124 foreach (var dictionary in _collectionTrack)
125 dictionary.ValuesChanged -= Item_ValuesChanged;
127 _collectionTrack.Clear();
132 if (e.NewItems != null)
134 foreach (var item in e.NewItems)
136 var rd = (ResourceDictionary)item;
137 _collectionTrack.Add(rd);
138 rd.ValuesChanged += Item_ValuesChanged;
139 OnValuesChanged(rd.ToArray());
144 if (e.OldItems != null)
146 foreach (var item in e.OldItems)
148 var rd = (ResourceDictionary)item;
149 rd.ValuesChanged -= Item_ValuesChanged;
150 _collectionTrack.Remove(rd);
155 void Item_ValuesChanged(object sender, ResourcesChangedEventArgs e)
157 OnValuesChanged(e.Values.ToArray());
160 void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
162 ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Add(item);
163 OnValuesChanged(item);
166 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
167 [EditorBrowsable(EditorBrowsableState.Never)]
170 _innerDictionary.Clear();
173 bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
175 return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Contains(item)
176 || (_mergedInstance != null && _mergedInstance.Contains(item));
179 void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
181 ((ICollection<KeyValuePair<string, object>>)_innerDictionary).CopyTo(array, arrayIndex);
184 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
185 [EditorBrowsable(EditorBrowsableState.Never)]
188 get { return _innerDictionary.Count; }
191 bool ICollection<KeyValuePair<string, object>>.IsReadOnly
193 get { return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).IsReadOnly; }
196 bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
198 return ((ICollection<KeyValuePair<string, object>>)_innerDictionary).Remove(item);
201 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
202 [EditorBrowsable(EditorBrowsableState.Never)]
203 public void Add(string key, object value)
205 if (ContainsKey(key))
206 throw new ArgumentException($"A resource with the key '{key}' is already present in the ResourceDictionary.");
207 _innerDictionary.Add(key, value);
208 OnValueChanged(key, value);
211 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
212 [EditorBrowsable(EditorBrowsableState.Never)]
213 public bool ContainsKey(string key)
215 return _innerDictionary.ContainsKey(key);
218 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
219 [EditorBrowsable(EditorBrowsableState.Never)]
220 [IndexerName("Item")]
221 public object this[string index]
225 if (_innerDictionary.ContainsKey(index))
226 return _innerDictionary[index];
227 if (_mergedInstance != null && _mergedInstance.ContainsKey(index))
228 return _mergedInstance[index];
229 if (MergedDictionaries != null)
230 foreach (var dict in MergedDictionaries.Reverse())
231 if (dict.ContainsKey(index))
233 throw new KeyNotFoundException($"The resource '{index}' is not present in the dictionary.");
237 _innerDictionary[index] = value;
238 OnValueChanged(index, value);
242 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
243 [EditorBrowsable(EditorBrowsableState.Never)]
244 public ICollection<string> Keys
246 get { return _innerDictionary.Keys; }
249 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
250 [EditorBrowsable(EditorBrowsableState.Never)]
251 public bool Remove(string key)
253 return _innerDictionary.Remove(key);
256 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
257 [EditorBrowsable(EditorBrowsableState.Never)]
258 public ICollection<object> Values
260 get { return _innerDictionary.Values; }
263 IEnumerator IEnumerable.GetEnumerator()
265 return GetEnumerator();
268 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
269 [EditorBrowsable(EditorBrowsableState.Never)]
270 public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
272 return _innerDictionary.GetEnumerator();
275 internal IEnumerable<KeyValuePair<string, object>> MergedResources {
277 if (MergedDictionaries != null)
278 foreach (var r in MergedDictionaries.Reverse().SelectMany(x => x.MergedResources))
280 if (_mergedInstance != null)
281 foreach (var r in _mergedInstance.MergedResources)
283 foreach (var r in _innerDictionary)
288 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
289 [EditorBrowsable(EditorBrowsableState.Never)]
290 public bool TryGetValue(string key, out object value)
292 return _innerDictionary.TryGetValue(key, out value)
293 || (_mergedInstance != null && _mergedInstance.TryGetValue(key, out value))
294 || (MergedDictionaries != null && TryGetMergedDictionaryValue(key, out value));
297 bool TryGetMergedDictionaryValue(string key, out object value)
299 foreach (var dictionary in MergedDictionaries.Reverse())
300 if (dictionary.TryGetValue(key, out value))
307 event EventHandler<ResourcesChangedEventArgs> IResourceDictionary.ValuesChanged
309 add { ValuesChanged += value; }
310 remove { ValuesChanged -= value; }
313 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
314 [EditorBrowsable(EditorBrowsableState.Never)]
315 public void Add(Style style)
317 if (string.IsNullOrEmpty(style.Class))
318 Add(style.TargetType.FullName, style);
321 IList<Style> classes;
323 if (!TryGetValue(Style.StyleClassPrefix + style.Class, out outclasses) || (classes = outclasses as IList<Style>) == null)
324 classes = new List<Style>();
326 this[Style.StyleClassPrefix + style.Class] = classes;
330 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
331 [EditorBrowsable(EditorBrowsableState.Never)]
332 public void Add(ResourceDictionary mergedResourceDictionary)
334 MergedDictionaries.Add(mergedResourceDictionary);
337 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
338 [EditorBrowsable(EditorBrowsableState.Never)]
339 public void Add(StyleSheets.StyleSheet styleSheet)
341 StyleSheets = StyleSheets ?? new List<StyleSheets.StyleSheet>(2);
342 StyleSheets.Add(styleSheet);
343 ValuesChanged?.Invoke(this, ResourcesChangedEventArgs.StyleSheets);
346 void OnValueChanged(string key, object value)
348 OnValuesChanged(new KeyValuePair<string, object>(key, value));
351 void OnValuesChanged(params KeyValuePair<string, object>[] values)
353 if (values == null || values.Length == 0)
355 ValuesChanged?.Invoke(this, new ResourcesChangedEventArgs(values));
358 event EventHandler<ResourcesChangedEventArgs> ValuesChanged;
360 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
361 [EditorBrowsable(EditorBrowsableState.Never)]
362 [Xaml.ProvideCompiled("Tizen.NUI.Xaml.Forms.XamlC.RDSourceTypeConverter")]
363 public class RDSourceTypeConverter : TypeConverter, IExtendedTypeConverter
365 object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
367 if (serviceProvider == null)
368 throw new ArgumentNullException(nameof(serviceProvider));
370 var targetRD = (serviceProvider.GetService(typeof(Xaml.IProvideValueTarget)) as Xaml.IProvideValueTarget)?.TargetObject as ResourceDictionary;
371 if (targetRD == null)
374 var rootObjectType = (serviceProvider.GetService(typeof(Xaml.IRootObjectProvider)) as Xaml.IRootObjectProvider)?.RootObject.GetType();
375 if (rootObjectType == null)
378 var lineInfo = (serviceProvider.GetService(typeof(Xaml.IXmlLineInfoProvider)) as Xaml.IXmlLineInfoProvider)?.XmlLineInfo;
379 var rootTargetPath = XamlResourceIdAttribute.GetPathForType(rootObjectType);
380 var uri = new Uri(value, UriKind.Relative); //we don't want file:// uris, even if they start with '/'
381 var resourcePath = GetResourcePath(uri, rootTargetPath);
383 targetRD.SetAndLoadSource(uri, resourcePath, rootObjectType.GetTypeInfo().Assembly, lineInfo);
387 internal static string GetResourcePath(Uri uri, string rootTargetPath)
389 //need a fake scheme so it's not seen as file:// uri, and the forward slashes are valid on all plats
390 var resourceUri = uri.OriginalString.StartsWith("/", StringComparison.Ordinal)
391 ? new Uri($"pack://{uri.OriginalString}", UriKind.Absolute)
392 : new Uri($"pack:///{rootTargetPath}/../{uri.OriginalString}", UriKind.Absolute);
394 //drop the leading '/'
395 return resourceUri.AbsolutePath.Substring(1);
398 object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
400 throw new NotImplementedException();
403 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
404 [EditorBrowsable(EditorBrowsableState.Never)]
405 public override object ConvertFromInvariantString(string value)
407 throw new NotImplementedException();