2 * Copyright(c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Collections.ObjectModel;
21 using System.ComponentModel;
22 using System.Runtime.CompilerServices;
24 using Tizen.NUI.Binding.Internals;
26 namespace Tizen.NUI.Binding
29 /// 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.
31 [EditorBrowsable(EditorBrowsableState.Never)]
32 public abstract partial class Element : BindableObject, IElement, INameScope, IElementController
34 internal static readonly ReadOnlyCollection<Element> EmptyChildren = new ReadOnlyCollection<Element>(System.Array.Empty<Element>());
37 /// Identifies the ClassId bindable property.
39 internal static readonly BindableProperty ClassIdProperty = BindableProperty.Create(nameof(ClassId), typeof(string), typeof(Tizen.NUI.BaseComponents.View), null);
43 IList<BindableObject> bindableResources;
45 List<Action<object, ResourcesChangedEventArgs>> changeHandlers;
47 Dictionary<BindableProperty, string> dynamicResources;
51 Element parentOverride;
56 /// Gets or sets a value that allows the automation framework to find and interact with this element.
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 AutomationId
62 get { return automationId; }
65 if (automationId != null)
66 throw new InvalidOperationException("AutomationId may only be set one time");
72 /// Gets or sets a value used to identify a collection of semantically similar elements.
74 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
75 [EditorBrowsable(EditorBrowsableState.Never)]
78 get { return (string)GetValue(ClassIdProperty); }
79 set { SetValue(ClassIdProperty, value); }
83 /// Gets a value that can be used to uniquely identify an element through the run of an application.
85 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
86 [EditorBrowsable(EditorBrowsableState.Never)]
98 /// Gets the element which is the closest ancestor of this element that is a BaseHandle.
100 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
101 [EditorBrowsable(EditorBrowsableState.Never)]
102 [Obsolete("ParentView is obsolete as of version 2.1.0. Please use Parent instead.")]
103 public BaseHandle ParentView
107 Element parent = Parent;
108 while (parent != null)
110 var parentView = parent as BaseHandle;
111 if (parentView != null)
113 parent = parent.RealParent;
120 /// Gets or sets a user defined value to uniquely identify the element.
122 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
123 [EditorBrowsable(EditorBrowsableState.Never)]
124 public string StyleId
126 get { return styleId; }
129 if (styleId == value)
132 OnPropertyChanging();
138 internal virtual ReadOnlyCollection<Element> LogicalChildrenInternal => EmptyChildren;
141 /// For internal use.
143 [EditorBrowsable(EditorBrowsableState.Never)]
144 public ReadOnlyCollection<Element> LogicalChildren => LogicalChildrenInternal;
146 internal bool Owned { get; set; }
148 internal Element ParentOverride
150 get { return parentOverride; }
153 if (parentOverride == value)
156 bool emitChange = Parent != value;
159 OnPropertyChanging(nameof(Parent));
161 parentOverride = value;
164 OnPropertyChanged(nameof(Parent));
169 /// For internal use.
171 [EditorBrowsable(EditorBrowsableState.Never)]
172 public Element RealParent { get; private set; }
174 Dictionary<BindableProperty, string> DynamicResources
176 get { return dynamicResources ?? (dynamicResources = new Dictionary<BindableProperty, string>()); }
179 void IElement.AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
181 changeHandlers = changeHandlers ?? new List<Action<object, ResourcesChangedEventArgs>>(2);
182 changeHandlers.Add(onchanged);
186 /// Gets or sets the parent element of the element.
188 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
189 [EditorBrowsable(EditorBrowsableState.Never)]
190 public Element Parent
192 get { return parentOverride ?? RealParent; }
195 if (RealParent == value)
198 OnPropertyChanging();
200 if (RealParent != null)
201 ((IElement)RealParent).RemoveResourcesChangedListener(OnParentResourcesChanged);
203 if (RealParent != null)
205 OnParentResourcesChanged(RealParent?.GetMergedResources());
206 ((IElement)RealParent).AddResourcesChangedListener(OnParentResourcesChanged);
209 object context = value != null ? value.BindingContext : null;
212 value.SetChildInheritedBindingContext(this, context);
216 SetInheritedBindingContext(this, null);
226 /// Gets the x:Name dictionary of the element.
228 [EditorBrowsable(EditorBrowsableState.Never)]
229 public Dictionary<string, object> XNames => (GetNameScope() as NameScope)?.XNames ?? null;
231 void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
233 if (changeHandlers == null)
235 changeHandlers.Remove(onchanged);
238 //void IElementController.SetValueFromRenderer(BindableProperty property, object value) => SetValueFromRenderer(property, value);
241 /// Sets the value of the specified property.
243 /// <param name="property">The BindableProperty on which to assign a value.</param>
244 /// <param name="value">The value to set.</param>
245 internal void SetValueFromRenderer(BindableProperty property, object value)
247 SetValueCore(property, value);
251 /// Sets the value of the propertyKey.
253 /// <param name="property">The BindablePropertyKey on which to assign a value.</param>
254 /// <param name="value">The value to set.</param>
255 internal void SetValueFromRenderer(BindablePropertyKey property, object value)
257 SetValueCore(property, value);
260 object INameScope.FindByName(string name)
262 INameScope namescope = GetNameScope();
263 if (namescope == null)
269 return namescope.FindByName(name);
273 void INameScope.RegisterName(string name, object scopedElement)
275 INameScope namescope = GetNameScope();
276 if (namescope == null)
277 throw new InvalidOperationException("this element is not in a namescope");
278 namescope.RegisterName(name, scopedElement);
282 void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
284 INameScope namescope = GetNameScope();
285 if (namescope == null)
286 throw new InvalidOperationException("this element is not in a namescope");
287 namescope.RegisterName(name, scopedElement, xmlLineInfo);
290 void INameScope.UnregisterName(string name)
292 INameScope namescope = GetNameScope();
293 if (namescope == null)
294 throw new InvalidOperationException("this element is not in a namescope");
295 namescope.UnregisterName(name);
298 internal event EventHandler<ElementEventArgs> ChildAdded;
300 internal event EventHandler<ElementEventArgs> ChildRemoved;
302 internal event EventHandler<ElementEventArgs> DescendantAdded;
304 internal event EventHandler<ElementEventArgs> DescendantRemoved;
307 /// Removes a previously set dynamic resource.
309 /// <param name="property">The BindableProperty from which to remove the DynamicResource.</param>
310 internal new void RemoveDynamicResource(BindableProperty property)
312 base.RemoveDynamicResource(property);
316 /// Sets the BindableProperty property of this element to be updated via the DynamicResource with the provided key.
318 /// <param name="property">The BindableProperty.</param>
319 /// <param name="key">The key of the DynamicResource</param>
320 internal new void SetDynamicResource(BindableProperty property, string key)
322 base.SetDynamicResource(property, key);
326 /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
328 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
329 [EditorBrowsable(EditorBrowsableState.Never)]
330 protected override void OnBindingContextChanged()
332 var gotBindingContext = false;
335 for (var index = 0; index < LogicalChildrenInternal.Count; index++)
337 Element child = LogicalChildrenInternal[index];
339 if (!gotBindingContext)
342 gotBindingContext = true;
345 SetChildInheritedBindingContext(child, bc);
348 if (bindableResources != null)
349 foreach (BindableObject item in bindableResources)
351 SetInheritedBindingContext(item, BindingContext);
354 base.OnBindingContextChanged();
358 /// Invoked whenever the ChildAdded event needs to be emitted.Implement this method to add class handling for this event.
360 /// <param name="child">The element that was added.</param>
361 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
362 /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
363 [EditorBrowsable(EditorBrowsableState.Never)]
364 protected virtual void OnChildAdded(Element child)
368 throw new ArgumentNullException(nameof(child));
373 child.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
375 ChildAdded?.Invoke(this, new ElementEventArgs(child));
377 OnDescendantAdded(child);
378 foreach (Element element in child.Descendants())
379 OnDescendantAdded(element);
383 /// Invoked whenever the ChildRemoved event needs to be emitted.Implement this method to add class handling for this event.
385 /// <param name="child">The element that was removed.</param>
386 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
387 /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
388 [EditorBrowsable(EditorBrowsableState.Never)]
389 protected virtual void OnChildRemoved(Element child)
393 throw new ArgumentNullException(nameof(child));
398 ChildRemoved?.Invoke(child, new ElementEventArgs(child));
400 OnDescendantRemoved(child);
401 foreach (Element element in child.Descendants())
402 OnDescendantRemoved(element);
406 /// 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.
408 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
409 [EditorBrowsable(EditorBrowsableState.Never)]
410 protected virtual void OnParentSet()
412 ParentSet?.Invoke(this, EventArgs.Empty);
413 // ApplyStyleSheetsOnParentSet();
417 /// Method that is called when a bound property is changed.
419 /// <param name="propertyName">The name of the bound property that changed.</param>
420 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
421 [EditorBrowsable(EditorBrowsableState.Never)]
422 protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
424 base.OnPropertyChanged(propertyName);
428 /// For internal use.
430 /// <returns>the elements</returns>
431 [EditorBrowsable(EditorBrowsableState.Never)]
432 public IEnumerable<Element> Descendants()
434 var queue = new Queue<Element>(16);
437 while (queue.Count > 0)
439 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
440 for (var i = 0; i < children.Count; i++)
442 Element child = children[i];
444 queue.Enqueue(child);
449 internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
451 OnParentResourcesChanged(e.Values);
454 internal virtual void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
456 OnResourcesChanged(values);
459 internal override void OnRemoveDynamicResource(BindableProperty property)
461 DynamicResources.Remove(property);
463 if (DynamicResources.Count == 0)
464 dynamicResources = null;
465 base.OnRemoveDynamicResource(property);
468 internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
470 OnResourcesChanged(e.Values);
473 internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
477 if (changeHandlers != null)
478 foreach (Action<object, ResourcesChangedEventArgs> handler in changeHandlers)
479 handler(this, new ResourcesChangedEventArgs(values));
480 if (dynamicResources == null)
482 if (bindableResources == null)
483 bindableResources = new List<BindableObject>();
484 foreach (KeyValuePair<string, object> value in values)
486 List<BindableProperty> changedResources = null;
487 foreach (KeyValuePair<BindableProperty, string> dynR in DynamicResources)
489 // when the DynamicResource bound to a BindableProperty is
490 // changing then the BindableProperty needs to be refreshed;
491 // The .Value is the name of DynamicResouce to which the BindableProperty is bound.
492 // The .Key is the name of the DynamicResource whose value is changing.
493 if (dynR.Value != value.Key)
495 changedResources = changedResources ?? new List<BindableProperty>();
496 changedResources.Add(dynR.Key);
498 if (changedResources == null)
500 foreach (BindableProperty changedResource in changedResources)
501 OnResourceChanged(changedResource, value.Value);
503 var bindableObject = value.Value as BindableObject;
504 if (bindableObject != null && (bindableObject as Element)?.Parent == null)
506 if (!bindableResources.Contains(bindableObject))
507 bindableResources.Add(bindableObject);
508 SetInheritedBindingContext(bindableObject, BindingContext);
513 internal override void OnSetDynamicResource(BindableProperty property, string key)
515 base.OnSetDynamicResource(property, key);
516 DynamicResources[property] = key;
518 if (this.TryGetResource(key, out value))
519 OnResourceChanged(property, value);
522 internal event EventHandler ParentSet;
524 internal virtual void SetChildInheritedBindingContext(Element child, object context)
526 SetInheritedBindingContext(child, context);
529 internal IEnumerable<Element> VisibleDescendants()
531 var queue = new Queue<Element>(16);
534 while (queue.Count > 0)
536 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
537 for (var i = 0; i < children.Count; i++)
539 var child = children[i] as BaseHandle;
545 queue.Enqueue(child);
550 INameScope GetNameScope()
552 INameScope namescope = NameScope.GetNameScope(this);
553 Element p = RealParent;
554 while (namescope == null && p != null)
556 namescope = NameScope.GetNameScope(p);
562 void OnDescendantAdded(Element child)
564 DescendantAdded?.Invoke(this, new ElementEventArgs(child));
566 if (RealParent != null)
567 RealParent.OnDescendantAdded(child);
570 void OnDescendantRemoved(Element child)
572 DescendantRemoved?.Invoke(this, new ElementEventArgs(child));
574 if (RealParent != null)
575 RealParent.OnDescendantRemoved(child);
578 void OnResourceChanged(BindableProperty property, object value)
580 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearTwoWayBindings);