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();
47 if (newValue is BindableObject targetBindableObject)
49 targetBindableObject.IsBinded = true;
53 defaultValueCreator: (BindableProperty.CreateDefaultValueDelegate)((bindable) =>
55 if (null != bindable.bindingContext)
57 return bindable.bindingContext;
60 if (bindable is Container container)
62 return container.Parent?.BindingContext;
70 readonly List<BindablePropertyContext> properties = new List<BindablePropertyContext>(4);
73 object inheritedContext;
75 private object bindingContext;
78 /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject.
80 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
81 [EditorBrowsable(EditorBrowsableState.Never)]
82 public object BindingContext
84 get { return inheritedContext ?? GetValue(BindingContextProperty); }
85 set { SetValue(BindingContextProperty, value); }
88 void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
90 SetDynamicResource(property, key, false);
94 /// Raised when a property has changed.
96 /// <since_tizen> 9 </since_tizen>
97 public event PropertyChangedEventHandler PropertyChanged;
99 /// <summary>Copy properties of other ViewStyle to this.</summary>
100 /// <param name="other">The other BindableProperty merge to this.</param>
101 [EditorBrowsable(EditorBrowsableState.Never)]
102 public virtual void CopyFrom(BindableObject other)
104 if (null == other) return;
106 Type type1 = this.GetType();
107 BindableProperty.GetBindablePropertysOfType(type1, out var nameToBindableProperty1);
109 Type type2 = other.GetType();
110 BindableProperty.GetBindablePropertysOfType(type2, out var nameToBindableProperty2);
112 if (null != nameToBindableProperty1)
114 foreach (KeyValuePair<string, BindableProperty> keyValuePair in nameToBindableProperty1)
116 nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
118 if (null != bindableProperty)
120 object value = other.GetValue(bindableProperty);
124 SetValue(keyValuePair.Value, value);
132 /// Raised whenever the BindingContext property changes.
134 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
135 [EditorBrowsable(EditorBrowsableState.Never)]
136 public event EventHandler BindingContextChanged;
138 internal void ClearValue(BindableProperty property, bool fromStyle)
140 ClearValue(property, fromStyle: fromStyle, checkAccess: true);
144 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue.
146 /// <param name="property">The BindableProperty to clear</param>
147 internal void ClearValue(BindableProperty property)
149 ClearValue(property, fromStyle: false, checkAccess: true);
153 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue for the property that is identified by propertyKey.
155 /// <param name="propertyKey">The BindablePropertyKey that identifies the BindableProperty to clear.</param>
156 internal void ClearValue(BindablePropertyKey propertyKey)
158 if (propertyKey == null)
159 throw new ArgumentNullException(nameof(propertyKey));
161 ClearValue(propertyKey.BindableProperty, fromStyle: false, checkAccess: false);
165 /// Return true if the target property exists and has been set.
167 /// <param name="targetProperty">The target property</param>
168 /// <returns>return true if the target property exists and has been set</returns>
169 internal bool IsSet(BindableProperty targetProperty)
171 if (targetProperty == null)
172 throw new ArgumentNullException(nameof(targetProperty));
174 var bpcontext = GetContext(targetProperty);
175 return bpcontext != null
176 && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0;
180 /// Returns the value that is contained the BindableProperty.
182 /// <param name="property">The BindableProperty for which to get the value.</param>
183 /// <returns>The value that is contained the BindableProperty</returns>
184 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
185 [EditorBrowsable(EditorBrowsableState.Never)]
186 public object GetValue(BindableProperty property)
188 if (property == null)
189 throw new ArgumentNullException(nameof(property));
191 if (!IsBinded && property.DefaultValueCreator != null)
193 return property.DefaultValueCreator(this);
196 BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
199 return property.DefaultValue;
201 return context.Value;
205 /// Raised when a property is about to change.
207 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
208 [EditorBrowsable(EditorBrowsableState.Never)]
209 public event PropertyChangingEventHandler PropertyChanging;
212 /// Removes a previously set binding.
214 /// <param name="property">The BindableProperty from which to remove bindings.</param>
215 internal void RemoveBinding(BindableProperty property)
217 if (property == null)
218 throw new ArgumentNullException(nameof(property));
220 BindablePropertyContext context = GetContext(property);
221 if (context == null || context.Binding == null)
224 RemoveBinding(property, context);
228 /// Assigns a binding to a property.
230 /// <param name="targetProperty">The BindableProperty on which to set a binding.</param>
231 /// <param name="binding">The binding to set.</param>
232 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
233 [EditorBrowsable(EditorBrowsableState.Never)]
234 public void SetBinding(BindableProperty targetProperty, BindingBase binding)
236 SetBinding(targetProperty, binding, false);
239 private bool isCreateByXaml = false;
240 /// Only used by the IL of xaml, will never changed to not hidden.
241 [EditorBrowsable(EditorBrowsableState.Never)]
242 public virtual bool IsCreateByXaml
246 return isCreateByXaml;
250 isCreateByXaml = value;
255 /// Sets the value of the specified property.
257 /// <param name="property">The BindableProperty on which to assign a value.</param>
258 /// <param name="value">The value to set.</param>
259 /// <exception cref="ArgumentNullException"> Thrown when property is null. </exception>
260 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
261 [EditorBrowsable(EditorBrowsableState.Never)]
262 public void SetValue(BindableProperty property, object value)
264 if (true == IsBinded)
266 SetValue(property, value, false, true);
270 if (null == property)
272 throw new ArgumentNullException(nameof(property));
275 object oldvalue = null;
276 if (null == property.DefaultValueCreator)
278 BindablePropertyContext context = GetOrCreateContext(property);
281 oldvalue = context.Value;
282 context.Value = value;
287 oldvalue = property.DefaultValueCreator.Invoke(this);
290 property.PropertyChanged?.Invoke(this, oldvalue, value);
292 OnPropertyChanged(property.PropertyName);
293 OnPropertyChangedWithData(property);
297 internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
299 if (property == null)
300 throw new ArgumentNullException(nameof(property));
302 if (true == isCreateByXaml)
304 if (property.IsReadOnly)
305 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
307 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
308 SetValuePrivateFlags.ManuallySet | SetValuePrivateFlags.CheckAccess, true);
312 property.PropertyChanged?.Invoke(this, null, value);
317 /// Sets the value of the propertyKey.
319 /// <param name="propertyKey">The BindablePropertyKey on which to assign a value.</param>
320 /// <param name="value">The value to set.</param>
321 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
322 [EditorBrowsable(EditorBrowsableState.Never)]
323 public void SetValue(BindablePropertyKey propertyKey, object value)
325 if (propertyKey == null)
326 throw new ArgumentNullException(nameof(propertyKey));
328 SetValue(propertyKey.BindableProperty, value, false, false);
332 /// Set the inherited context to a neated element.
334 /// <param name="bindable">The object on which to set the inherited binding context.</param>
335 /// <param name="value">The inherited context to set.</param>
336 /// <exception cref="ArgumentNullException"> Thrown when bindable is null. </exception>
337 [EditorBrowsable(EditorBrowsableState.Never)]
338 public static void SetInheritedBindingContext(BindableObject bindable, object value)
340 if (null == bindable)
342 throw new ArgumentNullException(nameof(bindable));
345 BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
346 if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
349 object oldContext = bindable.inheritedContext;
351 if (ReferenceEquals(oldContext, value))
354 if (bpContext != null && oldContext == null)
355 oldContext = bpContext.Value;
357 if (bpContext != null && bpContext.Binding != null)
359 bpContext.Binding.Context = value;
360 bindable.inheritedContext = null;
364 bindable.inheritedContext = value;
367 bindable.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
368 bindable.OnBindingContextChanged();
372 /// Register the properties which can effect each other in Binding to same group.
374 [EditorBrowsable(EditorBrowsableState.Never)]
375 public static void RegisterPropertyGroup(BindableProperty property, HashSet<BindableProperty> group)
377 if (!PropertyToGroup.ContainsKey(property))
379 PropertyToGroup.Add(property, group);
382 if (null != group && !(group.Contains(property)))
388 /// Apply the bindings to BindingContext.
390 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
391 [EditorBrowsable(EditorBrowsableState.Never)]
392 protected void ApplyBindings()
394 ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false);
398 /// Override this method to execute an action when the BindingContext changes.
400 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
401 [EditorBrowsable(EditorBrowsableState.Never)]
402 protected virtual void OnBindingContextChanged()
404 BindingContextChanged?.Invoke(this, EventArgs.Empty);
408 /// Call this method from a child class to notify that a change happened on a property.
410 /// <param name="propertyName">The name of the property that changed.</param>
411 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
412 [EditorBrowsable(EditorBrowsableState.Never)]
413 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
414 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
417 /// Call this method from a child class to notify that a change is going to happen on a property.
419 /// <param name="propertyName">The name of the property that is changing.</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 virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
423 => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
426 /// Method that is called when a bound property is changed.
428 [EditorBrowsable(EditorBrowsableState.Never)]
429 protected virtual void OnPropertyChangedWithData(BindableProperty prop) { }
432 /// Unapplies all previously set bindings.
434 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
435 [EditorBrowsable(EditorBrowsableState.Never)]
436 protected void UnapplyBindings()
438 for (int i = 0, _propertiesCount = properties.Count; i < _propertiesCount; i++)
440 BindablePropertyContext context = properties[i];
441 if (context.Binding == null)
444 context.Binding.Unapply();
448 internal bool GetIsBound(BindableProperty targetProperty)
450 if (targetProperty == null)
451 throw new ArgumentNullException(nameof(targetProperty));
453 BindablePropertyContext bpcontext = GetContext(targetProperty);
454 return bpcontext != null && bpcontext.Binding != null;
458 /// Returns the value that is contained the BindableProperty.
460 /// <param name="property0">The BindableProperty instance.</param>
461 /// <param name="property1">The BindableProperty instance.</param>
462 /// <returns>The value that is contained the BindableProperty</returns>
463 internal object[] GetValues(BindableProperty property0, BindableProperty property1)
465 var values = new object[2];
467 for (var i = 0; i < properties.Count; i++)
469 BindablePropertyContext context = properties[i];
471 if (ReferenceEquals(context.Property, property0))
473 values[0] = context.Value;
476 else if (ReferenceEquals(context.Property, property1))
478 values[1] = context.Value;
482 if (property0 == null && property1 == null)
486 if (!ReferenceEquals(property0, null))
487 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
488 if (!ReferenceEquals(property1, null))
489 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
495 /// Returns the value that is contained the BindableProperty.
497 /// <param name="property0">The BindableProperty instance.</param>
498 /// <param name="property1">The BindableProperty instance.</param>
499 /// <param name="property2">The BindableProperty instance.</param>
500 /// <returns>The value that is contained the BindableProperty</returns>
501 internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
503 var values = new object[3];
505 for (var i = 0; i < properties.Count; i++)
507 BindablePropertyContext context = properties[i];
509 if (ReferenceEquals(context.Property, property0))
511 values[0] = context.Value;
514 else if (ReferenceEquals(context.Property, property1))
516 values[1] = context.Value;
519 else if (ReferenceEquals(context.Property, property2))
521 values[2] = context.Value;
525 if (property0 == null && property1 == null && property2 == null)
529 if (!ReferenceEquals(property0, null))
530 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
531 if (!ReferenceEquals(property1, null))
532 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
533 if (!ReferenceEquals(property2, null))
534 values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
540 /// Returns the value that is contained the BindableProperty.
542 /// <param name="properties">The array of the BindableProperty instances</param>
543 /// <returns>The values that is contained the BindableProperty instances.</returns>
544 internal object[] GetValues(params BindableProperty[] properties)
546 var values = new object[properties.Length];
547 for (var i = 0; i < this.properties.Count; i++)
549 var context = this.properties[i];
550 var index = properties.IndexOf(context.Property);
553 values[index] = context.Value;
555 for (var i = 0; i < values.Length; i++)
557 if (!ReferenceEquals(values[i], null))
559 values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value;
564 internal virtual void OnRemoveDynamicResource(BindableProperty property)
568 internal virtual void OnSetDynamicResource(BindableProperty property, string key)
572 internal void RemoveDynamicResource(BindableProperty property)
574 if (property == null)
575 throw new ArgumentNullException(nameof(property));
577 OnRemoveDynamicResource(property);
578 BindablePropertyContext context = GetOrCreateContext(property);
579 context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
582 internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
584 if (targetProperty == null)
585 throw new ArgumentNullException(nameof(targetProperty));
587 throw new ArgumentNullException(nameof(binding));
589 if (fromStyle && !CanBeSetFromStyle(targetProperty))
594 var context = GetOrCreateContext(targetProperty);
596 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
598 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
600 if (context.Binding != null)
601 context.Binding.Unapply();
603 BindingBase oldBinding = context.Binding;
604 context.Binding = binding;
606 targetProperty.BindingChanging?.Invoke(this, oldBinding, binding);
608 binding.Apply(BindingContext, this, targetProperty);
611 bool CanBeSetFromStyle(BindableProperty property)
613 var context = GetContext(property);
616 if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle)
618 if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue)
620 if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated)
625 internal void SetDynamicResource(BindableProperty property, string key)
627 SetDynamicResource(property, key, false);
630 internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
632 if (property == null)
633 throw new ArgumentNullException(nameof(property));
634 if (string.IsNullOrEmpty(key))
635 throw new ArgumentNullException(nameof(key));
636 if (fromStyle && !CanBeSetFromStyle(property))
639 var context = GetOrCreateContext(property);
641 context.Attributes |= BindableContextAttributes.IsDynamicResource;
643 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
645 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
647 OnSetDynamicResource(property, key);
650 internal void SetValue(BindableProperty property, object value, bool fromStyle)
652 SetValue(property, value, fromStyle, true);
655 internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
657 SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None, false);
661 /// For internal use.
663 /// <param name="property">The BindableProperty on which to assign a value.</param>
664 /// <param name="value">The value to set</param>
665 /// <param name="attributes">The set value flag</param>
666 [EditorBrowsable(EditorBrowsableState.Never)]
667 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
669 SetValueCore(property, value, attributes, SetValuePrivateFlags.Default, false);
672 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, bool forceSendChangeSignal)
674 bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
675 bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
676 bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
677 bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
678 bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
680 if (property == null)
681 throw new ArgumentNullException(nameof(property));
682 if (checkAccess && property.IsReadOnly)
684 Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
688 if (!converted && !property.TryConvert(ref value))
690 Console.WriteLine($"SetValue : Can not convert {value} to type {property.ReturnType}");
694 if (property.ValidateValue != null && !property.ValidateValue(this, value))
695 throw new ArgumentException("Value was an invalid value for " + property.PropertyName, nameof(value));
697 if (property.CoerceValue != null)
698 value = property.CoerceValue(this, value);
700 BindablePropertyContext context = GetOrCreateContext(property);
703 context.Attributes |= BindableContextAttributes.IsManuallySet;
704 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
708 context.Attributes &= ~BindableContextAttributes.IsManuallySet;
713 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
715 // else omitted on purpose
717 bool currentlyApplying = applying;
719 if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
721 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
722 if (delayQueue == null)
723 context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
725 delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
729 context.Attributes |= BindableContextAttributes.IsBeingSet;
730 SetValueActual(property, context, value, currentlyApplying, forceSendChangeSignal, attributes, silent);
732 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
733 if (delayQueue != null)
735 while (delayQueue.Count > 0)
737 SetValueArgs s = delayQueue.Dequeue();
738 SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, forceSendChangeSignal, s.Attributes);
741 context.DelayedSetters = null;
744 context.Attributes &= ~BindableContextAttributes.IsBeingSet;
748 internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
750 var prop = properties.ToArray();
751 for (int i = 0, propLength = prop.Length; i < propLength; i++)
753 BindablePropertyContext context = prop[i];
754 BindingBase binding = context.Binding;
758 if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty))
761 binding.Unapply(fromBindingContextChanged: fromBindingContextChanged);
762 binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged);
766 internal bool IsBinded
772 static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
774 object context = bindable.inheritedContext;
775 var oldBinding = oldBindingBase as Binding;
776 var newBinding = newBindingBase as Binding;
778 if (context == null && oldBinding != null)
779 context = oldBinding.Context;
780 if (context != null && newBinding != null)
781 newBinding.Context = context;
784 void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
786 if (property == null)
787 throw new ArgumentNullException(nameof(property));
789 if (checkAccess && property.IsReadOnly)
790 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
792 BindablePropertyContext bpcontext = GetContext(property);
793 if (bpcontext == null)
796 if (fromStyle && !CanBeSetFromStyle(property))
799 object original = bpcontext.Value;
801 object newValue = property.GetDefaultValue(this);
803 bool same = Equals(original, newValue);
806 property.PropertyChanging?.Invoke(this, original, newValue);
808 OnPropertyChanging(property.PropertyName);
811 bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
812 bpcontext.Value = newValue;
813 if (property.DefaultValueCreator == null)
814 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
816 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated;
820 OnPropertyChanged(property.PropertyName);
821 OnPropertyChangedWithData(property);
822 property.PropertyChanged?.Invoke(this, original, newValue);
826 [MethodImpl(MethodImplOptions.AggressiveInlining)]
827 BindablePropertyContext CreateAndAddContext(BindableProperty property)
829 var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
831 if (property.DefaultValueCreator == null)
832 context.Attributes = BindableContextAttributes.IsDefaultValue;
834 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
836 properties.Add(context);
840 [MethodImpl(MethodImplOptions.AggressiveInlining)]
841 BindablePropertyContext GetContext(BindableProperty property)
843 List<BindablePropertyContext> propertyList = properties;
845 for (var i = 0; i < propertyList.Count; i++)
847 BindablePropertyContext context = propertyList[i];
848 if (ReferenceEquals(context.Property, property))
855 [MethodImpl(MethodImplOptions.AggressiveInlining)]
856 BindablePropertyContext GetOrCreateContext(BindableProperty property)
858 BindablePropertyContext context = GetContext(property);
861 context = CreateAndAddContext(property);
863 else if (property.DefaultValueCreator != null)
865 context.Value = property.DefaultValueCreator(this); //Update Value from dali
871 void RemoveBinding(BindableProperty property, BindablePropertyContext context)
873 context.Binding.Unapply();
875 property.BindingChanging?.Invoke(this, context.Binding, null);
877 context.Binding = null;
880 internal void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
882 if (property == null)
883 throw new ArgumentNullException(nameof(property));
885 if (checkAccess && property.IsReadOnly)
886 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
888 if (fromStyle && !CanBeSetFromStyle(property))
891 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
892 (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0),
896 void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, bool forceSendChangeSignal, SetValueFlags attributes, bool silent = false)
898 object original = context.Value;
899 bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
900 bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
901 bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
902 bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
904 bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
905 if (!silent && (!same || raiseOnEqual))
907 property.PropertyChanging?.Invoke(this, original, value);
909 OnPropertyChanging(property.PropertyName);
912 if (!same || raiseOnEqual)
914 context.Value = value;
917 context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
918 context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated;
920 if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
921 RemoveDynamicResource(property);
923 BindingBase binding = context.Binding;
926 if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
928 RemoveBinding(property, context);
933 PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
937 if ((!same || raiseOnEqual))
939 property.PropertyChanged?.Invoke(this, original, value);
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);
971 else if (true == same && true == forceSendChangeSignal)
973 if (binding != null && !currentlyApplying)
980 OnPropertyChanged(property.PropertyName);
982 if (null != propertyGroup)
984 foreach (var relativeProperty in propertyGroup)
986 if (relativeProperty != property)
988 var relativeContext = GetOrCreateContext(relativeProperty);
989 var relativeBinding = relativeContext.Binding;
991 if (null != relativeBinding)
994 relativeBinding.Apply(true);
998 OnPropertyChanged(relativeProperty.PropertyName);
1004 OnPropertyChangedWithData(property);
1008 private static Dictionary<BindableProperty, HashSet<BindableProperty>> PropertyToGroup { get; }
1009 = new Dictionary<BindableProperty, HashSet<BindableProperty>>();
1012 enum BindableContextAttributes
1014 IsManuallySet = 1 << 0,
1015 IsBeingSet = 1 << 1,
1016 IsDynamicResource = 1 << 2,
1017 IsSetFromStyle = 1 << 3,
1018 IsDefaultValue = 1 << 4,
1019 IsDefaultValueCreated = 1 << 5,
1022 class BindablePropertyContext
1024 public BindableContextAttributes Attributes;
1025 public BindingBase Binding;
1026 public Queue<SetValueArgs> DelayedSetters;
1027 public BindableProperty Property;
1028 public object Value;
1032 internal enum SetValuePrivateFlags
1035 CheckAccess = 1 << 0,
1037 ManuallySet = 1 << 2,
1040 Default = CheckAccess
1045 public readonly SetValueFlags Attributes;
1046 public readonly BindablePropertyContext Context;
1047 public readonly bool CurrentlyApplying;
1048 public readonly BindableProperty Property;
1049 public readonly object Value;
1051 public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
1053 Property = property;
1056 CurrentlyApplying = currentlyApplying;
1057 Attributes = attributes;
1061 internal void AddChildBindableObject(BindableObject child)
1065 children.Add(child);
1066 child.FlushBinding();
1070 internal void RemoveChildBindableObject(BindableObject child)
1072 children.Remove(child);
1075 private List<BindableObject> children = new List<BindableObject>();
1077 private void FlushBinding()
1079 ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
1080 OnBindingContextChanged();
1082 foreach (var child in children)
1084 child.FlushBinding();
1090 namespace Tizen.NUI.Binding.Internals
1093 /// SetValueFlags. For internal use.
1096 [EditorBrowsable(EditorBrowsableState.Never)]
1097 public enum SetValueFlags
1102 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1103 [EditorBrowsable(EditorBrowsableState.Never)]
1107 /// Clear OneWay bindings.
1109 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1110 [EditorBrowsable(EditorBrowsableState.Never)]
1111 ClearOneWayBindings = 1 << 0,
1114 /// Clear TwoWay bindings.
1116 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1117 [EditorBrowsable(EditorBrowsableState.Never)]
1118 ClearTwoWayBindings = 1 << 1,
1121 /// Clear dynamic resource.
1123 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1124 [EditorBrowsable(EditorBrowsableState.Never)]
1125 ClearDynamicResource = 1 << 2,
1130 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1131 [EditorBrowsable(EditorBrowsableState.Never)]
1132 RaiseOnEqual = 1 << 3