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