2 * Copyright(c) 2021 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);
225 void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
227 if (changeHandlers == null)
229 changeHandlers.Remove(onchanged);
232 //void IElementController.SetValueFromRenderer(BindableProperty property, object value) => SetValueFromRenderer(property, value);
235 /// Sets the value of the specified property.
237 /// <param name="property">The BindableProperty on which to assign a value.</param>
238 /// <param name="value">The value to set.</param>
239 internal void SetValueFromRenderer(BindableProperty property, object value)
241 SetValueCore(property, value);
245 /// Sets the value of the propertyKey.
247 /// <param name="property">The BindablePropertyKey on which to assign a value.</param>
248 /// <param name="value">The value to set.</param>
249 internal void SetValueFromRenderer(BindablePropertyKey property, object value)
251 SetValueCore(property, value);
254 object INameScope.FindByName(string name)
256 INameScope namescope = GetNameScope();
257 if (namescope == null)
263 return namescope.FindByName(name);
267 void INameScope.RegisterName(string name, object scopedElement)
269 INameScope namescope = GetNameScope();
270 if (namescope == null)
271 throw new InvalidOperationException("this element is not in a namescope");
272 namescope.RegisterName(name, scopedElement);
276 void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
278 INameScope namescope = GetNameScope();
279 if (namescope == null)
280 throw new InvalidOperationException("this element is not in a namescope");
281 namescope.RegisterName(name, scopedElement, xmlLineInfo);
284 void INameScope.UnregisterName(string name)
286 INameScope namescope = GetNameScope();
287 if (namescope == null)
288 throw new InvalidOperationException("this element is not in a namescope");
289 namescope.UnregisterName(name);
292 internal event EventHandler<ElementEventArgs> ChildAdded;
294 internal event EventHandler<ElementEventArgs> ChildRemoved;
296 internal event EventHandler<ElementEventArgs> DescendantAdded;
298 internal event EventHandler<ElementEventArgs> DescendantRemoved;
301 /// Removes a previously set dynamic resource.
303 /// <param name="property">The BindableProperty from which to remove the DynamicResource.</param>
304 internal new void RemoveDynamicResource(BindableProperty property)
306 base.RemoveDynamicResource(property);
310 /// Sets the BindableProperty property of this element to be updated via the DynamicResource with the provided key.
312 /// <param name="property">The BindableProperty.</param>
313 /// <param name="key">The key of the DynamicResource</param>
314 internal new void SetDynamicResource(BindableProperty property, string key)
316 base.SetDynamicResource(property, key);
320 /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
322 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
323 [EditorBrowsable(EditorBrowsableState.Never)]
324 protected override void OnBindingContextChanged()
326 var gotBindingContext = false;
329 for (var index = 0; index < LogicalChildrenInternal.Count; index++)
331 Element child = LogicalChildrenInternal[index];
333 if (!gotBindingContext)
336 gotBindingContext = true;
339 SetChildInheritedBindingContext(child, bc);
342 if (bindableResources != null)
343 foreach (BindableObject item in bindableResources)
345 SetInheritedBindingContext(item, BindingContext);
348 base.OnBindingContextChanged();
352 /// Invoked whenever the ChildAdded event needs to be emitted.Implement this method to add class handling for this event.
354 /// <param name="child">The element that was added.</param>
355 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
356 /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
357 [EditorBrowsable(EditorBrowsableState.Never)]
358 protected virtual void OnChildAdded(Element child)
362 throw new ArgumentNullException(nameof(child));
367 child.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
369 ChildAdded?.Invoke(this, new ElementEventArgs(child));
371 OnDescendantAdded(child);
372 foreach (Element element in child.Descendants())
373 OnDescendantAdded(element);
377 /// Invoked whenever the ChildRemoved event needs to be emitted.Implement this method to add class handling for this event.
379 /// <param name="child">The element that was removed.</param>
380 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
381 /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
382 [EditorBrowsable(EditorBrowsableState.Never)]
383 protected virtual void OnChildRemoved(Element child)
387 throw new ArgumentNullException(nameof(child));
392 ChildRemoved?.Invoke(child, new ElementEventArgs(child));
394 OnDescendantRemoved(child);
395 foreach (Element element in child.Descendants())
396 OnDescendantRemoved(element);
400 /// 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.
402 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
403 [EditorBrowsable(EditorBrowsableState.Never)]
404 protected virtual void OnParentSet()
406 ParentSet?.Invoke(this, EventArgs.Empty);
407 // ApplyStyleSheetsOnParentSet();
411 /// Method that is called when a bound property is changed.
413 /// <param name="propertyName">The name of the bound property that changed.</param>
414 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
415 [EditorBrowsable(EditorBrowsableState.Never)]
416 protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
418 base.OnPropertyChanged(propertyName);
422 /// For internal use.
424 /// <returns>the elements</returns>
425 [EditorBrowsable(EditorBrowsableState.Never)]
426 public IEnumerable<Element> Descendants()
428 var queue = new Queue<Element>(16);
431 while (queue.Count > 0)
433 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
434 for (var i = 0; i < children.Count; i++)
436 Element child = children[i];
438 queue.Enqueue(child);
443 internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
445 // if (e == ResourcesChangedEventArgs.StyleSheets)
446 // // ApplyStyleSheetsOnParentSet();
448 // OnParentResourcesChanged(e.Values);
451 internal virtual void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
453 OnResourcesChanged(values);
456 internal override void OnRemoveDynamicResource(BindableProperty property)
458 DynamicResources.Remove(property);
460 if (DynamicResources.Count == 0)
461 dynamicResources = null;
462 base.OnRemoveDynamicResource(property);
465 internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
467 OnResourcesChanged(e.Values);
470 internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
474 if (changeHandlers != null)
475 foreach (Action<object, ResourcesChangedEventArgs> handler in changeHandlers)
476 handler(this, new ResourcesChangedEventArgs(values));
477 if (dynamicResources == null)
479 if (bindableResources == null)
480 bindableResources = new List<BindableObject>();
481 foreach (KeyValuePair<string, object> value in values)
483 List<BindableProperty> changedResources = null;
484 foreach (KeyValuePair<BindableProperty, string> dynR in DynamicResources)
486 // when the DynamicResource bound to a BindableProperty is
487 // changing then the BindableProperty needs to be refreshed;
488 // The .Value is the name of DynamicResouce to which the BindableProperty is bound.
489 // The .Key is the name of the DynamicResource whose value is changing.
490 if (dynR.Value != value.Key)
492 changedResources = changedResources ?? new List<BindableProperty>();
493 changedResources.Add(dynR.Key);
495 if (changedResources == null)
497 foreach (BindableProperty changedResource in changedResources)
498 OnResourceChanged(changedResource, value.Value);
500 var bindableObject = value.Value as BindableObject;
501 if (bindableObject != null && (bindableObject as Element)?.Parent == null)
503 if (!bindableResources.Contains(bindableObject))
504 bindableResources.Add(bindableObject);
505 SetInheritedBindingContext(bindableObject, BindingContext);
510 internal override void OnSetDynamicResource(BindableProperty property, string key)
512 base.OnSetDynamicResource(property, key);
513 DynamicResources[property] = key;
515 if (this.TryGetResource(key, out value))
516 OnResourceChanged(property, value);
518 Tizen.NUI.Application.AddResourceChangedCallback(this, (this as Element).OnResourcesChanged);
521 internal event EventHandler ParentSet;
523 internal virtual void SetChildInheritedBindingContext(Element child, object context)
525 SetInheritedBindingContext(child, context);
528 internal IEnumerable<Element> VisibleDescendants()
530 var queue = new Queue<Element>(16);
533 while (queue.Count > 0)
535 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
536 for (var i = 0; i < children.Count; i++)
538 var child = children[i] as BaseHandle;
544 queue.Enqueue(child);
549 INameScope GetNameScope()
551 INameScope namescope = NameScope.GetNameScope(this);
552 Element p = RealParent;
553 while (namescope == null && p != null)
555 namescope = NameScope.GetNameScope(p);
561 void OnDescendantAdded(Element child)
563 DescendantAdded?.Invoke(this, new ElementEventArgs(child));
565 if (RealParent != null)
566 RealParent.OnDescendantAdded(child);
569 void OnDescendantRemoved(Element child)
571 DescendantRemoved?.Invoke(this, new ElementEventArgs(child));
573 if (RealParent != null)
574 RealParent.OnDescendantRemoved(child);
577 void OnResourceChanged(BindableProperty property, object value)
579 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearTwoWayBindings);