/* * Copyright(c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Reflection; using System.Linq; using System.Runtime.CompilerServices; using Tizen.NUI.Binding.Internals; namespace Tizen.NUI.Binding { /// /// Provides a mechanism by which application developers can propagate changes that are made to data in one object to another. /// /// 9 public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler { /// /// Implements the bound property whose interface is provided by the BindingContext property. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty BindingContextProperty = BindableProperty.Create(nameof(BindingContext), typeof(object), typeof(BindableObject), default(object), BindingMode.OneWay, null, BindingContextPropertyChanged, null, null, BindingContextPropertyBindingChanging); readonly Dictionary properties = new Dictionary(4); bool applying; object inheritedContext; /// /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public object BindingContext { get { return inheritedContext ?? GetValue(BindingContextProperty); } set { SetValue(BindingContextProperty, value); } } [EditorBrowsable(EditorBrowsableState.Never)] public int LineNumber { get; set; } = -1; [EditorBrowsable(EditorBrowsableState.Never)] public int LinePosition { get; set; } = -1; void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key) { SetDynamicResource(property, key, false); } /// /// Raised when a property has changed. /// /// 9 public event PropertyChangedEventHandler PropertyChanged; /// Copy properties of other ViewStyle to this. /// The other BindableProperty merge to this. [EditorBrowsable(EditorBrowsableState.Never)] public virtual void CopyFrom(BindableObject other) { if (null == other) return; Type type1 = this.GetType(); BindableProperty.GetBindablePropertysOfType(type1, out var nameToBindableProperty1); Type type2 = other.GetType(); BindableProperty.GetBindablePropertysOfType(type2, out var nameToBindableProperty2); if (null != nameToBindableProperty1) { foreach (KeyValuePair keyValuePair in nameToBindableProperty1) { nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty); if (null != bindableProperty && other.IsSet(bindableProperty)) { object value = other.GetValue(bindableProperty); InternalSetValue(keyValuePair.Value, value); } } } } /// /// Copy all binding from other object. /// /// [EditorBrowsable(EditorBrowsableState.Never)] public void CopyBindingRelationShip(BindableObject other) { if (null == other) { return; } foreach (var property in properties) { RemoveBinding(property.Key); } foreach (var property in other.properties) { if (null != property.Value.Binding) { var binding = property.Value.Binding; other.RemoveBinding(property.Key); SetBinding(property.Key, binding); } } } /// /// Raised whenever the BindingContext property changes. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler BindingContextChanged; internal void ClearValue(BindableProperty property, bool fromStyle) { ClearValue(property, fromStyle: fromStyle, checkAccess: true); } /// /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue. /// /// The BindableProperty to clear internal void ClearValue(BindableProperty property) { ClearValue(property, fromStyle: false, checkAccess: true); } /// /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue for the property that is identified by propertyKey. /// /// The BindablePropertyKey that identifies the BindableProperty to clear. internal void ClearValue(BindablePropertyKey propertyKey) { if (propertyKey == null) throw new ArgumentNullException(nameof(propertyKey)); ClearValue(propertyKey.BindableProperty, fromStyle: false, checkAccess: false); } /// /// Return true if the target property exists and has been set. /// /// The target property /// return true if the target property exists and has been set internal bool IsSet(BindableProperty targetProperty) { if (targetProperty == null) throw new ArgumentNullException(nameof(targetProperty)); var bpcontext = GetContext(targetProperty); return bpcontext != null && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0; } /// /// Returns the value that is contained the BindableProperty. /// /// The BindableProperty for which to get the value. /// The value that is contained the BindableProperty /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public object GetValue(BindableProperty property) { if (property == null) throw new ArgumentNullException(nameof(property)); if (!IsBound && property.ValueGetter != null) { return property.ValueGetter(this); } BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property); if (context == null) return property.DefaultValue; return context.Value; } /// /// Raised when a property is about to change. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public event PropertyChangingEventHandler PropertyChanging; /// /// Removes a previously set binding. /// /// The BindableProperty from which to remove bindings. internal void RemoveBinding(BindableProperty property) { if (property == null) throw new ArgumentNullException(nameof(property)); BindablePropertyContext context = GetContext(property); if (context == null || context.Binding == null) return; RemoveBinding(property, context); } /// /// Assigns a binding to a property. /// /// The BindableProperty on which to set a binding. /// The binding to set. /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public void SetBinding(BindableProperty targetProperty, BindingBase binding) { SetBinding(targetProperty, binding, false); } /// Internal used, will never changed to not hidden. [EditorBrowsable(EditorBrowsableState.Never)] public virtual bool IsCreateByXaml { get; set; } /// This will be public opened in next ACR. [EditorBrowsable(EditorBrowsableState.Never)] public void EnforceNotifyBindedInstance(BindableProperty property) { if (null != property) { BindablePropertyContext context = GetOrCreateContext(property); BindingBase binding = context.Binding; var currentlyApplying = applying; if (binding != null && !currentlyApplying) { applying = true; binding.Apply(true); applying = false; } OnPropertyChanged(property.PropertyName); PropertyToGroup.TryGetValue(property, out HashSet propertyGroup); if (null != propertyGroup) { foreach (var relativeProperty in propertyGroup) { if (relativeProperty != property) { var relativeContext = GetOrCreateContext(relativeProperty); var relativeBinding = relativeContext.Binding; if (null != relativeBinding) { applying = true; relativeBinding.Apply(true); applying = false; } OnPropertyChanged(relativeProperty.PropertyName); } } } } } /// /// Sets the value of the specified property. /// /// The BindableProperty on which to assign a value. /// The value to set. /// Thrown when property is null. /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public void SetValue(BindableProperty property, object value) { InternalSetValue(property, value); ChangedPropertiesSetExcludingStyle.Add(property); } internal void InternalSetValue(BindableProperty property, object value) { if (true == IsBound) { SetValue(property, value, false, true); } else { if (null == property) { throw new ArgumentNullException(nameof(property)); } object oldvalue = null; BindablePropertyContext context = GetOrCreateContext(property); if (null != context) { context.Attributes |= BindableContextAttributes.IsManuallySet; oldvalue = context.Value; context.Value = value; } property.PropertyChanged?.Invoke(this, oldvalue, value); OnPropertyChanged(property.PropertyName); OnPropertyChangedWithData(property); } } private HashSet changedPropertiesSetExcludingStyle; internal protected HashSet ChangedPropertiesSetExcludingStyle { get { if (null == changedPropertiesSetExcludingStyle) { changedPropertiesSetExcludingStyle = new HashSet(); } return changedPropertiesSetExcludingStyle; } } /// /// Sets the value of the propertyKey. /// /// The BindablePropertyKey on which to assign a value. /// The value to set. /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public void SetValue(BindablePropertyKey propertyKey, object value) { if (propertyKey == null) throw new ArgumentNullException(nameof(propertyKey)); SetValue(propertyKey.BindableProperty, value, false, false); } /// /// Set the inherited context to a neated element. /// /// The object on which to set the inherited binding context. /// The inherited context to set. /// Thrown when bindable is null. [EditorBrowsable(EditorBrowsableState.Never)] public static void SetInheritedBindingContext(BindableObject bindable, object value) { if (null == bindable) { throw new ArgumentNullException(nameof(bindable)); } BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty); if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0)) return; object oldContext = bindable.inheritedContext; if (ReferenceEquals(oldContext, value)) return; if (bpContext != null && oldContext == null) oldContext = bpContext.Value; if (bpContext != null && bpContext.Binding != null) { bpContext.Binding.Context = value; bindable.inheritedContext = null; } else { bindable.inheritedContext = value; } bindable.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true); bindable.OnBindingContextChanged(); } /// /// Register the properties which can effect each other in Binding to same group. /// [EditorBrowsable(EditorBrowsableState.Never)] public static void RegisterPropertyGroup(BindableProperty property, HashSet group) { if (!PropertyToGroup.ContainsKey(property)) { PropertyToGroup.Add(property, group); } if (null != group && !(group.Contains(property))) { group.Add(property); } } /// /// Apply the bindings to BindingContext. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected void ApplyBindings() { ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false); } /// /// Override this method to execute an action when the BindingContext changes. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected virtual void OnBindingContextChanged() { BindingContextChanged?.Invoke(this, EventArgs.Empty); } /// /// Call this method from a child class to notify that a change happened on a property. /// /// The name of the property that changed. /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); /// /// Call this method from a child class to notify that a change is going to happen on a property. /// /// The name of the property that is changing. /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null) => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); /// /// Method that is called when a bound property is changed. /// [EditorBrowsable(EditorBrowsableState.Never)] protected virtual void OnPropertyChangedWithData(BindableProperty prop) { } /// /// Unapplies all previously set bindings. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected void UnapplyBindings() { foreach (var context in properties.Values) { if (context.Binding == null) continue; context.Binding.Unapply(); } } internal bool GetIsBound(BindableProperty targetProperty) { if (targetProperty == null) throw new ArgumentNullException(nameof(targetProperty)); BindablePropertyContext bpcontext = GetContext(targetProperty); return bpcontext != null && bpcontext.Binding != null; } /// /// Returns the value that is contained the BindableProperty. /// /// The BindableProperty instance. /// The BindableProperty instance. /// The value that is contained the BindableProperty internal object[] GetValues(BindableProperty property0, BindableProperty property1) { var values = new object[2]; foreach (var context in properties.Values) { if (ReferenceEquals(context.Property, property0)) { values[0] = context.Value; property0 = null; } else if (ReferenceEquals(context.Property, property1)) { values[1] = context.Value; property1 = null; } if (property0 == null && property1 == null) return values; } if (!ReferenceEquals(property0, null)) values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value; if (!ReferenceEquals(property1, null)) values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value; return values; } /// /// Returns the value that is contained the BindableProperty. /// /// The BindableProperty instance. /// The BindableProperty instance. /// The BindableProperty instance. /// The value that is contained the BindableProperty internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2) { var values = new object[3]; foreach (var context in properties.Values) { if (ReferenceEquals(context.Property, property0)) { values[0] = context.Value; property0 = null; } else if (ReferenceEquals(context.Property, property1)) { values[1] = context.Value; property1 = null; } else if (ReferenceEquals(context.Property, property2)) { values[2] = context.Value; property2 = null; } if (property0 == null && property1 == null && property2 == null) return values; } if (!ReferenceEquals(property0, null)) values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value; if (!ReferenceEquals(property1, null)) values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value; if (!ReferenceEquals(property2, null)) values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value; return values; } /// /// Returns the value that is contained the BindableProperty. /// /// The array of the BindableProperty instances /// The values that is contained the BindableProperty instances. internal object[] GetValues(params BindableProperty[] properties) { var values = new object[properties.Length]; foreach (var context in this.properties.Values) { var index = properties.IndexOf(context.Property); if (index < 0) continue; values[index] = context.Value; } for (var i = 0; i < values.Length; i++) { if (!ReferenceEquals(values[i], null)) continue; values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value; } return values; } internal virtual void OnRemoveDynamicResource(BindableProperty property) { } internal virtual void OnSetDynamicResource(BindableProperty property, string key) { } internal void RemoveDynamicResource(BindableProperty property) { if (property == null) throw new ArgumentNullException(nameof(property)); OnRemoveDynamicResource(property); BindablePropertyContext context = GetOrCreateContext(property); context.Attributes &= ~BindableContextAttributes.IsDynamicResource; } internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle) { if (targetProperty == null) throw new ArgumentNullException(nameof(targetProperty)); if (binding == null) throw new ArgumentNullException(nameof(binding)); if (fromStyle && !CanBeSetFromStyle(targetProperty)) return; IsBound = true; var context = GetOrCreateContext(targetProperty); if (fromStyle) context.Attributes |= BindableContextAttributes.IsSetFromStyle; else context.Attributes &= ~BindableContextAttributes.IsSetFromStyle; if (context.Binding != null) context.Binding.Unapply(); BindingBase oldBinding = context.Binding; context.Binding = binding; targetProperty.BindingChanging?.Invoke(this, oldBinding, binding); binding.Apply(BindingContext, this, targetProperty); } bool CanBeSetFromStyle(BindableProperty property) { var context = GetContext(property); if (context == null) return true; if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle) return true; if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue) return true; if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated) return true; return false; } internal void SetDynamicResource(BindableProperty property, string key) { SetDynamicResource(property, key, false); } internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle) { if (property == null) throw new ArgumentNullException(nameof(property)); if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); if (fromStyle && !CanBeSetFromStyle(property)) return; var context = GetOrCreateContext(property); context.Attributes |= BindableContextAttributes.IsDynamicResource; if (fromStyle) context.Attributes |= BindableContextAttributes.IsSetFromStyle; else context.Attributes &= ~BindableContextAttributes.IsSetFromStyle; OnSetDynamicResource(property, key); } internal void SetValue(BindableProperty property, object value, bool fromStyle) { SetValue(property, value, fromStyle, true); } internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None) { SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None); } /// /// For internal use. /// /// The BindableProperty on which to assign a value. /// The value to set /// The set value flag [EditorBrowsable(EditorBrowsableState.Never)] internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None) { SetValueCore(property, value, attributes, SetValuePrivateFlags.Default); } internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes) { bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0; bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0; bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0; bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0; bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0; if (property == null) throw new ArgumentNullException(nameof(property)); if (checkAccess && property.IsReadOnly) { Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName); return; } if (!converted && !property.TryConvert(ref value)) { Console.WriteLine($"SetValue : Can not convert {value} to type {property.ReturnType}"); return; } if (property.ValidateValue != null && !property.ValidateValue(this, value)) throw new ArgumentException("Value was an invalid value for " + property.PropertyName, nameof(value)); if (property.CoerceValue != null) value = property.CoerceValue(this, value); BindablePropertyContext context = GetOrCreateContext(property); if (manuallySet) { context.Attributes |= BindableContextAttributes.IsManuallySet; context.Attributes &= ~BindableContextAttributes.IsSetFromStyle; } else { context.Attributes &= ~BindableContextAttributes.IsManuallySet; } if (fromStyle) { context.Attributes |= BindableContextAttributes.IsSetFromStyle; } // else omitted on purpose bool currentlyApplying = applying; if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0) { Queue delayQueue = context.DelayedSetters; if (delayQueue == null) context.DelayedSetters = delayQueue = new Queue(); delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes)); } else { context.Attributes |= BindableContextAttributes.IsBeingSet; SetValueActual(property, context, value, currentlyApplying, attributes, silent); Queue delayQueue = context.DelayedSetters; if (delayQueue != null) { while (delayQueue.Count > 0) { SetValueArgs s = delayQueue.Dequeue(); SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, s.Attributes); } context.DelayedSetters = null; } context.Attributes &= ~BindableContextAttributes.IsBeingSet; } } internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged) { var prop = properties.Values.ToArray(); for (int i = 0, propLength = prop.Length; i < propLength; i++) { BindablePropertyContext context = prop[i]; BindingBase binding = context.Binding; if (binding == null) continue; if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty)) continue; binding.Unapply(fromBindingContextChanged: fromBindingContextChanged); binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged); } } /// /// Check if object is bound or not. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsBound { get; set; } = false; static void BindingContextPropertyChanged(BindableObject bindable, object oldvalue, object newvalue) { bindable.inheritedContext = null; bindable.ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true); bindable.OnBindingContextChanged(); if (newvalue is BindableObject targetBindableObject) { targetBindableObject.IsBound = true; } } static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase) { object context = bindable.inheritedContext; var oldBinding = oldBindingBase as Binding; var newBinding = newBindingBase as Binding; if (context == null && oldBinding != null) context = oldBinding.Context; if (context != null && newBinding != null) newBinding.Context = context; } void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess) { if (property == null) throw new ArgumentNullException(nameof(property)); if (checkAccess && property.IsReadOnly) throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName)); BindablePropertyContext bpcontext = GetContext(property); if (bpcontext == null) return; if (fromStyle && !CanBeSetFromStyle(property)) return; object original = bpcontext.Value; object newValue = property.GetDefaultValue(this); bool same = Equals(original, newValue); if (!same) { property.PropertyChanging?.Invoke(this, original, newValue); OnPropertyChanging(property.PropertyName); } bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet; bpcontext.Value = newValue; if (property.DefaultValueCreator == null) bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue; else bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated; if (!same) { OnPropertyChanged(property.PropertyName); OnPropertyChangedWithData(property); property.PropertyChanged?.Invoke(this, original, newValue); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] BindablePropertyContext CreateAndAddContext(BindableProperty property) { var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue }; if (property.DefaultValueCreator == null) context.Attributes = BindableContextAttributes.IsDefaultValue; else context.Attributes = BindableContextAttributes.IsDefaultValueCreated; properties.Add(property, context); return context; } [MethodImpl(MethodImplOptions.AggressiveInlining)] BindablePropertyContext GetContext(BindableProperty property) => properties.TryGetValue(property, out var result) ? result : null; [MethodImpl(MethodImplOptions.AggressiveInlining)] BindablePropertyContext GetOrCreateContext(BindableProperty property) { BindablePropertyContext context = GetContext(property); if (context == null) { context = CreateAndAddContext(property); } else if (property.ValueGetter != null) { context.Value = property.ValueGetter(this); //Update Value from dali }//added by xiaohui.fang else if (property.DefaultValueCreator != null) //This will be removed in the future. { context.Value = property.DefaultValueCreator(this); //Update Value from dali }//added by xb.teng return context; } void RemoveBinding(BindableProperty property, BindablePropertyContext context) { context.Binding.Unapply(); property.BindingChanging?.Invoke(this, context.Binding, null); context.Binding = null; } internal void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess) { if (property == null) throw new ArgumentNullException(nameof(property)); if (checkAccess && property.IsReadOnly) throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName)); if (fromStyle && !CanBeSetFromStyle(property)) return; SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource, (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0)); } void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false) { object original = context.Value; bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0; bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0; bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0; bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0; bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original); if (!silent && (!same || raiseOnEqual)) { property.PropertyChanging?.Invoke(this, original, value); OnPropertyChanging(property.PropertyName); } if (!same || raiseOnEqual) { context.Value = value; } context.Attributes &= ~BindableContextAttributes.IsDefaultValue; context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated; if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources) RemoveDynamicResource(property); BindingBase binding = context.Binding; if (binding != null) { if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay) { RemoveBinding(property, context); binding = null; } } PropertyToGroup.TryGetValue(property, out HashSet propertyGroup); if (!silent && (!same || raiseOnEqual)) { property.PropertyChanged?.Invoke(this, original, value); if (binding != null && !currentlyApplying) { applying = true; binding.Apply(true); applying = false; } OnPropertyChanged(property.PropertyName); if (null != propertyGroup) { foreach (var relativeProperty in propertyGroup) { if (relativeProperty != property) { var relativeContext = GetOrCreateContext(relativeProperty); var relativeBinding = relativeContext.Binding; if (null != relativeBinding) { applying = true; relativeBinding.Apply(true); applying = false; } OnPropertyChanged(relativeProperty.PropertyName); } } } OnPropertyChangedWithData(property); } } private static Dictionary> PropertyToGroup { get; } = new Dictionary>(); [Flags] enum BindableContextAttributes { IsManuallySet = 1 << 0, IsBeingSet = 1 << 1, IsDynamicResource = 1 << 2, IsSetFromStyle = 1 << 3, IsDefaultValue = 1 << 4, IsDefaultValueCreated = 1 << 5, } class BindablePropertyContext { public BindableContextAttributes Attributes; public BindingBase Binding; public Queue DelayedSetters; public BindableProperty Property; public object Value; } [Flags] internal enum SetValuePrivateFlags { None = 0, CheckAccess = 1 << 0, Silent = 1 << 1, ManuallySet = 1 << 2, FromStyle = 1 << 3, Converted = 1 << 4, Default = CheckAccess } class SetValueArgs { public readonly SetValueFlags Attributes; public readonly BindablePropertyContext Context; public readonly bool CurrentlyApplying; public readonly BindableProperty Property; public readonly object Value; public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes) { Property = property; Context = context; Value = value; CurrentlyApplying = currentlyApplying; Attributes = attributes; } } internal void ReplaceBindingElement(Dictionary oldNameScope, Dictionary newNameScope) { var xElementToNameOfOld = new Dictionary(); foreach (var pair in oldNameScope) { xElementToNameOfOld.Add(pair.Value, pair.Key); } foreach (var property in properties) { if (property.Value.Binding is Binding binding && null != binding.Source) { string xName; xElementToNameOfOld.TryGetValue(binding.Source, out xName); if (null != xName) { var newObject = newNameScope[xName]; binding.Unapply(); binding.Source = newObject; SetBinding(property.Key, binding); } } } if (null != BindingContext) { string xName; xElementToNameOfOld.TryGetValue(BindingContext, out xName); if (null != xName) { var newObject = newNameScope[xName]; BindingContext = newObject; } } } internal void ClearBinding() { foreach (var property in properties) { if (null != property.Value.Binding) { property.Value.Binding.Unapply(); } } } } } namespace Tizen.NUI.Binding.Internals { /// /// SetValueFlags. For internal use. /// [Flags] [EditorBrowsable(EditorBrowsableState.Never)] public enum SetValueFlags { /// /// None. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] None = 0, /// /// Clear OneWay bindings. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] ClearOneWayBindings = 1 << 0, /// /// Clear TwoWay bindings. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] ClearTwoWayBindings = 1 << 1, /// /// Clear dynamic resource. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] ClearDynamicResource = 1 << 2, /// /// Raise or equal. /// /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] RaiseOnEqual = 1 << 3 } }