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