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;
24 using System.Runtime.CompilerServices;
25 using Tizen.NUI.Binding.Internals;
27 namespace Tizen.NUI.Binding
30 /// Provides a mechanism by which application developers can propagate changes that are made to data in one object to another.
32 /// <since_tizen> 9 </since_tizen>
33 public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
36 /// Implements the bound property whose interface is provided by the BindingContext property.
38 [EditorBrowsable(EditorBrowsableState.Never)]
39 public static readonly BindableProperty BindingContextProperty =
40 BindableProperty.Create(nameof(BindingContext), typeof(object), typeof(BindableObject), null, propertyChanged: (BindableProperty.BindingPropertyChangedDelegate)((bindable, oldValue, newValue) =>
42 var bindableObject = (BindableObject)bindable;
45 bindableObject.bindingContext = newValue;
46 bindableObject.FlushBinding();
48 if (newValue is BindableObject targetBindableObject)
50 targetBindableObject.IsBinded = true;
54 defaultValueCreator: (BindableProperty.CreateDefaultValueDelegate)((bindable) =>
56 if (null != bindable.bindingContext)
58 return bindable.bindingContext;
61 if (bindable is Container container)
63 return container.Parent?.BindingContext;
71 readonly Dictionary<BindableProperty, BindablePropertyContext> properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
74 object inheritedContext;
76 private object bindingContext;
79 /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject.
81 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
82 [EditorBrowsable(EditorBrowsableState.Never)]
83 public object BindingContext
85 get { return inheritedContext ?? GetValue(BindingContextProperty); }
86 set { SetValue(BindingContextProperty, value); }
89 [EditorBrowsable(EditorBrowsableState.Never)]
90 public int LineNumber { get; set; } = -1;
92 [EditorBrowsable(EditorBrowsableState.Never)]
93 public int LinePosition { get; set; } = -1;
95 void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
97 SetDynamicResource(property, key, false);
101 /// Raised when a property has changed.
103 /// <since_tizen> 9 </since_tizen>
104 public event PropertyChangedEventHandler PropertyChanged;
106 /// <summary>Copy properties of other ViewStyle to this.</summary>
107 /// <param name="other">The other BindableProperty merge to this.</param>
108 [EditorBrowsable(EditorBrowsableState.Never)]
109 public virtual void CopyFrom(BindableObject other)
111 if (null == other) return;
113 Type type1 = this.GetType();
114 BindableProperty.GetBindablePropertysOfType(type1, out var nameToBindableProperty1);
116 Type type2 = other.GetType();
117 BindableProperty.GetBindablePropertysOfType(type2, out var nameToBindableProperty2);
119 if (null != nameToBindableProperty1)
121 foreach (KeyValuePair<string, BindableProperty> keyValuePair in nameToBindableProperty1)
123 nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
125 if (null != bindableProperty && (SettedPropeties.Contains(bindableProperty) || other.SettedPropeties.Contains(bindableProperty)))
127 object value = other.GetValue(bindableProperty);
131 SetValue(keyValuePair.Value, value);
139 /// Copy all binding from other object.
141 /// <param name="other"></param>
142 [EditorBrowsable(EditorBrowsableState.Never)]
143 public void CopyBindingRelationShip(BindableObject other)
150 foreach (var property in properties)
152 RemoveBinding(property.Key);
155 foreach (var property in other.properties)
157 if (null != property.Value.Binding)
159 var binding = property.Value.Binding;
160 other.RemoveBinding(property.Key);
161 SetBinding(property.Key, binding);
167 /// Raised whenever the BindingContext property changes.
169 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
170 [EditorBrowsable(EditorBrowsableState.Never)]
171 public event EventHandler BindingContextChanged;
173 internal void ClearValue(BindableProperty property, bool fromStyle)
175 ClearValue(property, fromStyle: fromStyle, checkAccess: true);
179 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue.
181 /// <param name="property">The BindableProperty to clear</param>
182 internal void ClearValue(BindableProperty property)
184 ClearValue(property, fromStyle: false, checkAccess: true);
188 /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue for the property that is identified by propertyKey.
190 /// <param name="propertyKey">The BindablePropertyKey that identifies the BindableProperty to clear.</param>
191 internal void ClearValue(BindablePropertyKey propertyKey)
193 if (propertyKey == null)
194 throw new ArgumentNullException(nameof(propertyKey));
196 ClearValue(propertyKey.BindableProperty, fromStyle: false, checkAccess: false);
200 /// Return true if the target property exists and has been set.
202 /// <param name="targetProperty">The target property</param>
203 /// <returns>return true if the target property exists and has been set</returns>
204 internal bool IsSet(BindableProperty targetProperty)
206 if (targetProperty == null)
207 throw new ArgumentNullException(nameof(targetProperty));
209 var bpcontext = GetContext(targetProperty);
210 return bpcontext != null
211 && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0;
215 /// Returns the value that is contained the BindableProperty.
217 /// <param name="property">The BindableProperty for which to get the value.</param>
218 /// <returns>The value that is contained the BindableProperty</returns>
219 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
220 [EditorBrowsable(EditorBrowsableState.Never)]
221 public object GetValue(BindableProperty property)
223 if (property == null)
224 throw new ArgumentNullException(nameof(property));
226 if (!IsBinded && property.ValueGetter != null)
228 return property.ValueGetter(this);
231 BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
234 return property.DefaultValue;
236 return context.Value;
240 /// Raised when a property is about to change.
242 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
243 [EditorBrowsable(EditorBrowsableState.Never)]
244 public event PropertyChangingEventHandler PropertyChanging;
247 /// Removes a previously set binding.
249 /// <param name="property">The BindableProperty from which to remove bindings.</param>
250 internal void RemoveBinding(BindableProperty property)
252 if (property == null)
253 throw new ArgumentNullException(nameof(property));
255 BindablePropertyContext context = GetContext(property);
256 if (context == null || context.Binding == null)
259 RemoveBinding(property, context);
263 /// Assigns a binding to a property.
265 /// <param name="targetProperty">The BindableProperty on which to set a binding.</param>
266 /// <param name="binding">The binding to set.</param>
267 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
268 [EditorBrowsable(EditorBrowsableState.Never)]
269 public void SetBinding(BindableProperty targetProperty, BindingBase binding)
271 SetBinding(targetProperty, binding, false);
274 private bool isCreateByXaml = false;
275 /// Only used by the IL of xaml, will never changed to not hidden.
276 [EditorBrowsable(EditorBrowsableState.Never)]
277 public virtual bool IsCreateByXaml
281 return isCreateByXaml;
285 isCreateByXaml = value;
290 /// Sets the value of the specified property.
292 /// <param name="property">The BindableProperty on which to assign a value.</param>
293 /// <param name="value">The value to set.</param>
294 /// <exception cref="ArgumentNullException"> Thrown when property is null. </exception>
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(BindableProperty property, object value)
299 if (true == IsBinded)
301 SetValue(property, value, false, true);
305 if (null == property)
307 throw new ArgumentNullException(nameof(property));
310 object oldvalue = null;
311 if (null == property.DefaultValueCreator)
313 BindablePropertyContext context = GetOrCreateContext(property);
316 oldvalue = context.Value;
317 context.Value = value;
322 oldvalue = property.DefaultValueCreator.Invoke(this);
325 property.PropertyChanged?.Invoke(this, oldvalue, value);
327 OnPropertyChanged(property.PropertyName);
328 OnPropertyChangedWithData(property);
331 SettedPropeties.Add(property);
334 private HashSet<BindableProperty> settedPropeties;
335 private HashSet<BindableProperty> SettedPropeties
339 if (null == settedPropeties)
341 settedPropeties = new HashSet<BindableProperty>();
344 return settedPropeties;
348 internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
350 if (property == null)
351 throw new ArgumentNullException(nameof(property));
353 if (true == isCreateByXaml)
355 if (property.IsReadOnly)
356 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
358 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
359 SetValuePrivateFlags.ManuallySet | SetValuePrivateFlags.CheckAccess, true);
363 property.PropertyChanged?.Invoke(this, null, value);
368 /// Sets the value of the propertyKey.
370 /// <param name="propertyKey">The BindablePropertyKey on which to assign a value.</param>
371 /// <param name="value">The value to set.</param>
372 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
373 [EditorBrowsable(EditorBrowsableState.Never)]
374 public void SetValue(BindablePropertyKey propertyKey, object value)
376 if (propertyKey == null)
377 throw new ArgumentNullException(nameof(propertyKey));
379 SetValue(propertyKey.BindableProperty, value, false, false);
383 /// Set the inherited context to a neated element.
385 /// <param name="bindable">The object on which to set the inherited binding context.</param>
386 /// <param name="value">The inherited context to set.</param>
387 /// <exception cref="ArgumentNullException"> Thrown when bindable is null. </exception>
388 [EditorBrowsable(EditorBrowsableState.Never)]
389 public static void SetInheritedBindingContext(BindableObject bindable, object value)
391 if (null == bindable)
393 throw new ArgumentNullException(nameof(bindable));
396 BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
397 if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
400 object oldContext = bindable.inheritedContext;
402 if (ReferenceEquals(oldContext, value))
405 if (bpContext != null && oldContext == null)
406 oldContext = bpContext.Value;
408 if (bpContext != null && bpContext.Binding != null)
410 bpContext.Binding.Context = value;
411 bindable.inheritedContext = null;
415 bindable.inheritedContext = value;
418 bindable.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
419 bindable.OnBindingContextChanged();
423 /// Register the properties which can effect each other in Binding to same group.
425 [EditorBrowsable(EditorBrowsableState.Never)]
426 public static void RegisterPropertyGroup(BindableProperty property, HashSet<BindableProperty> group)
428 if (!PropertyToGroup.ContainsKey(property))
430 PropertyToGroup.Add(property, group);
433 if (null != group && !(group.Contains(property)))
439 /// Apply the bindings to BindingContext.
441 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
442 [EditorBrowsable(EditorBrowsableState.Never)]
443 protected void ApplyBindings()
445 ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false);
449 /// Override this method to execute an action when the BindingContext changes.
451 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
452 [EditorBrowsable(EditorBrowsableState.Never)]
453 protected virtual void OnBindingContextChanged()
455 BindingContextChanged?.Invoke(this, EventArgs.Empty);
459 /// Call this method from a child class to notify that a change happened on a property.
461 /// <param name="propertyName">The name of the property that changed.</param>
462 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
463 [EditorBrowsable(EditorBrowsableState.Never)]
464 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
465 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
468 /// Call this method from a child class to notify that a change is going to happen on a property.
470 /// <param name="propertyName">The name of the property that is changing.</param>
471 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
472 [EditorBrowsable(EditorBrowsableState.Never)]
473 protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
474 => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
477 /// Method that is called when a bound property is changed.
479 [EditorBrowsable(EditorBrowsableState.Never)]
480 protected virtual void OnPropertyChangedWithData(BindableProperty prop) { }
483 /// Unapplies all previously set bindings.
485 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
486 [EditorBrowsable(EditorBrowsableState.Never)]
487 protected void UnapplyBindings()
489 foreach (var context in properties.Values)
491 if (context.Binding == null)
494 context.Binding.Unapply();
498 internal bool GetIsBound(BindableProperty targetProperty)
500 if (targetProperty == null)
501 throw new ArgumentNullException(nameof(targetProperty));
503 BindablePropertyContext bpcontext = GetContext(targetProperty);
504 return bpcontext != null && bpcontext.Binding != null;
508 /// Returns the value that is contained the BindableProperty.
510 /// <param name="property0">The BindableProperty instance.</param>
511 /// <param name="property1">The BindableProperty instance.</param>
512 /// <returns>The value that is contained the BindableProperty</returns>
513 internal object[] GetValues(BindableProperty property0, BindableProperty property1)
515 var values = new object[2];
517 foreach (var context in properties.Values)
519 if (ReferenceEquals(context.Property, property0))
521 values[0] = context.Value;
524 else if (ReferenceEquals(context.Property, property1))
526 values[1] = context.Value;
530 if (property0 == null && property1 == null)
534 if (!ReferenceEquals(property0, null))
535 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
536 if (!ReferenceEquals(property1, null))
537 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
543 /// Returns the value that is contained the BindableProperty.
545 /// <param name="property0">The BindableProperty instance.</param>
546 /// <param name="property1">The BindableProperty instance.</param>
547 /// <param name="property2">The BindableProperty instance.</param>
548 /// <returns>The value that is contained the BindableProperty</returns>
549 internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
551 var values = new object[3];
553 foreach (var context in properties.Values)
555 if (ReferenceEquals(context.Property, property0))
557 values[0] = context.Value;
560 else if (ReferenceEquals(context.Property, property1))
562 values[1] = context.Value;
565 else if (ReferenceEquals(context.Property, property2))
567 values[2] = context.Value;
571 if (property0 == null && property1 == null && property2 == null)
575 if (!ReferenceEquals(property0, null))
576 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
577 if (!ReferenceEquals(property1, null))
578 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
579 if (!ReferenceEquals(property2, null))
580 values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
586 /// Returns the value that is contained the BindableProperty.
588 /// <param name="properties">The array of the BindableProperty instances</param>
589 /// <returns>The values that is contained the BindableProperty instances.</returns>
590 internal object[] GetValues(params BindableProperty[] properties)
592 var values = new object[properties.Length];
593 foreach (var context in this.properties.Values)
595 var index = properties.IndexOf(context.Property);
598 values[index] = context.Value;
600 for (var i = 0; i < values.Length; i++)
602 if (!ReferenceEquals(values[i], null))
604 values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value;
609 internal virtual void OnRemoveDynamicResource(BindableProperty property)
613 internal virtual void OnSetDynamicResource(BindableProperty property, string key)
617 internal void RemoveDynamicResource(BindableProperty property)
619 if (property == null)
620 throw new ArgumentNullException(nameof(property));
622 OnRemoveDynamicResource(property);
623 BindablePropertyContext context = GetOrCreateContext(property);
624 context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
627 internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
629 if (targetProperty == null)
630 throw new ArgumentNullException(nameof(targetProperty));
632 throw new ArgumentNullException(nameof(binding));
634 if (fromStyle && !CanBeSetFromStyle(targetProperty))
639 var context = GetOrCreateContext(targetProperty);
641 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
643 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
645 if (context.Binding != null)
646 context.Binding.Unapply();
648 BindingBase oldBinding = context.Binding;
649 context.Binding = binding;
651 targetProperty.BindingChanging?.Invoke(this, oldBinding, binding);
653 binding.Apply(BindingContext, this, targetProperty);
656 bool CanBeSetFromStyle(BindableProperty property)
658 var context = GetContext(property);
661 if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle)
663 if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue)
665 if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated)
670 internal void SetDynamicResource(BindableProperty property, string key)
672 SetDynamicResource(property, key, false);
675 internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
677 if (property == null)
678 throw new ArgumentNullException(nameof(property));
679 if (string.IsNullOrEmpty(key))
680 throw new ArgumentNullException(nameof(key));
681 if (fromStyle && !CanBeSetFromStyle(property))
684 var context = GetOrCreateContext(property);
686 context.Attributes |= BindableContextAttributes.IsDynamicResource;
688 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
690 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
692 OnSetDynamicResource(property, key);
695 internal void SetValue(BindableProperty property, object value, bool fromStyle)
697 SetValue(property, value, fromStyle, true);
700 internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
702 SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None, false);
706 /// For internal use.
708 /// <param name="property">The BindableProperty on which to assign a value.</param>
709 /// <param name="value">The value to set</param>
710 /// <param name="attributes">The set value flag</param>
711 [EditorBrowsable(EditorBrowsableState.Never)]
712 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
714 SetValueCore(property, value, attributes, SetValuePrivateFlags.Default, false);
717 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, bool forceSendChangeSignal)
719 bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
720 bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
721 bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
722 bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
723 bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
725 if (property == null)
726 throw new ArgumentNullException(nameof(property));
727 if (checkAccess && property.IsReadOnly)
729 Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
733 if (!converted && !property.TryConvert(ref value))
735 Console.WriteLine($"SetValue : Can not convert {value} to type {property.ReturnType}");
739 if (property.ValidateValue != null && !property.ValidateValue(this, value))
740 throw new ArgumentException("Value was an invalid value for " + property.PropertyName, nameof(value));
742 if (property.CoerceValue != null)
743 value = property.CoerceValue(this, value);
745 BindablePropertyContext context = GetOrCreateContext(property);
748 context.Attributes |= BindableContextAttributes.IsManuallySet;
749 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
753 context.Attributes &= ~BindableContextAttributes.IsManuallySet;
758 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
760 // else omitted on purpose
762 bool currentlyApplying = applying;
764 if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
766 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
767 if (delayQueue == null)
768 context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
770 delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
774 context.Attributes |= BindableContextAttributes.IsBeingSet;
775 SetValueActual(property, context, value, currentlyApplying, forceSendChangeSignal, attributes, silent);
777 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
778 if (delayQueue != null)
780 while (delayQueue.Count > 0)
782 SetValueArgs s = delayQueue.Dequeue();
783 SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, forceSendChangeSignal, s.Attributes);
786 context.DelayedSetters = null;
789 context.Attributes &= ~BindableContextAttributes.IsBeingSet;
793 internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
795 var prop = properties.Values.ToArray();
796 for (int i = 0, propLength = prop.Length; i < propLength; i++)
798 BindablePropertyContext context = prop[i];
799 BindingBase binding = context.Binding;
803 if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty))
806 binding.Unapply(fromBindingContextChanged: fromBindingContextChanged);
807 binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged);
811 internal bool IsBinded
817 static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
819 object context = bindable.inheritedContext;
820 var oldBinding = oldBindingBase as Binding;
821 var newBinding = newBindingBase as Binding;
823 if (context == null && oldBinding != null)
824 context = oldBinding.Context;
825 if (context != null && newBinding != null)
826 newBinding.Context = context;
829 void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
831 if (property == null)
832 throw new ArgumentNullException(nameof(property));
834 if (checkAccess && property.IsReadOnly)
835 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
837 BindablePropertyContext bpcontext = GetContext(property);
838 if (bpcontext == null)
841 if (fromStyle && !CanBeSetFromStyle(property))
844 object original = bpcontext.Value;
846 object newValue = property.GetDefaultValue(this);
848 bool same = Equals(original, newValue);
851 property.PropertyChanging?.Invoke(this, original, newValue);
853 OnPropertyChanging(property.PropertyName);
856 bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
857 bpcontext.Value = newValue;
858 if (property.DefaultValueCreator == null)
859 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
861 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated;
865 OnPropertyChanged(property.PropertyName);
866 OnPropertyChangedWithData(property);
867 property.PropertyChanged?.Invoke(this, original, newValue);
871 [MethodImpl(MethodImplOptions.AggressiveInlining)]
872 BindablePropertyContext CreateAndAddContext(BindableProperty property)
874 var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
876 if (property.DefaultValueCreator == null)
877 context.Attributes = BindableContextAttributes.IsDefaultValue;
879 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
881 properties.Add(property, context);
885 [MethodImpl(MethodImplOptions.AggressiveInlining)]
886 BindablePropertyContext GetContext(BindableProperty property) => properties.TryGetValue(property, out var result) ? result : null;
888 [MethodImpl(MethodImplOptions.AggressiveInlining)]
889 BindablePropertyContext GetOrCreateContext(BindableProperty property)
891 BindablePropertyContext context = GetContext(property);
894 context = CreateAndAddContext(property);
896 else if (property.ValueGetter != null)
898 context.Value = property.ValueGetter(this); //Update Value from dali
899 }//added by xiaohui.fang
900 else if (property.DefaultValueCreator != null) //This will be removed in the future.
902 context.Value = property.DefaultValueCreator(this); //Update Value from dali
908 void RemoveBinding(BindableProperty property, BindablePropertyContext context)
910 context.Binding.Unapply();
912 property.BindingChanging?.Invoke(this, context.Binding, null);
914 context.Binding = null;
917 internal void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
919 if (property == null)
920 throw new ArgumentNullException(nameof(property));
922 if (checkAccess && property.IsReadOnly)
923 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
925 if (fromStyle && !CanBeSetFromStyle(property))
928 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
929 (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0),
933 void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, bool forceSendChangeSignal, SetValueFlags attributes, bool silent = false)
935 object original = context.Value;
936 bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
937 bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
938 bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
939 bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
941 bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
942 if (!silent && (!same || raiseOnEqual))
944 property.PropertyChanging?.Invoke(this, original, value);
946 OnPropertyChanging(property.PropertyName);
949 if (!same || raiseOnEqual)
951 context.Value = value;
954 context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
955 context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated;
957 if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
958 RemoveDynamicResource(property);
960 BindingBase binding = context.Binding;
963 if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
965 RemoveBinding(property, context);
970 PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
974 if ((!same || raiseOnEqual))
976 property.PropertyChanged?.Invoke(this, original, value);
978 if (binding != null && !currentlyApplying)
985 OnPropertyChanged(property.PropertyName);
987 if (null != propertyGroup)
989 foreach (var relativeProperty in propertyGroup)
991 if (relativeProperty != property)
993 var relativeContext = GetOrCreateContext(relativeProperty);
994 var relativeBinding = relativeContext.Binding;
996 if (null != relativeBinding)
999 relativeBinding.Apply(true);
1003 OnPropertyChanged(relativeProperty.PropertyName);
1008 else if (true == same && true == forceSendChangeSignal)
1010 if (binding != null && !currentlyApplying)
1013 binding.Apply(true);
1017 OnPropertyChanged(property.PropertyName);
1019 if (null != propertyGroup)
1021 foreach (var relativeProperty in propertyGroup)
1023 if (relativeProperty != property)
1025 var relativeContext = GetOrCreateContext(relativeProperty);
1026 var relativeBinding = relativeContext.Binding;
1028 if (null != relativeBinding)
1031 relativeBinding.Apply(true);
1035 OnPropertyChanged(relativeProperty.PropertyName);
1041 OnPropertyChangedWithData(property);
1045 private static Dictionary<BindableProperty, HashSet<BindableProperty>> PropertyToGroup { get; }
1046 = new Dictionary<BindableProperty, HashSet<BindableProperty>>();
1049 enum BindableContextAttributes
1051 IsManuallySet = 1 << 0,
1052 IsBeingSet = 1 << 1,
1053 IsDynamicResource = 1 << 2,
1054 IsSetFromStyle = 1 << 3,
1055 IsDefaultValue = 1 << 4,
1056 IsDefaultValueCreated = 1 << 5,
1059 class BindablePropertyContext
1061 public BindableContextAttributes Attributes;
1062 public BindingBase Binding;
1063 public Queue<SetValueArgs> DelayedSetters;
1064 public BindableProperty Property;
1065 public object Value;
1069 internal enum SetValuePrivateFlags
1072 CheckAccess = 1 << 0,
1074 ManuallySet = 1 << 2,
1077 Default = CheckAccess
1082 public readonly SetValueFlags Attributes;
1083 public readonly BindablePropertyContext Context;
1084 public readonly bool CurrentlyApplying;
1085 public readonly BindableProperty Property;
1086 public readonly object Value;
1088 public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
1090 Property = property;
1093 CurrentlyApplying = currentlyApplying;
1094 Attributes = attributes;
1098 internal void AddChildBindableObject(BindableObject child)
1102 children.Add(child);
1103 child.FlushBinding();
1107 internal void RemoveChildBindableObject(BindableObject child)
1109 children.Remove(child);
1112 internal void ReplaceBindingElement(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
1114 var xElementToNameOfOld = new Dictionary<object, string>();
1116 foreach (var pair in oldNameScope)
1118 xElementToNameOfOld.Add(pair.Value, pair.Key);
1121 foreach (var property in properties)
1123 if (property.Value.Binding is Binding binding && null != binding.Source)
1126 xElementToNameOfOld.TryGetValue(binding.Source, out xName);
1130 var newObject = newNameScope[xName];
1132 binding.Source = newObject;
1133 SetBinding(property.Key, binding);
1138 if (null != BindingContext)
1141 xElementToNameOfOld.TryGetValue(BindingContext, out xName);
1145 var newObject = newNameScope[xName];
1146 BindingContext = newObject;
1151 internal void ClearBinding()
1153 foreach (var property in properties)
1155 if (null != property.Value.Binding)
1157 property.Value.Binding.Unapply();
1162 private List<BindableObject> children = new List<BindableObject>();
1164 private void FlushBinding()
1166 ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
1167 OnBindingContextChanged();
1169 foreach (var child in children)
1171 child.FlushBinding();
1177 namespace Tizen.NUI.Binding.Internals
1180 /// SetValueFlags. For internal use.
1183 [EditorBrowsable(EditorBrowsableState.Never)]
1184 public enum SetValueFlags
1189 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1190 [EditorBrowsable(EditorBrowsableState.Never)]
1194 /// Clear OneWay bindings.
1196 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1197 [EditorBrowsable(EditorBrowsableState.Never)]
1198 ClearOneWayBindings = 1 << 0,
1201 /// Clear TwoWay bindings.
1203 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1204 [EditorBrowsable(EditorBrowsableState.Never)]
1205 ClearTwoWayBindings = 1 << 1,
1208 /// Clear dynamic resource.
1210 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1211 [EditorBrowsable(EditorBrowsableState.Never)]
1212 ClearDynamicResource = 1 << 2,
1217 /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1218 [EditorBrowsable(EditorBrowsableState.Never)]
1219 RaiseOnEqual = 1 << 3