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