1 #define DO_NOT_CHECK_FOR_BINDING_REUSE
4 using System.ComponentModel;
5 using System.Globalization;
6 using System.Collections.Generic;
7 using Tizen.NUI.Binding;
9 namespace Tizen.NUI.Binding.Internals
11 //FIXME: need a better name for this, and share with Binding, so we can share more unittests
12 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
13 [EditorBrowsable(EditorBrowsableState.Never)]
14 public abstract class TypedBindingBase : BindingBase
16 IValueConverter _converter;
17 object _converterParameter;
19 string _updateSourceEventName;
21 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
22 [EditorBrowsable(EditorBrowsableState.Never)]
23 public IValueConverter Converter
25 get { return _converter; }
33 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
34 [EditorBrowsable(EditorBrowsableState.Never)]
35 public object ConverterParameter
37 get { return _converterParameter; }
41 _converterParameter = value;
45 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
46 [EditorBrowsable(EditorBrowsableState.Never)]
49 get { return _source; }
57 internal string UpdateSourceEventName
59 get { return _updateSourceEventName; }
63 _updateSourceEventName = value;
67 internal TypedBindingBase()
72 internal sealed class TypedBinding<TSource, TProperty> : TypedBindingBase
74 readonly Func<TSource, TProperty> _getter;
75 readonly Action<TSource, TProperty> _setter;
76 readonly PropertyChangedProxy[] _handlers;
78 public TypedBinding(Func<TSource, TProperty> getter, Action<TSource, TProperty> setter, Tuple<Func<TSource, object>, string>[] handlers)
81 throw new ArgumentNullException(nameof(getter));
89 _handlers = new PropertyChangedProxy[handlers.Length];
90 for (var i = 0; i < handlers.Length; i++)
91 _handlers[i] = new PropertyChangedProxy(handlers[i].Item1, handlers[i].Item2, this);
94 readonly WeakReference<object> _weakSource = new WeakReference<object>(null);
95 readonly WeakReference<BindableObject> _weakTarget = new WeakReference<BindableObject>(null);
96 BindableProperty _targetProperty;
98 // Applies the binding to a previously set source and target.
99 internal override void Apply(bool fromTarget = false)
101 base.Apply(fromTarget);
103 BindableObject target;
104 #if DO_NOT_CHECK_FOR_BINDING_REUSE
105 if (!_weakTarget.TryGetTarget(out target))
106 throw new InvalidOperationException();
108 if (!_weakTarget.TryGetTarget(out target) || target == null) {
114 if (_weakSource.TryGetTarget(out source) && source != null)
115 ApplyCore(source, target, _targetProperty, fromTarget);
118 // Applies the binding to a new source or target.
119 internal override void Apply(object context, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged = false)
121 _targetProperty = targetProperty;
122 var source = Source ?? Context ?? context;
123 var isApplied = IsApplied;
125 if (Source != null && isApplied && fromBindingContextChanged)
128 base.Apply(source, bindObj, targetProperty, fromBindingContextChanged);
130 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
131 BindableObject prevTarget;
132 if (_weakTarget.TryGetTarget(out prevTarget) && !ReferenceEquals(prevTarget, bindObj))
133 throw new InvalidOperationException("Binding instances can not be reused");
135 object previousSource;
136 if (_weakSource.TryGetTarget(out previousSource) && !ReferenceEquals(previousSource, source))
137 throw new InvalidOperationException("Binding instances can not be reused");
139 _weakSource.SetTarget(source);
140 _weakTarget.SetTarget(bindObj);
142 ApplyCore(source, bindObj, targetProperty);
145 internal override BindingBase Clone()
147 Tuple<Func<TSource, object>, string>[] handlers = _handlers == null ? null : new Tuple<Func<TSource, object>, string>[_handlers.Length];
148 if (handlers != null)
150 for (var i = 0; i < _handlers.Length; i++)
151 handlers[i] = new Tuple<Func<TSource, object>, string>(_handlers[i].PartGetter, _handlers[i].PropertyName);
153 return new TypedBinding<TSource, TProperty>(_getter, _setter, handlers)
156 Converter = Converter,
157 ConverterParameter = ConverterParameter,
158 StringFormat = StringFormat,
160 UpdateSourceEventName = UpdateSourceEventName,
164 internal override object GetSourceValue(object value, Type targetPropertyType)
166 if (Converter != null)
167 value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
169 //return base.GetSourceValue(value, targetPropertyType);
170 if (StringFormat != null)
171 return string.Format(StringFormat, value);
176 internal override object GetTargetValue(object value, Type sourcePropertyType)
178 if (Converter != null)
179 value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
181 //return base.GetTargetValue(value, sourcePropertyType);
185 internal override void Unapply(bool fromBindingContextChanged = false)
187 if (Source != null && fromBindingContextChanged && IsApplied)
190 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
191 base.Unapply(fromBindingContextChanged:fromBindingContextChanged);
193 if (_handlers != null)
196 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
197 _weakSource.SetTarget(null);
198 _weakTarget.SetTarget(null);
202 // ApplyCore is as slim as it should be:
203 // Setting 100000 values : 17ms.
204 // ApplyCore 100000 (w/o INPC, w/o unnapply) : 20ms.
205 internal void ApplyCore(object sourceObject, BindableObject target, BindableProperty property, bool fromTarget = false)
207 var isTSource = sourceObject != null && sourceObject is TSource;
208 var mode = this.GetRealizedMode(property);
209 if ((mode == BindingMode.OneWay || mode == BindingMode.OneTime) && fromTarget)
212 var needsGetter = (mode == BindingMode.TwoWay && !fromTarget) || mode == BindingMode.OneWay || mode == BindingMode.OneTime;
214 if (isTSource && (mode == BindingMode.OneWay || mode == BindingMode.TwoWay) && _handlers != null)
215 Subscribe((TSource)sourceObject);
219 var value = property.DefaultValue;
224 value = GetSourceValue(_getter((TSource)sourceObject), property.ReturnType);
226 catch (Exception ex) when (ex is NullReferenceException || ex is KeyNotFoundException)
230 if (!TryConvert(ref value, property, property.ReturnType, true))
232 // Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, property.ReturnType);
235 target.SetValueCore(property, value, SetValueFlags.ClearDynamicResource, BindableObject.SetValuePrivateFlags.Default | BindableObject.SetValuePrivateFlags.Converted, false);
239 var needsSetter = (mode == BindingMode.TwoWay && fromTarget) || mode == BindingMode.OneWayToSource;
240 if (needsSetter && _setter != null && isTSource)
242 var value = GetTargetValue(target.GetValue(property), typeof(TProperty));
243 if (!TryConvert(ref value, property, typeof(TProperty), false))
245 // Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, typeof(TProperty));
248 _setter((TSource)sourceObject, (TProperty)value);
252 static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget)
256 if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value)))
259 object original = value;
262 value = Convert.ChangeType(value, convertTo, CultureInfo.InvariantCulture);
265 catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is OverflowException)
272 class PropertyChangedProxy
274 public Func<TSource, object> PartGetter { get; }
275 public string PropertyName { get; }
276 public BindingExpression.WeakPropertyChangedProxy Listener { get; }
277 WeakReference<INotifyPropertyChanged> _weakPart = new WeakReference<INotifyPropertyChanged>(null);
278 readonly BindingBase _binding;
280 public INotifyPropertyChanged Part
284 INotifyPropertyChanged target;
285 if (_weakPart.TryGetTarget(out target))
291 _weakPart.SetTarget(value);
292 Listener.SubscribeTo(value, OnPropertyChanged);
296 public PropertyChangedProxy(Func<TSource, object> partGetter, string propertyName, BindingBase binding)
298 PartGetter = partGetter;
299 PropertyName = propertyName;
301 Listener = new BindingExpression.WeakPropertyChangedProxy();
304 void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
306 if (!string.IsNullOrEmpty(e.PropertyName) && string.CompareOrdinal(e.PropertyName, PropertyName) != 0)
308 Device.BeginInvokeOnMainThread(() => _binding.Apply(false));
312 void Subscribe(TSource sourceObject)
314 for (var i = 0; i < _handlers.Length; i++)
316 var part = _handlers[i].PartGetter(sourceObject);
319 var inpc = part as INotifyPropertyChanged;
322 _handlers[i].Part = (inpc);
328 for (var i = 0; i < _handlers.Length; i++)
329 _handlers[i].Listener.Unsubscribe();