[NUI] Add API so that user can copy properties, bindings from other container
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / BindableObject.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Diagnostics;
22 using System.Reflection;
23 using System.Linq;
24 using System.Runtime.CompilerServices;
25 using Tizen.NUI.Binding.Internals;
26
27 namespace Tizen.NUI.Binding
28 {
29     /// <summary>
30     /// Provides a mechanism by which application developers can propagate changes that are made to data in one object to another.
31     /// </summary>
32     /// <since_tizen> 9 </since_tizen>
33     public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
34     {
35         /// <summary>
36         /// Implements the bound property whose interface is provided by the BindingContext property.
37         /// </summary>
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) =>
41             {
42                 var bindableObject = (BindableObject)bindable;
43                 if (newValue != null)
44                 {
45                     bindableObject.bindingContext = newValue;
46                     bindableObject.FlushBinding();
47
48                     if (newValue is BindableObject targetBindableObject)
49                     {
50                         targetBindableObject.IsBinded = true;
51                     }
52                 }
53             }),
54             defaultValueCreator: (BindableProperty.CreateDefaultValueDelegate)((bindable) =>
55             {
56                 if (null != bindable.bindingContext)
57                 {
58                     return bindable.bindingContext;
59                 }
60
61                 if (bindable is Container container)
62                 {
63                     return container.Parent?.BindingContext;
64                 }
65                 else
66                 {
67                     return null;
68                 }
69             }));
70
71         readonly Dictionary<BindableProperty, BindablePropertyContext> properties = new Dictionary<BindableProperty, BindablePropertyContext>(4);
72
73         bool applying;
74         object inheritedContext;
75
76         private object bindingContext;
77
78         /// <summary>
79         /// Gets or sets object that contains the properties that will be targeted by the bound properties that belong to this BindableObject.
80         /// </summary>
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
84         {
85             get { return inheritedContext ?? GetValue(BindingContextProperty); }
86             set { SetValue(BindingContextProperty, value); }
87         }
88
89         void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
90         {
91             SetDynamicResource(property, key, false);
92         }
93
94         /// <summary>
95         /// Raised when a property has changed.
96         /// </summary>
97         /// <since_tizen> 9 </since_tizen>
98         public event PropertyChangedEventHandler PropertyChanged;
99
100         /// <summary>Copy properties of other ViewStyle to this.</summary>
101         /// <param name="other">The other BindableProperty merge to this.</param>
102         [EditorBrowsable(EditorBrowsableState.Never)]
103         public virtual void CopyFrom(BindableObject other)
104         {
105             if (null == other) return;
106
107             Type type1 = this.GetType();
108             BindableProperty.GetBindablePropertysOfType(type1, out var nameToBindableProperty1);
109
110             Type type2 = other.GetType();
111             BindableProperty.GetBindablePropertysOfType(type2, out var nameToBindableProperty2);
112
113             if (null != nameToBindableProperty1)
114             {
115                 foreach (KeyValuePair<string, BindableProperty> keyValuePair in nameToBindableProperty1)
116                 {
117                     nameToBindableProperty2.TryGetValue(keyValuePair.Key, out var bindableProperty);
118
119                     if (null != bindableProperty && (SettedPropeties.Contains(bindableProperty) || other.SettedPropeties.Contains(bindableProperty)))
120                     {
121                         object value = other.GetValue(bindableProperty);
122
123                         if (null != value)
124                         {
125                             SetValue(keyValuePair.Value, value);
126                         }
127                     }
128                 }
129             }
130         }
131
132         /// <summary>
133         /// Copy all binding from other object.
134         /// </summary>
135         /// <param name="other"></param>
136         [EditorBrowsable(EditorBrowsableState.Never)]
137         public void CopyBindingRelationShip(BindableObject other)
138         {
139             if (null == other)
140             {
141                 return;
142             }
143
144             foreach (var property in properties)
145             {
146                 RemoveBinding(property.Key);
147             }
148
149             foreach (var property in other.properties)
150             {
151                 if (null != property.Value.Binding)
152                 {
153                     var binding = property.Value.Binding;
154                     other.RemoveBinding(property.Key);
155                     SetBinding(property.Key, binding);
156                 }
157             }
158         }
159
160         /// <summary>
161         /// Raised whenever the BindingContext property changes.
162         /// </summary>
163         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
164         [EditorBrowsable(EditorBrowsableState.Never)]
165         public event EventHandler BindingContextChanged;
166
167         internal void ClearValue(BindableProperty property, bool fromStyle)
168         {
169             ClearValue(property, fromStyle: fromStyle, checkAccess: true);
170         }
171
172         /// <summary>
173         /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue.
174         /// </summary>
175         /// <param name="property">The BindableProperty to clear</param>
176         internal void ClearValue(BindableProperty property)
177         {
178             ClearValue(property, fromStyle: false, checkAccess: true);
179         }
180
181         /// <summary>
182         /// Clears any value set by Tizen.NUI.Xaml.BindableObject.SetValue for the property that is identified by propertyKey.
183         /// </summary>
184         /// <param name="propertyKey">The BindablePropertyKey that identifies the BindableProperty to clear.</param>
185         internal void ClearValue(BindablePropertyKey propertyKey)
186         {
187             if (propertyKey == null)
188                 throw new ArgumentNullException(nameof(propertyKey));
189
190             ClearValue(propertyKey.BindableProperty, fromStyle: false, checkAccess: false);
191         }
192
193         /// <summary>
194         /// Return true if the target property exists and has been set.
195         /// </summary>
196         /// <param name="targetProperty">The target property</param>
197         /// <returns>return true if the target property exists and has been set</returns>
198         internal bool IsSet(BindableProperty targetProperty)
199         {
200             if (targetProperty == null)
201                 throw new ArgumentNullException(nameof(targetProperty));
202
203             var bpcontext = GetContext(targetProperty);
204             return bpcontext != null
205                 && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0;
206         }
207
208         /// <summary>
209         /// Returns the value that is contained the BindableProperty.
210         /// </summary>
211         /// <param name="property">The BindableProperty for which to get the value.</param>
212         /// <returns>The value that is contained the BindableProperty</returns>
213         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
214         [EditorBrowsable(EditorBrowsableState.Never)]
215         public object GetValue(BindableProperty property)
216         {
217             if (property == null)
218                 throw new ArgumentNullException(nameof(property));
219
220             if (!IsBinded && property.ValueGetter != null)
221             {
222                 return property.ValueGetter(this);
223             }
224
225             BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
226
227             if (context == null)
228                 return property.DefaultValue;
229
230             return context.Value;
231         }
232
233         /// <summary>
234         /// Raised when a property is about to change.
235         /// </summary>
236         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
237         [EditorBrowsable(EditorBrowsableState.Never)]
238         public event PropertyChangingEventHandler PropertyChanging;
239
240         /// <summary>
241         /// Removes a previously set binding.
242         /// </summary>
243         /// <param name="property">The BindableProperty from which to remove bindings.</param>
244         internal void RemoveBinding(BindableProperty property)
245         {
246             if (property == null)
247                 throw new ArgumentNullException(nameof(property));
248
249             BindablePropertyContext context = GetContext(property);
250             if (context == null || context.Binding == null)
251                 return;
252
253             RemoveBinding(property, context);
254         }
255
256         /// <summary>
257         /// Assigns a binding to a property.
258         /// </summary>
259         /// <param name="targetProperty">The BindableProperty on which to set a binding.</param>
260         /// <param name="binding">The binding to set.</param>
261         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
262         [EditorBrowsable(EditorBrowsableState.Never)]
263         public void SetBinding(BindableProperty targetProperty, BindingBase binding)
264         {
265             SetBinding(targetProperty, binding, false);
266         }
267
268         private bool isCreateByXaml = false;
269         /// Only used by the IL of xaml, will never changed to not hidden.
270         [EditorBrowsable(EditorBrowsableState.Never)]
271         public virtual bool IsCreateByXaml
272         {
273             get
274             {
275                 return isCreateByXaml;
276             }
277             set
278             {
279                 isCreateByXaml = value;
280             }
281         }
282
283         /// <summary>
284         /// Sets the value of the specified property.
285         /// </summary>
286         /// <param name="property">The BindableProperty on which to assign a value.</param>
287         /// <param name="value">The value to set.</param>
288         /// <exception cref="ArgumentNullException"> Thrown when property is null. </exception>
289         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
290         [EditorBrowsable(EditorBrowsableState.Never)]
291         public void SetValue(BindableProperty property, object value)
292         {
293             if (true == IsBinded)
294             {
295                 SetValue(property, value, false, true);
296             }
297             else
298             {
299                 if (null == property)
300                 {
301                     throw new ArgumentNullException(nameof(property));
302                 }
303
304                 object oldvalue = null;
305                 if (null == property.DefaultValueCreator)
306                 {
307                     BindablePropertyContext context = GetOrCreateContext(property);
308                     if (null != context)
309                     {
310                         oldvalue = context.Value;
311                         context.Value = value;
312                     }
313                 }
314                 else
315                 {
316                     oldvalue = property.DefaultValueCreator.Invoke(this);
317                 }
318
319                 property.PropertyChanged?.Invoke(this, oldvalue, value);
320
321                 OnPropertyChanged(property.PropertyName);
322                 OnPropertyChangedWithData(property);
323             }
324
325             SettedPropeties.Add(property);
326         }
327
328         private HashSet<BindableProperty> settedPropeties;
329         private HashSet<BindableProperty> SettedPropeties
330         {
331             get
332             {
333                 if (null == settedPropeties)
334                 {
335                     settedPropeties = new HashSet<BindableProperty>();
336                 }
337
338                 return settedPropeties;
339             }
340         }
341
342         internal void SetValueAndForceSendChangeSignal(BindableProperty property, object value)
343         {
344             if (property == null)
345                 throw new ArgumentNullException(nameof(property));
346
347             if (true == isCreateByXaml)
348             {
349                 if (property.IsReadOnly)
350                     throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
351
352                 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
353                     SetValuePrivateFlags.ManuallySet | SetValuePrivateFlags.CheckAccess, true);
354             }
355             else
356             {
357                 property.PropertyChanged?.Invoke(this, null, value);
358             }
359         }
360
361         /// <summary>
362         /// Sets the value of the propertyKey.
363         /// </summary>
364         /// <param name="propertyKey">The BindablePropertyKey on which to assign a value.</param>
365         /// <param name="value">The value to set.</param>
366         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
367         [EditorBrowsable(EditorBrowsableState.Never)]
368         public void SetValue(BindablePropertyKey propertyKey, object value)
369         {
370             if (propertyKey == null)
371                 throw new ArgumentNullException(nameof(propertyKey));
372
373             SetValue(propertyKey.BindableProperty, value, false, false);
374         }
375
376         /// <summary>
377         /// Set the inherited context to a neated element.
378         /// </summary>
379         /// <param name="bindable">The object on which to set the inherited binding context.</param>
380         /// <param name="value">The inherited context to set.</param>
381         /// <exception cref="ArgumentNullException"> Thrown when bindable is null. </exception>
382         [EditorBrowsable(EditorBrowsableState.Never)]
383         public static void SetInheritedBindingContext(BindableObject bindable, object value)
384         {
385             if (null == bindable)
386             {
387                 throw new ArgumentNullException(nameof(bindable));
388             }
389
390             BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
391             if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
392                 return;
393
394             object oldContext = bindable.inheritedContext;
395
396             if (ReferenceEquals(oldContext, value))
397                 return;
398
399             if (bpContext != null && oldContext == null)
400                 oldContext = bpContext.Value;
401
402             if (bpContext != null && bpContext.Binding != null)
403             {
404                 bpContext.Binding.Context = value;
405                 bindable.inheritedContext = null;
406             }
407             else
408             {
409                 bindable.inheritedContext = value;
410             }
411
412             bindable.ApplyBindings(skipBindingContext: false, fromBindingContextChanged: true);
413             bindable.OnBindingContextChanged();
414         }
415
416         /// <summary>
417         /// Register the properties which can effect each other in Binding to same group.
418         /// </summary>
419         [EditorBrowsable(EditorBrowsableState.Never)]
420         public static void RegisterPropertyGroup(BindableProperty property, HashSet<BindableProperty> group)
421         {
422             if (!PropertyToGroup.ContainsKey(property))
423             {
424                 PropertyToGroup.Add(property, group);
425             }
426
427             if (null != group && !(group.Contains(property)))
428             {
429                 group.Add(property);
430             }
431         }
432         /// <summary>
433         /// Apply the bindings to BindingContext.
434         /// </summary>
435         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
436         [EditorBrowsable(EditorBrowsableState.Never)]
437         protected void ApplyBindings()
438         {
439             ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false);
440         }
441
442         /// <summary>
443         /// Override this method to execute an action when the BindingContext changes.
444         /// </summary>
445         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
446         [EditorBrowsable(EditorBrowsableState.Never)]
447         protected virtual void OnBindingContextChanged()
448         {
449             BindingContextChanged?.Invoke(this, EventArgs.Empty);
450         }
451
452         /// <summary>
453         /// Call this method from a child class to notify that a change happened on a property.
454         /// </summary>
455         /// <param name="propertyName">The name of the property that changed.</param>
456         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
457         [EditorBrowsable(EditorBrowsableState.Never)]
458         protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
459             => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
460
461         /// <summary>
462         /// Call this method from a child class to notify that a change is going to happen on a property.
463         /// </summary>
464         /// <param name="propertyName">The name of the property that is changing.</param>
465         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
466         [EditorBrowsable(EditorBrowsableState.Never)]
467         protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
468             => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
469         
470         /// <summary>
471         /// Method that is called when a bound property is changed.
472         /// </summary>
473         [EditorBrowsable(EditorBrowsableState.Never)]
474         protected virtual void OnPropertyChangedWithData(BindableProperty prop) { }
475
476         /// <summary>
477         /// Unapplies all previously set bindings.
478         /// </summary>
479         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
480         [EditorBrowsable(EditorBrowsableState.Never)]
481         protected void UnapplyBindings()
482         {
483             foreach (var context in properties.Values)
484             {
485                 if (context.Binding == null)
486                     continue;
487
488                 context.Binding.Unapply();
489             }
490         }
491
492         internal bool GetIsBound(BindableProperty targetProperty)
493         {
494             if (targetProperty == null)
495                 throw new ArgumentNullException(nameof(targetProperty));
496
497             BindablePropertyContext bpcontext = GetContext(targetProperty);
498             return bpcontext != null && bpcontext.Binding != null;
499         }
500
501         /// <summary>
502         /// Returns the value that is contained the BindableProperty.
503         /// </summary>
504         /// <param name="property0">The BindableProperty instance.</param>
505         /// <param name="property1">The BindableProperty instance.</param>
506         /// <returns>The value that is contained the BindableProperty</returns>
507         internal object[] GetValues(BindableProperty property0, BindableProperty property1)
508         {
509             var values = new object[2];
510
511             foreach (var context in properties.Values)
512             {
513                 if (ReferenceEquals(context.Property, property0))
514                 {
515                     values[0] = context.Value;
516                     property0 = null;
517                 }
518                 else if (ReferenceEquals(context.Property, property1))
519                 {
520                     values[1] = context.Value;
521                     property1 = null;
522                 }
523
524                 if (property0 == null && property1 == null)
525                     return values;
526             }
527
528             if (!ReferenceEquals(property0, null))
529                 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
530             if (!ReferenceEquals(property1, null))
531                 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
532
533             return values;
534         }
535
536         /// <summary>
537         /// Returns the value that is contained the BindableProperty.
538         /// </summary>
539         /// <param name="property0">The BindableProperty instance.</param>
540         /// <param name="property1">The BindableProperty instance.</param>
541         /// <param name="property2">The BindableProperty instance.</param>
542         /// <returns>The value that is contained the BindableProperty</returns>
543         internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
544         {
545             var values = new object[3];
546
547             foreach (var context in properties.Values)
548             {
549                 if (ReferenceEquals(context.Property, property0))
550                 {
551                     values[0] = context.Value;
552                     property0 = null;
553                 }
554                 else if (ReferenceEquals(context.Property, property1))
555                 {
556                     values[1] = context.Value;
557                     property1 = null;
558                 }
559                 else if (ReferenceEquals(context.Property, property2))
560                 {
561                     values[2] = context.Value;
562                     property2 = null;
563                 }
564
565                 if (property0 == null && property1 == null && property2 == null)
566                     return values;
567             }
568
569             if (!ReferenceEquals(property0, null))
570                 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
571             if (!ReferenceEquals(property1, null))
572                 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
573             if (!ReferenceEquals(property2, null))
574                 values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
575
576             return values;
577         }
578
579         /// <summary>
580         /// Returns the value that is contained the BindableProperty.
581         /// </summary>
582         /// <param name="properties">The array of the BindableProperty instances</param>
583         /// <returns>The values that is contained the BindableProperty instances.</returns>
584         internal object[] GetValues(params BindableProperty[] properties)
585         {
586             var values = new object[properties.Length];
587             foreach (var context in this.properties.Values)
588             {
589                 var index = properties.IndexOf(context.Property);
590                 if (index < 0)
591                     continue;
592                 values[index] = context.Value;
593             }
594             for (var i = 0; i < values.Length; i++)
595             {
596                 if (!ReferenceEquals(values[i], null))
597                     continue;
598                 values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value;
599             }
600             return values;
601         }
602
603         internal virtual void OnRemoveDynamicResource(BindableProperty property)
604         {
605         }
606
607         internal virtual void OnSetDynamicResource(BindableProperty property, string key)
608         {
609         }
610
611         internal void RemoveDynamicResource(BindableProperty property)
612         {
613             if (property == null)
614                 throw new ArgumentNullException(nameof(property));
615
616             OnRemoveDynamicResource(property);
617             BindablePropertyContext context = GetOrCreateContext(property);
618             context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
619         }
620
621         internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
622         {
623             if (targetProperty == null)
624                 throw new ArgumentNullException(nameof(targetProperty));
625             if (binding == null)
626                 throw new ArgumentNullException(nameof(binding));
627
628             if (fromStyle && !CanBeSetFromStyle(targetProperty))
629                 return;
630
631             IsBinded = true;
632
633             var context = GetOrCreateContext(targetProperty);
634             if (fromStyle)
635                 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
636             else
637                 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
638
639             if (context.Binding != null)
640                 context.Binding.Unapply();
641
642             BindingBase oldBinding = context.Binding;
643             context.Binding = binding;
644
645             targetProperty.BindingChanging?.Invoke(this, oldBinding, binding);
646
647             binding.Apply(BindingContext, this, targetProperty);
648         }
649
650         bool CanBeSetFromStyle(BindableProperty property)
651         {
652             var context = GetContext(property);
653             if (context == null)
654                 return true;
655             if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle)
656                 return true;
657             if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue)
658                 return true;
659             if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated)
660                 return true;
661             return false;
662         }
663
664         internal void SetDynamicResource(BindableProperty property, string key)
665         {
666             SetDynamicResource(property, key, false);
667         }
668
669         internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
670         {
671             if (property == null)
672                 throw new ArgumentNullException(nameof(property));
673             if (string.IsNullOrEmpty(key))
674                 throw new ArgumentNullException(nameof(key));
675             if (fromStyle && !CanBeSetFromStyle(property))
676                 return;
677
678             var context = GetOrCreateContext(property);
679
680             context.Attributes |= BindableContextAttributes.IsDynamicResource;
681             if (fromStyle)
682                 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
683             else
684                 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
685
686             OnSetDynamicResource(property, key);
687         }
688
689         internal void SetValue(BindableProperty property, object value, bool fromStyle)
690         {
691             SetValue(property, value, fromStyle, true);
692         }
693
694         internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
695         {
696             SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None, false);
697         }
698
699         /// <summary>
700         /// For internal use.
701         /// </summary>
702         /// <param name="property">The BindableProperty on which to assign a value.</param>
703         /// <param name="value">The value to set</param>
704         /// <param name="attributes">The set value flag</param>
705         [EditorBrowsable(EditorBrowsableState.Never)]
706         internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
707         {
708             SetValueCore(property, value, attributes, SetValuePrivateFlags.Default, false);
709         }
710
711         internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, bool forceSendChangeSignal)
712         {
713             bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
714             bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
715             bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
716             bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
717             bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
718
719             if (property == null)
720                 throw new ArgumentNullException(nameof(property));
721             if (checkAccess && property.IsReadOnly)
722             {
723                 Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
724                 return;
725             }
726
727             if (!converted && !property.TryConvert(ref value))
728             {
729                 Console.WriteLine($"SetValue : Can not convert {value} to type {property.ReturnType}");
730                 return;
731             }
732
733             if (property.ValidateValue != null && !property.ValidateValue(this, value))
734                 throw new ArgumentException("Value was an invalid value for " + property.PropertyName, nameof(value));
735
736             if (property.CoerceValue != null)
737                 value = property.CoerceValue(this, value);
738
739             BindablePropertyContext context = GetOrCreateContext(property);
740             if (manuallySet)
741             {
742                 context.Attributes |= BindableContextAttributes.IsManuallySet;
743                 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
744             }
745             else
746             {
747                 context.Attributes &= ~BindableContextAttributes.IsManuallySet;
748             }
749
750             if (fromStyle)
751             {
752                 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
753             }
754             // else omitted on purpose
755
756             bool currentlyApplying = applying;
757
758             if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
759             {
760                 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
761                 if (delayQueue == null)
762                     context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
763
764                 delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
765             }
766             else
767             {
768                 context.Attributes |= BindableContextAttributes.IsBeingSet;
769                 SetValueActual(property, context, value, currentlyApplying, forceSendChangeSignal, attributes, silent);
770
771                 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
772                 if (delayQueue != null)
773                 {
774                     while (delayQueue.Count > 0)
775                     {
776                         SetValueArgs s = delayQueue.Dequeue();
777                         SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, forceSendChangeSignal, s.Attributes);
778                     }
779
780                     context.DelayedSetters = null;
781                 }
782
783                 context.Attributes &= ~BindableContextAttributes.IsBeingSet;
784             }
785         }
786
787         internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
788         {
789             var prop = properties.Values.ToArray();
790             for (int i = 0, propLength = prop.Length; i < propLength; i++)
791             {
792                 BindablePropertyContext context = prop[i];
793                 BindingBase binding = context.Binding;
794                 if (binding == null)
795                     continue;
796
797                 if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty))
798                     continue;
799
800                 binding.Unapply(fromBindingContextChanged: fromBindingContextChanged);
801                 binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged);
802             }
803         }
804
805         internal bool IsBinded
806         {
807             get;
808             set;
809         } = false;
810
811         static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
812         {
813             object context = bindable.inheritedContext;
814             var oldBinding = oldBindingBase as Binding;
815             var newBinding = newBindingBase as Binding;
816
817             if (context == null && oldBinding != null)
818                 context = oldBinding.Context;
819             if (context != null && newBinding != null)
820                 newBinding.Context = context;
821         }
822
823         void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
824         {
825             if (property == null)
826                 throw new ArgumentNullException(nameof(property));
827
828             if (checkAccess && property.IsReadOnly)
829                 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
830
831             BindablePropertyContext bpcontext = GetContext(property);
832             if (bpcontext == null)
833                 return;
834
835             if (fromStyle && !CanBeSetFromStyle(property))
836                 return;
837
838             object original = bpcontext.Value;
839
840             object newValue = property.GetDefaultValue(this);
841
842             bool same = Equals(original, newValue);
843             if (!same)
844             {
845                 property.PropertyChanging?.Invoke(this, original, newValue);
846
847                 OnPropertyChanging(property.PropertyName);
848             }
849
850             bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
851             bpcontext.Value = newValue;
852             if (property.DefaultValueCreator == null)
853                 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
854             else
855                 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated;
856
857             if (!same)
858             {
859                 OnPropertyChanged(property.PropertyName);
860                 OnPropertyChangedWithData(property);
861                 property.PropertyChanged?.Invoke(this, original, newValue);
862             }
863         }
864
865         [MethodImpl(MethodImplOptions.AggressiveInlining)]
866         BindablePropertyContext CreateAndAddContext(BindableProperty property)
867         {
868             var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
869
870             if (property.DefaultValueCreator == null)
871                 context.Attributes = BindableContextAttributes.IsDefaultValue;
872             else
873                 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
874
875             properties.Add(property, context);
876             return context;
877         }
878
879         [MethodImpl(MethodImplOptions.AggressiveInlining)]
880         BindablePropertyContext GetContext(BindableProperty property) => properties.TryGetValue(property, out var result) ? result : null;
881
882         [MethodImpl(MethodImplOptions.AggressiveInlining)]
883         BindablePropertyContext GetOrCreateContext(BindableProperty property)
884         {
885             BindablePropertyContext context = GetContext(property);
886             if (context == null)
887             {
888                 context = CreateAndAddContext(property);
889             }
890             else if (property.ValueGetter != null)
891             {
892                 context.Value = property.ValueGetter(this); //Update Value from dali
893             }//added by xiaohui.fang
894             else if (property.DefaultValueCreator != null) //This will be removed in the future.
895             {
896                 context.Value = property.DefaultValueCreator(this); //Update Value from dali
897             }//added by xb.teng
898
899             return context;
900         }
901
902         void RemoveBinding(BindableProperty property, BindablePropertyContext context)
903         {
904             context.Binding.Unapply();
905
906             property.BindingChanging?.Invoke(this, context.Binding, null);
907
908             context.Binding = null;
909         }
910
911         internal void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
912         {
913             if (property == null)
914                 throw new ArgumentNullException(nameof(property));
915
916             if (checkAccess && property.IsReadOnly)
917                 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
918
919             if (fromStyle && !CanBeSetFromStyle(property))
920                 return;
921
922             SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
923                 (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0),
924                 false);
925         }
926
927         void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, bool forceSendChangeSignal, SetValueFlags attributes, bool silent = false)
928         {
929             object original = context.Value;
930             bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
931             bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
932             bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
933             bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
934
935             bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
936             if (!silent && (!same || raiseOnEqual))
937             {
938                 property.PropertyChanging?.Invoke(this, original, value);
939
940                 OnPropertyChanging(property.PropertyName);
941             }
942
943             if (!same || raiseOnEqual)
944             {
945                 context.Value = value;
946             }
947
948             context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
949             context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated;
950
951             if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
952                 RemoveDynamicResource(property);
953
954             BindingBase binding = context.Binding;
955             if (binding != null)
956             {
957                 if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
958                 {
959                     RemoveBinding(property, context);
960                     binding = null;
961                 }
962             }
963
964             PropertyToGroup.TryGetValue(property, out HashSet<BindableProperty> propertyGroup);
965
966             if (!silent)
967             {
968                 if ((!same || raiseOnEqual))
969                 {
970                     property.PropertyChanged?.Invoke(this, original, value);
971
972                     if (binding != null && !currentlyApplying)
973                     {
974                         applying = true;
975                         binding.Apply(true);
976                         applying = false;
977                     }
978
979                     OnPropertyChanged(property.PropertyName);
980
981                     if (null != propertyGroup)
982                     {
983                         foreach (var relativeProperty in propertyGroup)
984                         {
985                             if (relativeProperty != property)
986                             {
987                                 var relativeContext = GetOrCreateContext(relativeProperty);
988                                 var relativeBinding = relativeContext.Binding;
989
990                                 if (null != relativeBinding)
991                                 {
992                                     applying = true;
993                                     relativeBinding.Apply(true);
994                                     applying = false;
995                                 }
996
997                                 OnPropertyChanged(relativeProperty.PropertyName);
998                             }
999                         }
1000                     }
1001                 }
1002                 else if (true == same && true == forceSendChangeSignal)
1003                 {
1004                     if (binding != null && !currentlyApplying)
1005                     {
1006                         applying = true;
1007                         binding.Apply(true);
1008                         applying = false;
1009                     }
1010
1011                     OnPropertyChanged(property.PropertyName);
1012
1013                     if (null != propertyGroup)
1014                     {
1015                         foreach (var relativeProperty in propertyGroup)
1016                         {
1017                             if (relativeProperty != property)
1018                             {
1019                                 var relativeContext = GetOrCreateContext(relativeProperty);
1020                                 var relativeBinding = relativeContext.Binding;
1021
1022                                 if (null != relativeBinding)
1023                                 {
1024                                     applying = true;
1025                                     relativeBinding.Apply(true);
1026                                     applying = false;
1027                                 }
1028
1029                                 OnPropertyChanged(relativeProperty.PropertyName);
1030                             }
1031                         }
1032                     }
1033                 }
1034
1035                 OnPropertyChangedWithData(property);
1036             }
1037         }
1038
1039         private static Dictionary<BindableProperty, HashSet<BindableProperty>> PropertyToGroup { get; }
1040             = new Dictionary<BindableProperty, HashSet<BindableProperty>>();
1041
1042         [Flags]
1043         enum BindableContextAttributes
1044         {
1045             IsManuallySet = 1 << 0,
1046             IsBeingSet = 1 << 1,
1047             IsDynamicResource = 1 << 2,
1048             IsSetFromStyle = 1 << 3,
1049             IsDefaultValue = 1 << 4,
1050             IsDefaultValueCreated = 1 << 5,
1051         }
1052
1053         class BindablePropertyContext
1054         {
1055             public BindableContextAttributes Attributes;
1056             public BindingBase Binding;
1057             public Queue<SetValueArgs> DelayedSetters;
1058             public BindableProperty Property;
1059             public object Value;
1060         }
1061
1062         [Flags]
1063         internal enum SetValuePrivateFlags
1064         {
1065             None = 0,
1066             CheckAccess = 1 << 0,
1067             Silent = 1 << 1,
1068             ManuallySet = 1 << 2,
1069             FromStyle = 1 << 3,
1070             Converted = 1 << 4,
1071             Default = CheckAccess
1072         }
1073
1074         class SetValueArgs
1075         {
1076             public readonly SetValueFlags Attributes;
1077             public readonly BindablePropertyContext Context;
1078             public readonly bool CurrentlyApplying;
1079             public readonly BindableProperty Property;
1080             public readonly object Value;
1081
1082             public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
1083             {
1084                 Property = property;
1085                 Context = context;
1086                 Value = value;
1087                 CurrentlyApplying = currentlyApplying;
1088                 Attributes = attributes;
1089             }
1090         }
1091
1092         internal void AddChildBindableObject(BindableObject child)
1093         {
1094             if (null != child)
1095             {
1096                 children.Add(child);
1097                 child.FlushBinding();
1098             }
1099         }
1100
1101         internal void RemoveChildBindableObject(BindableObject child)
1102         {
1103             children.Remove(child);
1104         }
1105
1106         internal void ReplaceBindingElement(Dictionary<string, object> oldNameScope, Dictionary<string, object> newNameScope)
1107         {
1108             var xElementToNameOfOld = new Dictionary<object, string>();
1109
1110             foreach (var pair in oldNameScope)
1111             {
1112                 xElementToNameOfOld.Add(pair.Value, pair.Key);
1113             }
1114
1115             foreach (var property in properties)
1116             {
1117                 if (property.Value.Binding is Binding binding && null != binding.Source)
1118                 {
1119                     string xName;
1120                     xElementToNameOfOld.TryGetValue(binding.Source, out xName);
1121
1122                     if (null != xName)
1123                     {
1124                         var newObject = newNameScope[xName];
1125                         binding.Unapply();
1126                         binding.Source = newObject;
1127                         SetBinding(property.Key, binding);
1128                     }
1129                 }
1130             }
1131
1132             if (null != BindingContext)
1133             {
1134                 string xName;
1135                 xElementToNameOfOld.TryGetValue(BindingContext, out xName);
1136
1137                 if (null != xName)
1138                 {
1139                     var newObject = newNameScope[xName];
1140                     BindingContext = newObject;
1141                 }
1142             }
1143         }
1144
1145         internal void ClearBinding()
1146         {
1147             foreach (var property in properties)
1148             {
1149                 if (null != property.Value.Binding)
1150                 {
1151                     property.Value.Binding.Unapply();
1152                 }
1153             }
1154         }
1155
1156         private List<BindableObject> children = new List<BindableObject>();
1157
1158         private void FlushBinding()
1159         {
1160             ApplyBindings(skipBindingContext: true, fromBindingContextChanged: true);
1161             OnBindingContextChanged();
1162
1163             foreach (var child in children)
1164             {
1165                 child.FlushBinding();
1166             }
1167         }
1168     }
1169 }
1170
1171 namespace Tizen.NUI.Binding.Internals
1172 {
1173     /// <summary>
1174     /// SetValueFlags. For internal use.
1175     /// </summary>
1176     [Flags]
1177     [EditorBrowsable(EditorBrowsableState.Never)]
1178     public enum SetValueFlags
1179     {
1180         /// <summary>
1181         /// None.
1182         /// </summary>
1183         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1184         [EditorBrowsable(EditorBrowsableState.Never)]
1185         None = 0,
1186
1187         /// <summary>
1188         /// Clear OneWay bindings.
1189         /// </summary>
1190         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1191         [EditorBrowsable(EditorBrowsableState.Never)]
1192         ClearOneWayBindings = 1 << 0,
1193
1194         /// <summary>
1195         /// Clear TwoWay bindings.
1196         /// </summary>
1197         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1198         [EditorBrowsable(EditorBrowsableState.Never)]
1199         ClearTwoWayBindings = 1 << 1,
1200
1201         /// <summary>
1202         /// Clear dynamic resource.
1203         /// </summary>
1204         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1205         [EditorBrowsable(EditorBrowsableState.Never)]
1206         ClearDynamicResource = 1 << 2,
1207
1208         /// <summary>
1209         /// Raise or equal.
1210         /// </summary>
1211         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
1212         [EditorBrowsable(EditorBrowsableState.Never)]
1213         RaiseOnEqual = 1 << 3
1214     }
1215 }