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