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