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