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