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