3728f80ec36d3a16d8ea38f9ca076b223fefc770
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / Element.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Collections.ObjectModel;
4 using System.Collections.Specialized;
5 using System.ComponentModel;
6 using System.Runtime.CompilerServices;
7 using System.Xml;
8 using Tizen.NUI.Binding.Internals;
9
10 namespace Tizen.NUI.Binding
11 {
12     /// <summary>
13     /// Provides the base class for all Tizen.NUI.Binding hierarchal elements. This class contains all the methods and properties required to represent an element in the Tizen.NUI.Binding hierarchy.
14     /// </summary>
15     [EditorBrowsable(EditorBrowsableState.Never)]
16     public abstract partial class Element : BindableObject, IElement, INameScope, IElementController
17     {
18         internal static readonly ReadOnlyCollection<Element> EmptyChildren = new ReadOnlyCollection<Element>(System.Array.Empty<Element>());
19
20         /// <summary>
21         /// Identifies the ClassId bindable property.
22         /// </summary>
23         internal static readonly BindableProperty ClassIdProperty = BindableProperty.Create(nameof(ClassId), typeof(string), typeof(Tizen.NUI.BaseComponents.View), null);
24
25         string _automationId;
26
27         IList<BindableObject> _bindableResources;
28
29         List<Action<object, ResourcesChangedEventArgs>> _changeHandlers;
30
31         Dictionary<BindableProperty, string> _dynamicResources;
32
33         Guid? _id;
34
35         Element _parentOverride;
36
37         string _styleId;
38
39         /// <summary>
40         /// Gets or sets a value that allows the automation framework to find and interact with this element.
41         /// </summary>
42         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
43         [EditorBrowsable(EditorBrowsableState.Never)]
44         public string AutomationId
45         {
46             get { return _automationId; }
47             set
48             {
49                 if (_automationId != null)
50                     throw new InvalidOperationException("AutomationId may only be set one time");
51                 _automationId = value;
52             }
53         }
54
55         /// <summary>
56         /// Gets or sets a value used to identify a collection of semantically similar elements.
57         /// </summary>
58         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
59         [EditorBrowsable(EditorBrowsableState.Never)]
60         public string ClassId
61         {
62             get { return (string)GetValue(ClassIdProperty); }
63             set { SetValue(ClassIdProperty, value); }
64         }
65
66         /// <summary>
67         /// Gets a value that can be used to uniquely identify an element through the run of an application.
68         /// </summary>
69         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
70         [EditorBrowsable(EditorBrowsableState.Never)]
71         public Guid Id
72         {
73             get
74             {
75                 if (!_id.HasValue)
76                     _id = Guid.NewGuid();
77                 return _id.Value;
78             }
79         }
80
81         /// <summary>
82         /// Gets the element which is the closest ancestor of this element that is a BaseHandle.
83         /// </summary>
84         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
85         [EditorBrowsable(EditorBrowsableState.Never)]
86         [Obsolete("ParentView is obsolete as of version 2.1.0. Please use Parent instead.")]
87         public BaseHandle ParentView
88         {
89             get
90             {
91                 Element parent = Parent;
92                 while (parent != null)
93                 {
94                     var parentView = parent as BaseHandle;
95                     if (parentView != null)
96                         return parentView;
97                     parent = parent.RealParent;
98                 }
99                 return null;
100             }
101         }
102
103         /// <summary>
104         /// Gets or sets a user defined value to uniquely identify the element.
105         /// </summary>
106         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
107         [EditorBrowsable(EditorBrowsableState.Never)]
108         public string StyleId
109         {
110             get { return _styleId; }
111             set
112             {
113                 if (_styleId == value)
114                     return;
115
116                 OnPropertyChanging();
117                 _styleId = value;
118                 OnPropertyChanged();
119             }
120         }
121
122         internal virtual ReadOnlyCollection<Element> LogicalChildrenInternal => EmptyChildren;
123
124         /// <summary>
125         /// For internal use.
126         /// </summary>
127         [EditorBrowsable(EditorBrowsableState.Never)]
128         public ReadOnlyCollection<Element> LogicalChildren => LogicalChildrenInternal;
129
130         internal bool Owned { get; set; }
131
132         internal Element ParentOverride
133         {
134             get { return _parentOverride; }
135             set
136             {
137                 if (_parentOverride == value)
138                     return;
139
140                 bool emitChange = Parent != value;
141
142                 if (emitChange)
143                     OnPropertyChanging(nameof(Parent));
144
145                 _parentOverride = value;
146
147                 if (emitChange)
148                     OnPropertyChanged(nameof(Parent));
149             }
150         }
151
152         /// <summary>
153         /// For internal use.
154         /// </summary>
155         [EditorBrowsable(EditorBrowsableState.Never)]
156         public Element RealParent { get; private set; }
157
158         Dictionary<BindableProperty, string> DynamicResources
159         {
160             get { return _dynamicResources ?? (_dynamicResources = new Dictionary<BindableProperty, string>()); }
161         }
162
163         void IElement.AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
164         {
165             _changeHandlers = _changeHandlers ?? new List<Action<object, ResourcesChangedEventArgs>>(2);
166             _changeHandlers.Add(onchanged);
167         }
168
169         /// <summary>
170         /// Gets or sets the parent element of the element.
171         /// </summary>
172         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
173         [EditorBrowsable(EditorBrowsableState.Never)]
174         public Element Parent
175         {
176             get { return _parentOverride ?? RealParent; }
177             set
178             {
179                 if (RealParent == value)
180                     return;
181
182                 OnPropertyChanging();
183
184                 if (RealParent != null)
185                     ((IElement)RealParent).RemoveResourcesChangedListener(OnParentResourcesChanged);
186                 RealParent = value;
187                 if (RealParent != null)
188                 {
189                     OnParentResourcesChanged(RealParent?.GetMergedResources());
190                     ((IElement)RealParent).AddResourcesChangedListener(OnParentResourcesChanged);
191                 }
192
193                 object context = value != null ? value.BindingContext : null;
194                 if (value != null)
195                 {
196                     value.SetChildInheritedBindingContext(this, context);
197                 }
198                 else
199                 {
200                     SetInheritedBindingContext(this, null);
201                 }
202
203                 OnParentSet();
204
205                 OnPropertyChanged();
206             }
207         }
208
209         void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
210         {
211             if (_changeHandlers == null)
212                 return;
213             _changeHandlers.Remove(onchanged);
214         }
215
216         //void IElementController.SetValueFromRenderer(BindableProperty property, object value) => SetValueFromRenderer(property, value);
217
218         /// <summary>
219         /// Sets the value of the specified property.
220         /// </summary>
221         /// <param name="property">The BindableProperty on which to assign a value.</param>
222         /// <param name="value">The value to set.</param>
223         internal void SetValueFromRenderer(BindableProperty property, object value)
224         {
225             SetValueCore(property, value);
226         }
227
228         /// <summary>
229         /// Sets the value of the propertyKey.
230         /// </summary>
231         /// <param name="property">The BindablePropertyKey on which to assign a value.</param>
232         /// <param name="value">The value to set.</param>
233         internal void SetValueFromRenderer(BindablePropertyKey property, object value)
234         {
235             SetValueCore(property, value);
236         }
237
238         object INameScope.FindByName(string name)
239         {
240             INameScope namescope = GetNameScope();
241             if (namescope == null)
242             {
243                 return null;
244             }
245             else
246             {
247                 return namescope.FindByName(name);
248             }
249         }
250
251         void INameScope.RegisterName(string name, object scopedElement)
252         {
253             INameScope namescope = GetNameScope();
254             if (namescope == null)
255                 throw new InvalidOperationException("this element is not in a namescope");
256             namescope.RegisterName(name, scopedElement);
257         }
258
259         [Obsolete]
260         void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
261         {
262             INameScope namescope = GetNameScope();
263             if (namescope == null)
264                 throw new InvalidOperationException("this element is not in a namescope");
265             namescope.RegisterName(name, scopedElement, xmlLineInfo);
266         }
267
268         void INameScope.UnregisterName(string name)
269         {
270             INameScope namescope = GetNameScope();
271             if (namescope == null)
272                 throw new InvalidOperationException("this element is not in a namescope");
273             namescope.UnregisterName(name);
274         }
275
276         internal event EventHandler<ElementEventArgs> ChildAdded;
277
278         internal event EventHandler<ElementEventArgs> ChildRemoved;
279
280         internal event EventHandler<ElementEventArgs> DescendantAdded;
281
282         internal event EventHandler<ElementEventArgs> DescendantRemoved;
283
284         /// <summary>
285         /// Removes a previously set dynamic resource.
286         /// </summary>
287         /// <param name="property">The BindableProperty from which to remove the DynamicResource.</param>
288         internal new void RemoveDynamicResource(BindableProperty property)
289         {
290             base.RemoveDynamicResource(property);
291         }
292
293         /// <summary>
294         /// Sets the BindableProperty property of this element to be updated via the DynamicResource with the provided key.
295         /// </summary>
296         /// <param name="property">The BindableProperty.</param>
297         /// <param name="key">The key of the DynamicResource</param>
298         internal new void SetDynamicResource(BindableProperty property, string key)
299         {
300             base.SetDynamicResource(property, key);
301         }
302
303         /// <summary>
304         /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
305         /// </summary>
306         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
307         [EditorBrowsable(EditorBrowsableState.Never)]
308         protected override void OnBindingContextChanged()
309         {
310             var gotBindingContext = false;
311             object bc = null;
312
313             for (var index = 0; index < LogicalChildrenInternal.Count; index++)
314             {
315                 Element child = LogicalChildrenInternal[index];
316
317                 if (!gotBindingContext)
318                 {
319                     bc = BindingContext;
320                     gotBindingContext = true;
321                 }
322
323                 SetChildInheritedBindingContext(child, bc);
324             }
325
326             if (_bindableResources != null)
327                 foreach (BindableObject item in _bindableResources)
328                 {
329                     SetInheritedBindingContext(item, BindingContext);
330                 }
331
332             base.OnBindingContextChanged();
333         }
334
335         /// <summary>
336         /// Invoked whenever the ChildAdded event needs to be emitted.Implement this method to add class handling for this event.
337         /// </summary>
338         /// <param name="child">The element that was added.</param>
339         /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
340         /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
341         [EditorBrowsable(EditorBrowsableState.Never)]
342         protected virtual void OnChildAdded(Element child)
343         {
344             if (child == null)
345             {
346                 throw new ArgumentNullException(nameof(child));
347             }
348
349             child.Parent = this;
350
351             child.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
352
353             ChildAdded?.Invoke(this, new ElementEventArgs(child));
354
355             OnDescendantAdded(child);
356             foreach (Element element in child.Descendants())
357                 OnDescendantAdded(element);
358         }
359
360         /// <summary>
361         /// Invoked whenever the ChildRemoved event needs to be emitted.Implement this method to add class handling for this event.
362         /// </summary>
363         /// <param name="child">The element that was removed.</param>
364         /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
365         /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
366         [EditorBrowsable(EditorBrowsableState.Never)]
367         protected virtual void OnChildRemoved(Element child)
368         {
369             if (child == null)
370             {
371                 throw new ArgumentNullException(nameof(child));
372             }
373
374             child.Parent = null;
375
376             ChildRemoved?.Invoke(child, new ElementEventArgs(child));
377
378             OnDescendantRemoved(child);
379             foreach (Element element in child.Descendants())
380                 OnDescendantRemoved(element);
381         }
382
383         /// <summary>
384         /// Invoked whenever the Parent of an element is set.Implement this method in order to add behavior when the element is added to a parent.
385         /// </summary>
386         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
387         [EditorBrowsable(EditorBrowsableState.Never)]
388         protected virtual void OnParentSet()
389         {
390             ParentSet?.Invoke(this, EventArgs.Empty);
391             // ApplyStyleSheetsOnParentSet();
392         }
393
394         /// <summary>
395         /// Method that is called when a bound property is changed.
396         /// </summary>
397         /// <param name="propertyName">The name of the bound property that changed.</param>
398         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
399         [EditorBrowsable(EditorBrowsableState.Never)]
400         protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
401         {
402             base.OnPropertyChanged(propertyName);
403         }
404
405         /// <summary>
406         /// For internal use.
407         /// </summary>
408         /// <returns>the elements</returns>
409         [EditorBrowsable(EditorBrowsableState.Never)]
410         public IEnumerable<Element> Descendants()
411         {
412             var queue = new Queue<Element>(16);
413             queue.Enqueue(this);
414
415             while (queue.Count > 0)
416             {
417                 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
418                 for (var i = 0; i < children.Count; i++)
419                 {
420                     Element child = children[i];
421                     yield return child;
422                     queue.Enqueue(child);
423                 }
424             }
425         }
426
427         internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
428         {
429             // if (e == ResourcesChangedEventArgs.StyleSheets)
430             //  // ApplyStyleSheetsOnParentSet();
431             // else
432             //  OnParentResourcesChanged(e.Values);
433         }
434
435         internal virtual void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
436         {
437             OnResourcesChanged(values);
438         }
439
440         internal override void OnRemoveDynamicResource(BindableProperty property)
441         {
442             DynamicResources.Remove(property);
443
444             if (DynamicResources.Count == 0)
445                 _dynamicResources = null;
446             base.OnRemoveDynamicResource(property);
447         }
448
449         internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
450         {
451             OnResourcesChanged(e.Values);
452         }
453
454         internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
455         {
456             if (values == null)
457                 return;
458             if (_changeHandlers != null)
459                 foreach (Action<object, ResourcesChangedEventArgs> handler in _changeHandlers)
460                     handler(this, new ResourcesChangedEventArgs(values));
461             if (_dynamicResources == null)
462                 return;
463             if (_bindableResources == null)
464                 _bindableResources = new List<BindableObject>();
465             foreach (KeyValuePair<string, object> value in values)
466             {
467                 List<BindableProperty> changedResources = null;
468                 foreach (KeyValuePair<BindableProperty, string> dynR in DynamicResources)
469                 {
470                     // when the DynamicResource bound to a BindableProperty is
471                     // changing then the BindableProperty needs to be refreshed;
472                     // The .Value is the name of DynamicResouce to which the BindableProperty is bound.
473                     // The .Key is the name of the DynamicResource whose value is changing.
474                     if (dynR.Value != value.Key)
475                         continue;
476                     changedResources = changedResources ?? new List<BindableProperty>();
477                     changedResources.Add(dynR.Key);
478                 }
479                 if (changedResources == null)
480                     continue;
481                 foreach (BindableProperty changedResource in changedResources)
482                     OnResourceChanged(changedResource, value.Value);
483
484                 var bindableObject = value.Value as BindableObject;
485                 if (bindableObject != null && (bindableObject as Element)?.Parent == null)
486                 {
487                     if (!_bindableResources.Contains(bindableObject))
488                         _bindableResources.Add(bindableObject);
489                     SetInheritedBindingContext(bindableObject, BindingContext);
490                 }
491             }
492         }
493
494         internal override void OnSetDynamicResource(BindableProperty property, string key)
495         {
496             base.OnSetDynamicResource(property, key);
497             DynamicResources[property] = key;
498             object value;
499             if (this.TryGetResource(key, out value))
500                 OnResourceChanged(property, value);
501
502             Tizen.NUI.Application.AddResourceChangedCallback(this, (this as Element).OnResourcesChanged);
503         }
504
505         internal event EventHandler ParentSet;
506
507         internal virtual void SetChildInheritedBindingContext(Element child, object context)
508         {
509             SetInheritedBindingContext(child, context);
510         }
511
512         internal IEnumerable<Element> VisibleDescendants()
513         {
514             var queue = new Queue<Element>(16);
515             queue.Enqueue(this);
516
517             while (queue.Count > 0)
518             {
519                 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
520                 for (var i = 0; i < children.Count; i++)
521                 {
522                     var child = children[i] as BaseHandle;
523                     if (child == null)
524                     {
525                         continue;
526                     }
527                     yield return child;
528                     queue.Enqueue(child);
529                 }
530             }
531         }
532
533         INameScope GetNameScope()
534         {
535             INameScope namescope = NameScope.GetNameScope(this);
536             Element p = RealParent;
537             while (namescope == null && p != null)
538             {
539                 namescope = NameScope.GetNameScope(p);
540                 p = p.RealParent;
541             }
542             return namescope;
543         }
544
545         void OnDescendantAdded(Element child)
546         {
547             DescendantAdded?.Invoke(this, new ElementEventArgs(child));
548
549             if (RealParent != null)
550                 RealParent.OnDescendantAdded(child);
551         }
552
553         void OnDescendantRemoved(Element child)
554         {
555             DescendantRemoved?.Invoke(this, new ElementEventArgs(child));
556
557             if (RealParent != null)
558                 RealParent.OnDescendantRemoved(child);
559         }
560
561         void OnResourceChanged(BindableProperty property, object value)
562         {
563             SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearTwoWayBindings);
564         }
565     }
566 }