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