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