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