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