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