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.ComponentModel;
21 using System.Diagnostics;
22 using System.Reflection;
23 using System.Runtime.CompilerServices;
24 using Tizen.NUI.Binding.Internals;
26 namespace Tizen.NUI.Binding
29 /// Provides a mechanism by which application developers can propagate changes that are made to data in one object to another.
31 /// <since_tizen> 9 </since_tizen>
32 public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
35 /// Implements the bound property whose interface is provided by the BindingContext property.
37 [EditorBrowsable(EditorBrowsableState.Never)]
38 public static readonly BindableProperty BindingContextProperty =
39 BindableProperty.Create(nameof(BindingContext), typeof(object), typeof(BindableObject), null, propertyChanged: (BindableProperty.BindingPropertyChangedDelegate)((bindable, oldValue, newValue) =>
41 var bindableObject = (BindableObject)bindable;
44 bindableObject.bindingContext = newValue;
45 bindableObject.FlushBinding();
48 defaultValueCreator: (BindableProperty.CreateDefaultValueDelegate)((bindable) =>
50 if (null != bindable.bindingContext)
52 return bindable.bindingContext;
55 if (bindable is Container container)
57 return container.Parent?.BindingContext;
65 readonly List<BindablePropertyContext> properties = new List<BindablePropertyContext>(4);
68 object inheritedContext;
70 private object bindingContext;
73 /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject.
75 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
76 [EditorBrowsable(EditorBrowsableState.Never)]
77 public object BindingContext
79 get { return inheritedContext ?? GetValue(BindingContextProperty); }
80 set { SetValue(BindingContextProperty, value); }
83 void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
85 SetDynamicResource(property, key, false);
89 /// Raised when a property has changed.
91 /// <since_tizen> 9 </since_tizen>
92 public event PropertyChangedEventHandler PropertyChanged;
94 /// <summary>Copy properties of other ViewStyle to this.</summary>
95 /// <param name="other">The other BindableProperty merge to this.</param>
96 [EditorBrowsable(EditorBrowsableState.Never)]
97 public virtual void CopyFrom(BindableObject other)
99 if (null == other) return;
101 Type type1 = this.GetType();
102 BindableProperty.GetBindablePropertysOfType(type1, out var nameToBindableProperty1);
104 Type type2 = other.GetType();
105 BindableProperty.GetBindablePropertysOfType(type2, out var nameToBindableProperty2);
107 if (null != nameToBindableProperty1)
109 foreach (KeyValuePair<string, BindableProperty> keyValuePair in nameToBindableProperty1)
111 nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
113 if (null != bindableProperty)
115 object value = other.GetValue(bindableProperty);
119 SetValue(keyValuePair.Value, value);
127 /// Raised whenever the BindingContext property changes.
129 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
130 [EditorBrowsable(EditorBrowsableState.Never)]
131 public event EventHandler BindingContextChanged;
133 internal void ClearValue(BindableProperty property, bool fromStyle)
135 ClearValue(property, fromStyle: fromStyle, checkAccess: true);
139 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue.
141 /// <param name="property">The BindableProperty to clear</param>
142 internal void ClearValue(BindableProperty property)
144 ClearValue(property, fromStyle: false, checkAccess: true);
148 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue for the property that is identified by propertyKey.
150 /// <param name="propertyKey">The BindablePropertyKey that identifies the BindableProperty to clear.</param>
151 internal void ClearValue(BindablePropertyKey propertyKey)
153 if (propertyKey == null)
154 throw new ArgumentNullException(nameof(propertyKey));
156 ClearValue(propertyKey.BindableProperty, fromStyle: false, checkAccess: false);
160 /// Return true if the target property exists and has been set.
162 /// <param name="targetProperty">The target property</param>
163 /// <returns>return true if the target property exists and has been set</returns>
164 internal bool IsSet(BindableProperty targetProperty)
166 if (targetProperty == null)
167 throw new ArgumentNullException(nameof(targetProperty));
169 var bpcontext = GetContext(targetProperty);
170 return bpcontext != null
171 && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0;
175 /// Returns the value that is contained the BindableProperty.
177 /// <param name="property">The BindableProperty for which to get the value.</param>
178 /// <returns>The value that is contained the BindableProperty</returns>
179 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
180 [EditorBrowsable(EditorBrowsableState.Never)]
181 public object GetValue(BindableProperty property)
183 if (property == null)
184 throw new ArgumentNullException(nameof(property));
186 BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
189 return property.DefaultValue;
191 return context.Value;
195 /// Raised when a property is about to change.
197 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
198 [EditorBrowsable(EditorBrowsableState.Never)]
199 public event PropertyChangingEventHandler PropertyChanging;
202 /// Removes a previously set binding.
204 /// <param name="property">The BindableProperty from which to remove bindings.</param>
205 internal void RemoveBinding(BindableProperty property)
207 if (property == null)
208 throw new ArgumentNullException(nameof(property));
210 BindablePropertyContext context = GetContext(property);
211 if (context == null || context.Binding == null)
214 RemoveBinding(property, context);
218 /// Assigns a binding to a property.
220 /// <param name="targetProperty">The BindableProperty on which to set a binding.</param>
221 /// <param name="binding">The binding to set.</param>
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 void SetBinding(BindableProperty targetProperty, BindingBase binding)
226 SetBinding(targetProperty, binding, false);
229 private bool isCreateByXaml = false;
230 /// Only used by the IL of xaml, will never changed to not hidden.
231 [EditorBrowsable(EditorBrowsableState.Never)]
232 public virtual bool IsCreateByXaml
236 return isCreateByXaml;
240 isCreateByXaml = value;
245 /// Sets the value of the specified property.
247 /// <param name="property">The BindableProperty on which to assign a value.</param>
248 /// <param name="value">The value to set.</param>
249 /// <exception cref="ArgumentNullException"> Thrown when property is null. </exception>
250 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
251 [EditorBrowsable(EditorBrowsableState.Never)]
252 public void SetValue(BindableProperty property, object value)
254 if (true == isCreateByXaml)
256 SetValue(property, value, false, true);
260 if (null == property)
262 throw new ArgumentNullException(nameof(property));
264 property.PropertyChanged?.Invoke(this, null, value);
266 OnPropertyChanged(property.PropertyName);
267 OnPropertyChangedWithData(property);
271 internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
273 if (property == null)
274 throw new ArgumentNullException(nameof(property));
276 if (true == isCreateByXaml)
278 if (property.IsReadOnly)
279 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
281 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
282 SetValuePrivateFlags.ManuallySet | SetValuePrivateFlags.CheckAccess, true);
286 property.PropertyChanged?.Invoke(this, null, value);
291 /// Sets the value of the propertyKey.
293 /// <param name="propertyKey">The BindablePropertyKey on which to assign a value.</param>
294 /// <param name="value">The value to set.</param>
295 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
296 [EditorBrowsable(EditorBrowsableState.Never)]
297 public void SetValue(BindablePropertyKey propertyKey, object value)
299 if (propertyKey == null)
300 throw new ArgumentNullException(nameof(propertyKey));
302 SetValue(propertyKey.BindableProperty, value, false, false);
306 /// Set the inherited context to a neated element.
308 /// <param name="bindable">The object on which to set the inherited binding context.</param>
309 /// <param name="value">The inherited context to set.</param>
310 /// <exception cref="ArgumentNullException"> Thrown when bindable is null. </exception>
311 [EditorBrowsable(EditorBrowsableState.Never)]
312 public static void SetInheritedBindingContext(BindableObject bindable, object value)
314 if (null == bindable)
316 throw new ArgumentNullException(nameof(bindable));
319 BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
320 if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
323 object oldContext = bindable.inheritedContext;
325 if (ReferenceEquals(oldContext, value))
328 if (bpContext != null && oldContext == null)
329 oldContext = bpContext.Value;
331 if (bpContext != null && bpContext.Binding != null)
333 bpContext.Binding.Context = value;
334 bindable.inheritedContext = null;
338 bindable.inheritedContext = value;
341 bindable.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
342 bindable.OnBindingContextChanged();
346 /// Register the properties which can effect each other in Binding to same group.
348 [EditorBrowsable(EditorBrowsableState.Never)]
349 public static void RegisterPropertyGroup(BindableProperty property, HashSet<BindableProperty> group)
351 if (!PropertyToGroup.ContainsKey(property))
353 PropertyToGroup.Add(property, group);
356 if (null != group && !(group.Contains(property)))
362 /// Apply the bindings to BindingContext.
364 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
365 [EditorBrowsable(EditorBrowsableState.Never)]
366 protected void ApplyBindings()
368 ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false);
372 /// Override this method to execute an action when the BindingContext changes.
374 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
375 [EditorBrowsable(EditorBrowsableState.Never)]
376 protected virtual void OnBindingContextChanged()
378 BindingContextChanged?.Invoke(this, EventArgs.Empty);
382 /// Call this method from a child class to notify that a change happened on a property.
384 /// <param name="propertyName">The name of the property that changed.</param>
385 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
386 [EditorBrowsable(EditorBrowsableState.Never)]
387 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
388 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
391 /// Call this method from a child class to notify that a change is going to happen on a property.
393 /// <param name="propertyName">The name of the property that is changing.</param>
394 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
395 [EditorBrowsable(EditorBrowsableState.Never)]
396 protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
397 => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
400 /// Method that is called when a bound property is changed.
402 [EditorBrowsable(EditorBrowsableState.Never)]
403 protected virtual void OnPropertyChangedWithData(BindableProperty prop) { }
406 /// Unapplies all previously set bindings.
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 void UnapplyBindings()
412 for (int i = 0, _propertiesCount = properties.Count; i < _propertiesCount; i++)
414 BindablePropertyContext context = properties[i];
415 if (context.Binding == null)
418 context.Binding.Unapply();
422 internal bool GetIsBound(BindableProperty targetProperty)
424 if (targetProperty == null)
425 throw new ArgumentNullException(nameof(targetProperty));
427 BindablePropertyContext bpcontext = GetContext(targetProperty);
428 return bpcontext != null && bpcontext.Binding != null;
432 /// Returns the value that is contained the BindableProperty.
434 /// <param name="property0">The BindableProperty instance.</param>
435 /// <param name="property1">The BindableProperty instance.</param>
436 /// <returns>The value that is contained the BindableProperty</returns>
437 internal object[] GetValues(BindableProperty property0, BindableProperty property1)
439 var values = new object[2];
441 for (var i = 0; i < properties.Count; i++)
443 BindablePropertyContext context = properties[i];
445 if (ReferenceEquals(context.Property, property0))
447 values[0] = context.Value;
450 else if (ReferenceEquals(context.Property, property1))
452 values[1] = context.Value;
456 if (property0 == null && property1 == null)
460 if (!ReferenceEquals(property0, null))
461 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
462 if (!ReferenceEquals(property1, null))
463 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
469 /// Returns the value that is contained the BindableProperty.
471 /// <param name="property0">The BindableProperty instance.</param>
472 /// <param name="property1">The BindableProperty instance.</param>
473 /// <param name="property2">The BindableProperty instance.</param>
474 /// <returns>The value that is contained the BindableProperty</returns>
475 internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
477 var values = new object[3];
479 for (var i = 0; i < properties.Count; i++)
481 BindablePropertyContext context = properties[i];
483 if (ReferenceEquals(context.Property, property0))
485 values[0] = context.Value;
488 else if (ReferenceEquals(context.Property, property1))
490 values[1] = context.Value;
493 else if (ReferenceEquals(context.Property, property2))
495 values[2] = context.Value;
499 if (property0 == null && property1 == null && property2 == null)
503 if (!ReferenceEquals(property0, null))
504 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
505 if (!ReferenceEquals(property1, null))
506 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
507 if (!ReferenceEquals(property2, null))
508 values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
514 /// Returns the value that is contained the BindableProperty.
516 /// <param name="properties">The array of the BindableProperty instances</param>
517 /// <returns>The values that is contained the BindableProperty instances.</returns>
518 internal object[] GetValues(params BindableProperty[] properties)
520 var values = new object[properties.Length];
521 for (var i = 0; i < this.properties.Count; i++)
523 var context = this.properties[i];
524 var index = properties.IndexOf(context.Property);
527 values[index] = context.Value;
529 for (var i = 0; i < values.Length; i++)
531 if (!ReferenceEquals(values[i], null))
533 values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value;
538 internal virtual void OnRemoveDynamicResource(BindableProperty property)
542 internal virtual void OnSetDynamicResource(BindableProperty property, string key)
546 internal void RemoveDynamicResource(BindableProperty property)
548 if (property == null)
549 throw new ArgumentNullException(nameof(property));
551 OnRemoveDynamicResource(property);
552 BindablePropertyContext context = GetOrCreateContext(property);
553 context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
556 internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
558 if (targetProperty == null)
559 throw new ArgumentNullException(nameof(targetProperty));
561 throw new ArgumentNullException(nameof(binding));
563 if (fromStyle && !CanBeSetFromStyle(targetProperty))
566 IsCreateByXaml = true;
568 var context = GetOrCreateContext(targetProperty);
570 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
572 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
574 if (context.Binding != null)
575 context.Binding.Unapply();
577 BindingBase oldBinding = context.Binding;
578 context.Binding = binding;
580 targetProperty.BindingChanging?.Invoke(this, oldBinding, binding);
582 binding.Apply(BindingContext, this, targetProperty);
585 bool CanBeSetFromStyle(BindableProperty property)
587 var context = GetContext(property);
590 if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle)
592 if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue)
594 if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated)
599 internal void SetDynamicResource(BindableProperty property, string key)
601 SetDynamicResource(property, key, false);
604 internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
606 if (property == null)
607 throw new ArgumentNullException(nameof(property));
608 if (string.IsNullOrEmpty(key))
609 throw new ArgumentNullException(nameof(key));
610 if (fromStyle && !CanBeSetFromStyle(property))
613 var context = GetOrCreateContext(property);
615 context.Attributes |= BindableContextAttributes.IsDynamicResource;
617 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
619 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
621 OnSetDynamicResource(property, key);
624 internal void SetValue(BindableProperty property, object value, bool fromStyle)
626 SetValue(property, value, fromStyle, true);
629 internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
631 SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None, false);
635 /// For internal use.
637 /// <param name="property">The BindableProperty on which to assign a value.</param>
638 /// <param name="value">The value to set</param>
639 /// <param name="attributes">The set value flag</param>
640 [EditorBrowsable(EditorBrowsableState.Never)]
641 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
643 SetValueCore(property, value, attributes, SetValuePrivateFlags.Default, false);
646 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, bool forceSendChangeSignal)
648 bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
649 bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
650 bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
651 bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
652 bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
654 if (property == null)
655 throw new ArgumentNullException(nameof(property));
656 if (checkAccess && property.IsReadOnly)
658 Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
662 if (!converted && !property.TryConvert(ref value))
664 Console.WriteLine($"SetValue : Can not convert {value} to type {property.ReturnType}");
668 if (property.ValidateValue != null && !property.ValidateValue(this, value))
669 throw new ArgumentException("Value was an invalid value for " + property.PropertyName, nameof(value));
671 if (property.CoerceValue != null)
672 value = property.CoerceValue(this, value);
674 BindablePropertyContext context = GetOrCreateContext(property);
677 context.Attributes |= BindableContextAttributes.IsManuallySet;
678 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
682 context.Attributes &= ~BindableContextAttributes.IsManuallySet;
687 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
689 // else omitted on purpose
691 bool currentlyApplying = applying;
693 if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
695 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
696 if (delayQueue == null)
697 context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
699 delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
703 context.Attributes |= BindableContextAttributes.IsBeingSet;
704 SetValueActual(property, context, value, currentlyApplying, forceSendChangeSignal, attributes, silent);
706 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
707 if (delayQueue != null)
709 while (delayQueue.Count > 0)
711 SetValueArgs s = delayQueue.Dequeue();
712 SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, forceSendChangeSignal, s.Attributes);
715 context.DelayedSetters = null;
718 context.Attributes &= ~BindableContextAttributes.IsBeingSet;
722 internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
724 var prop = properties.ToArray();
725 for (int i = 0, propLength = prop.Length; i < propLength; i++)
727 BindablePropertyContext context = prop[i];
728 BindingBase binding = context.Binding;
732 if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty))
735 binding.Unapply(fromBindingContextChanged: fromBindingContextChanged);
736 binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged);
740 static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
742 object context = bindable.inheritedContext;
743 var oldBinding = oldBindingBase as Binding;
744 var newBinding = newBindingBase as Binding;
746 if (context == null && oldBinding != null)
747 context = oldBinding.Context;
748 if (context != null && newBinding != null)
749 newBinding.Context = context;
752 void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
754 if (property == null)
755 throw new ArgumentNullException(nameof(property));
757 if (checkAccess && property.IsReadOnly)
758 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
760 BindablePropertyContext bpcontext = GetContext(property);
761 if (bpcontext == null)
764 if (fromStyle && !CanBeSetFromStyle(property))
767 object original = bpcontext.Value;
769 object newValue = property.GetDefaultValue(this);
771 bool same = Equals(original, newValue);
774 property.PropertyChanging?.Invoke(this, original, newValue);
776 OnPropertyChanging(property.PropertyName);
779 bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
780 bpcontext.Value = newValue;
781 if (property.DefaultValueCreator == null)
782 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
784 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated;
788 OnPropertyChanged(property.PropertyName);
789 OnPropertyChangedWithData(property);
790 property.PropertyChanged?.Invoke(this, original, newValue);
794 [MethodImpl(MethodImplOptions.AggressiveInlining)]
795 BindablePropertyContext CreateAndAddContext(BindableProperty property)
797 var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
799 if (property.DefaultValueCreator == null)
800 context.Attributes = BindableContextAttributes.IsDefaultValue;
802 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
804 properties.Add(context);
808 [MethodImpl(MethodImplOptions.AggressiveInlining)]
809 BindablePropertyContext GetContext(BindableProperty property)
811 List<BindablePropertyContext> propertyList = properties;
813 for (var i = 0; i < propertyList.Count; i++)
815 BindablePropertyContext context = propertyList[i];
816 if (ReferenceEquals(context.Property, property))
823 [MethodImpl(MethodImplOptions.AggressiveInlining)]
824 BindablePropertyContext GetOrCreateContext(BindableProperty property)
826 BindablePropertyContext context = GetContext(property);
829 context = CreateAndAddContext(property);
831 else if (property.DefaultValueCreator != null)
833 context.Value = property.DefaultValueCreator(this); //Update Value from dali
839 void RemoveBinding(BindableProperty property, BindablePropertyContext context)
841 context.Binding.Unapply();
843 property.BindingChanging?.Invoke(this, context.Binding, null);
845 context.Binding = null;
848 void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
850 if (property == null)
851 throw new ArgumentNullException(nameof(property));
853 if (checkAccess && property.IsReadOnly)
854 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
856 if (fromStyle && !CanBeSetFromStyle(property))
859 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
860 (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0),
864 void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, bool forceSendChangeSignal, SetValueFlags attributes, bool silent = false)
866 object original = context.Value;
867 bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
868 bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
869 bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
870 bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
872 bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
873 if (!silent && (!same || raiseOnEqual))
875 property.PropertyChanging?.Invoke(this, original, value);
877 OnPropertyChanging(property.PropertyName);
880 if (!same || raiseOnEqual)
882 context.Value = value;
885 context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
886 context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated;
888 if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
889 RemoveDynamicResource(property);
891 BindingBase binding = context.Binding;
894 if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
896 RemoveBinding(property, context);
901 PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
905 if ((!same || raiseOnEqual))
907 property.PropertyChanged?.Invoke(this, original, value);
909 if (binding != null && !currentlyApplying)
916 OnPropertyChanged(property.PropertyName);
918 if (null != propertyGroup)
920 foreach (var relativeProperty in propertyGroup)
922 if (relativeProperty != property)
924 var relativeContext = GetOrCreateContext(relativeProperty);
925 var relativeBinding = relativeContext.Binding;
927 if (null != relativeBinding)
930 relativeBinding.Apply(true);
934 OnPropertyChanged(relativeProperty.PropertyName);
939 else if (true == same && true == forceSendChangeSignal)
941 if (binding != null && !currentlyApplying)
948 OnPropertyChanged(property.PropertyName);
950 if (null != propertyGroup)
952 foreach (var relativeProperty in propertyGroup)
954 if (relativeProperty != property)
956 var relativeContext = GetOrCreateContext(relativeProperty);
957 var relativeBinding = relativeContext.Binding;
959 if (null != relativeBinding)
962 relativeBinding.Apply(true);
966 OnPropertyChanged(relativeProperty.PropertyName);
972 OnPropertyChangedWithData(property);
976 private static Dictionary<BindableProperty, HashSet<BindableProperty>> PropertyToGroup { get; }
977 = new Dictionary<BindableProperty, HashSet<BindableProperty>>();
980 enum BindableContextAttributes
982 IsManuallySet = 1 << 0,
984 IsDynamicResource = 1 << 2,
985 IsSetFromStyle = 1 << 3,
986 IsDefaultValue = 1 << 4,
987 IsDefaultValueCreated = 1 << 5,
990 class BindablePropertyContext
992 public BindableContextAttributes Attributes;
993 public BindingBase Binding;
994 public Queue<SetValueArgs> DelayedSetters;
995 public BindableProperty Property;
1000 internal enum SetValuePrivateFlags
1003 CheckAccess = 1 << 0,
1005 ManuallySet = 1 << 2,
1008 Default = CheckAccess
1013 public readonly SetValueFlags Attributes;
1014 public readonly BindablePropertyContext Context;
1015 public readonly bool CurrentlyApplying;
1016 public readonly BindableProperty Property;
1017 public readonly object Value;
1019 public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
1021 Property = property;
1024 CurrentlyApplying = currentlyApplying;
1025 Attributes = attributes;
1029 internal void AddChildBindableObject(BindableObject child)
1033 children.Add(child);
1034 child.FlushBinding();
1038 internal void RemoveChildBindableObject(BindableObject child)
1040 children.Remove(child);
1043 private List<BindableObject> children = new List<BindableObject>();
1045 private void FlushBinding()
1047 ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
1048 OnBindingContextChanged();
1050 foreach (var child in children)
1052 child.FlushBinding();
1058 namespace Tizen.NUI.Binding.Internals
1061 /// SetValueFlags. For internal use.
1064 [EditorBrowsable(EditorBrowsableState.Never)]
1065 public enum SetValueFlags
1070 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1071 [EditorBrowsable(EditorBrowsableState.Never)]
1075 /// Clear OneWay bindings.
1077 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1078 [EditorBrowsable(EditorBrowsableState.Never)]
1079 ClearOneWayBindings = 1 << 0,
1082 /// Clear TwoWay bindings.
1084 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1085 [EditorBrowsable(EditorBrowsableState.Never)]
1086 ClearTwoWayBindings = 1 << 1,
1089 /// Clear dynamic resource.
1091 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1092 [EditorBrowsable(EditorBrowsableState.Never)]
1093 ClearDynamicResource = 1 << 2,
1098 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1099 [EditorBrowsable(EditorBrowsableState.Never)]
1100 RaiseOnEqual = 1 << 3