2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Reflection;
6 using System.Runtime.CompilerServices;
7 using Tizen.NUI.Internals;
9 namespace Tizen.NUI.Binding
11 public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
13 public static readonly BindableProperty BindingContextProperty =
14 BindableProperty.Create("BindingContext", typeof(object), typeof(BindableObject), default(object),
15 BindingMode.OneWay, null, BindingContextPropertyChanged, null, null, BindingContextPropertyBindingChanging);
17 readonly List<BindablePropertyContext> _properties = new List<BindablePropertyContext>(4);
20 object _inheritedContext;
22 public object BindingContext
24 get { return _inheritedContext ?? GetValue(BindingContextProperty); }
25 set { SetValue(BindingContextProperty, value); }
28 void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
30 SetDynamicResource(property, key, false);
33 public event PropertyChangedEventHandler PropertyChanged;
35 public event EventHandler BindingContextChanged;
37 internal void ClearValue(BindableProperty property, bool fromStyle)
39 ClearValue(property, fromStyle: fromStyle, checkAccess: true);
42 public void ClearValue(BindableProperty property)
44 ClearValue(property, fromStyle: false, checkAccess: true);
47 public void ClearValue(BindablePropertyKey propertyKey)
49 if (propertyKey == null)
50 throw new ArgumentNullException("propertyKey");
52 ClearValue(propertyKey.BindableProperty, fromStyle:false, checkAccess: false);
55 public bool IsSet(BindableProperty targetProperty)
57 if (targetProperty == null)
58 throw new ArgumentNullException(nameof(targetProperty));
60 var bpcontext = GetContext(targetProperty);
61 return bpcontext != null
62 && (bpcontext.Attributes & BindableContextAttributes.IsDefaultValue) == 0;
65 public object GetValue(BindableProperty property)
68 throw new ArgumentNullException("property");
70 BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
73 return property.DefaultValue;
78 public event PropertyChangingEventHandler PropertyChanging;
80 public void RemoveBinding(BindableProperty property)
83 throw new ArgumentNullException("property");
85 BindablePropertyContext context = GetContext(property);
86 if (context == null || context.Binding == null)
89 RemoveBinding(property, context);
92 public void SetBinding(BindableProperty targetProperty, BindingBase binding)
94 SetBinding(targetProperty, binding, false);
97 public void SetValue(BindableProperty property, object value)
99 SetValue(property, value, false, true);
102 public void SetValue(BindablePropertyKey propertyKey, object value)
104 if (propertyKey == null)
105 throw new ArgumentNullException("propertyKey");
107 SetValue(propertyKey.BindableProperty, value, false, false);
110 [EditorBrowsable(EditorBrowsableState.Never)]
111 public static void SetInheritedBindingContext(BindableObject bindable, object value)
113 BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
114 if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
117 object oldContext = bindable._inheritedContext;
119 if (ReferenceEquals(oldContext, value))
122 if (bpContext != null && oldContext == null)
123 oldContext = bpContext.Value;
125 if (bpContext != null && bpContext.Binding != null)
127 bpContext.Binding.Context = value;
128 bindable._inheritedContext = null;
132 bindable._inheritedContext = value;
135 bindable.ApplyBindings(skipBindingContext:false, fromBindingContextChanged:true);
136 bindable.OnBindingContextChanged();
139 protected void ApplyBindings()
141 ApplyBindings(skipBindingContext: false, fromBindingContextChanged: false);
144 protected virtual void OnBindingContextChanged()
146 BindingContextChanged?.Invoke(this, EventArgs.Empty);
149 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
150 => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
152 protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
153 => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
155 protected void UnapplyBindings()
157 for (int i = 0, _propertiesCount = _properties.Count; i < _propertiesCount; i++) {
158 BindablePropertyContext context = _properties [i];
159 if (context.Binding == null)
162 context.Binding.Unapply();
166 internal bool GetIsBound(BindableProperty targetProperty)
168 if (targetProperty == null)
169 throw new ArgumentNullException("targetProperty");
171 BindablePropertyContext bpcontext = GetContext(targetProperty);
172 return bpcontext != null && bpcontext.Binding != null;
175 [EditorBrowsable(EditorBrowsableState.Never)]
176 public object[] GetValues(BindableProperty property0, BindableProperty property1)
178 var values = new object[2];
180 for (var i = 0; i < _properties.Count; i++)
182 BindablePropertyContext context = _properties[i];
184 if (ReferenceEquals(context.Property, property0))
186 values[0] = context.Value;
189 else if (ReferenceEquals(context.Property, property1))
191 values[1] = context.Value;
195 if (property0 == null && property1 == null)
199 if (!ReferenceEquals(property0, null))
200 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
201 if (!ReferenceEquals(property1, null))
202 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
207 [EditorBrowsable(EditorBrowsableState.Never)]
208 public object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
210 var values = new object[3];
212 for (var i = 0; i < _properties.Count; i++)
214 BindablePropertyContext context = _properties[i];
216 if (ReferenceEquals(context.Property, property0))
218 values[0] = context.Value;
221 else if (ReferenceEquals(context.Property, property1))
223 values[1] = context.Value;
226 else if (ReferenceEquals(context.Property, property2))
228 values[2] = context.Value;
232 if (property0 == null && property1 == null && property2 == null)
236 if (!ReferenceEquals(property0, null))
237 values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
238 if (!ReferenceEquals(property1, null))
239 values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
240 if (!ReferenceEquals(property2, null))
241 values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
246 [EditorBrowsable(EditorBrowsableState.Never)]
247 internal object[] GetValues(params BindableProperty[] properties)
249 var values = new object[properties.Length];
250 for (var i = 0; i < _properties.Count; i++) {
251 var context = _properties[i];
252 var index = properties.IndexOf(context.Property);
255 values[index] = context.Value;
257 for (var i = 0; i < values.Length; i++) {
258 if (!ReferenceEquals(values[i], null))
260 values[i] = properties[i].DefaultValueCreator == null ? properties[i].DefaultValue : CreateAndAddContext(properties[i]).Value;
265 internal virtual void OnRemoveDynamicResource(BindableProperty property)
269 internal virtual void OnSetDynamicResource(BindableProperty property, string key)
273 internal void RemoveDynamicResource(BindableProperty property)
275 if (property == null)
276 throw new ArgumentNullException("property");
278 OnRemoveDynamicResource(property);
279 BindablePropertyContext context = GetOrCreateContext(property);
280 context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
283 internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
285 if (targetProperty == null)
286 throw new ArgumentNullException("targetProperty");
288 throw new ArgumentNullException("binding");
290 if (fromStyle && !CanBeSetFromStyle(targetProperty))
293 var context = GetOrCreateContext(targetProperty);
295 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
297 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
299 if (context.Binding != null)
300 context.Binding.Unapply();
302 BindingBase oldBinding = context.Binding;
303 context.Binding = binding;
305 targetProperty.BindingChanging?.Invoke(this, oldBinding, binding);
307 binding.Apply(BindingContext, this, targetProperty);
310 bool CanBeSetFromStyle(BindableProperty property)
312 var context = GetContext(property);
315 if ((context.Attributes & BindableContextAttributes.IsSetFromStyle) == BindableContextAttributes.IsSetFromStyle)
317 if ((context.Attributes & BindableContextAttributes.IsDefaultValue) == BindableContextAttributes.IsDefaultValue)
319 if ((context.Attributes & BindableContextAttributes.IsDefaultValueCreated) == BindableContextAttributes.IsDefaultValueCreated)
324 internal void SetDynamicResource(BindableProperty property, string key)
326 SetDynamicResource(property, key, false);
329 internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
331 if (property == null)
332 throw new ArgumentNullException(nameof(property));
333 if (string.IsNullOrEmpty(key))
334 throw new ArgumentNullException(nameof(key));
335 if (fromStyle && !CanBeSetFromStyle(property))
338 var context = GetOrCreateContext(property);
340 context.Attributes |= BindableContextAttributes.IsDynamicResource;
342 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
344 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
346 OnSetDynamicResource(property, key);
349 internal void SetValue(BindableProperty property, object value, bool fromStyle)
351 SetValue(property, value, fromStyle, true);
354 internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
356 SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None);
359 [EditorBrowsable(EditorBrowsableState.Never)]
360 public void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
362 SetValueCore(property, value, attributes, SetValuePrivateFlags.Default);
365 internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
367 bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
368 bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
369 bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
370 bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
371 bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
373 if (property == null)
374 throw new ArgumentNullException("property");
375 if (checkAccess && property.IsReadOnly)
377 Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
381 if (!converted && !property.TryConvert(ref value))
383 Console.WriteLine("SetValue", "Can not convert {0} to type '{1}'", value, property.ReturnType);
387 if (property.ValidateValue != null && !property.ValidateValue(this, value))
388 throw new ArgumentException("Value was an invalid value for " + property.PropertyName, "value");
390 if (property.CoerceValue != null)
391 value = property.CoerceValue(this, value);
393 BindablePropertyContext context = GetOrCreateContext(property);
395 context.Attributes |= BindableContextAttributes.IsManuallySet;
396 context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
398 context.Attributes &= ~BindableContextAttributes.IsManuallySet;
401 context.Attributes |= BindableContextAttributes.IsSetFromStyle;
402 // else omitted on purpose
404 bool currentlyApplying = _applying;
406 if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
408 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
409 if (delayQueue == null)
410 context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
412 delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
416 context.Attributes |= BindableContextAttributes.IsBeingSet;
417 SetValueActual(property, context, value, currentlyApplying, attributes, silent);
419 Queue<SetValueArgs> delayQueue = context.DelayedSetters;
420 if (delayQueue != null)
422 while (delayQueue.Count > 0)
424 SetValueArgs s = delayQueue.Dequeue();
425 SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, s.Attributes);
428 context.DelayedSetters = null;
431 context.Attributes &= ~BindableContextAttributes.IsBeingSet;
435 internal void ApplyBindings(bool skipBindingContext, bool fromBindingContextChanged)
437 var prop = _properties.ToArray();
438 for (int i = 0, propLength = prop.Length; i < propLength; i++) {
439 BindablePropertyContext context = prop [i];
440 BindingBase binding = context.Binding;
444 if (skipBindingContext && ReferenceEquals(context.Property, BindingContextProperty))
447 binding.Unapply(fromBindingContextChanged: fromBindingContextChanged);
448 binding.Apply(BindingContext, this, context.Property, fromBindingContextChanged: fromBindingContextChanged);
452 static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
454 object context = bindable._inheritedContext;
455 var oldBinding = oldBindingBase as Binding;
456 var newBinding = newBindingBase as Binding;
458 if (context == null && oldBinding != null)
459 context = oldBinding.Context;
460 if (context != null && newBinding != null)
461 newBinding.Context = context;
464 static void BindingContextPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
466 bindable._inheritedContext = null;
467 bindable.ApplyBindings(skipBindingContext: true, fromBindingContextChanged:true);
468 bindable.OnBindingContextChanged();
471 void ClearValue(BindableProperty property, bool fromStyle, bool checkAccess)
473 if (property == null)
474 throw new ArgumentNullException(nameof(property));
476 if (checkAccess && property.IsReadOnly)
477 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
479 BindablePropertyContext bpcontext = GetContext(property);
480 if (bpcontext == null)
483 if (fromStyle && !CanBeSetFromStyle(property))
486 object original = bpcontext.Value;
488 object newValue = property.GetDefaultValue(this);
490 bool same = Equals(original, newValue);
493 property.PropertyChanging?.Invoke(this, original, newValue);
495 OnPropertyChanging(property.PropertyName);
498 bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
499 bpcontext.Value = newValue;
500 if (property.DefaultValueCreator == null)
501 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
503 bpcontext.Attributes |= BindableContextAttributes.IsDefaultValueCreated;
507 OnPropertyChanged(property.PropertyName);
508 property.PropertyChanged?.Invoke(this, original, newValue);
512 [MethodImpl(MethodImplOptions.AggressiveInlining)]
513 BindablePropertyContext CreateAndAddContext(BindableProperty property)
515 var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
517 if (property.DefaultValueCreator == null)
518 context.Attributes = BindableContextAttributes.IsDefaultValue;
520 context.Attributes = BindableContextAttributes.IsDefaultValueCreated;
522 _properties.Add(context);
526 [MethodImpl(MethodImplOptions.AggressiveInlining)]
527 BindablePropertyContext GetContext(BindableProperty property)
529 List<BindablePropertyContext> properties = _properties;
531 for (var i = 0; i < properties.Count; i++)
533 BindablePropertyContext context = properties[i];
534 if (ReferenceEquals(context.Property, property))
541 [MethodImpl(MethodImplOptions.AggressiveInlining)]
542 BindablePropertyContext GetOrCreateContext(BindableProperty property)
544 BindablePropertyContext context = GetContext(property);
547 context = CreateAndAddContext(property);
553 void RemoveBinding(BindableProperty property, BindablePropertyContext context)
555 context.Binding.Unapply();
557 property.BindingChanging?.Invoke(this, context.Binding, null);
559 context.Binding = null;
562 void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
564 if (property == null)
565 throw new ArgumentNullException("property");
567 if (checkAccess && property.IsReadOnly)
568 throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
570 if (fromStyle && !CanBeSetFromStyle(property))
573 SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
574 (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0));
577 void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
579 object original = context.Value;
580 bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
581 bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
582 bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
583 bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
585 bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
586 if (!silent && (!same || raiseOnEqual))
588 property.PropertyChanging?.Invoke(this, original, value);
590 OnPropertyChanging(property.PropertyName);
593 if (!same || raiseOnEqual)
595 context.Value = value;
598 context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
599 context.Attributes &= ~BindableContextAttributes.IsDefaultValueCreated;
601 if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
602 RemoveDynamicResource(property);
604 BindingBase binding = context.Binding;
607 if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
609 RemoveBinding(property, context);
614 if (!silent && (!same || raiseOnEqual))
616 if (binding != null && !currentlyApplying)
623 OnPropertyChanged(property.PropertyName);
625 property.PropertyChanged?.Invoke(this, original, value);
630 enum BindableContextAttributes
632 IsManuallySet = 1 << 0,
634 IsDynamicResource = 1 << 2,
635 IsSetFromStyle = 1 << 3,
636 IsDefaultValue = 1 << 4,
637 IsDefaultValueCreated = 1 << 5,
640 class BindablePropertyContext
642 public BindableContextAttributes Attributes;
643 public BindingBase Binding;
644 public Queue<SetValueArgs> DelayedSetters;
645 public BindableProperty Property;
650 internal enum SetValuePrivateFlags
653 CheckAccess = 1 << 0,
655 ManuallySet = 1 << 2,
658 Default = CheckAccess
663 public readonly SetValueFlags Attributes;
664 public readonly BindablePropertyContext Context;
665 public readonly bool CurrentlyApplying;
666 public readonly BindableProperty Property;
667 public readonly object Value;
669 public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
674 CurrentlyApplying = currentlyApplying;
675 Attributes = attributes;
681 namespace Tizen.NUI.Internals
684 [EditorBrowsable(EditorBrowsableState.Never)]
685 public enum SetValueFlags
688 ClearOneWayBindings = 1 << 0,
689 ClearTwoWayBindings = 1 << 1,
690 ClearDynamicResource = 1 << 2,
691 RaiseOnEqual = 1 << 3