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 internal abstract class TypedBindingBase : BindingBase
14 IValueConverter _converter;
15 object _converterParameter;
17 string _updateSourceEventName;
19 public IValueConverter Converter {
20 get { return _converter; }
27 public object ConverterParameter {
28 get { return _converterParameter; }
31 _converterParameter = value;
35 public object Source {
36 get { return _source; }
43 internal string UpdateSourceEventName {
44 get { return _updateSourceEventName; }
47 _updateSourceEventName = value;
51 internal TypedBindingBase()
56 internal sealed class TypedBinding<TSource, TProperty> : TypedBindingBase
58 readonly Func<TSource, TProperty> _getter;
59 readonly Action<TSource, TProperty> _setter;
60 readonly PropertyChangedProxy [] _handlers;
62 public TypedBinding(Func<TSource, TProperty> getter, Action<TSource, TProperty> setter, Tuple<Func<TSource, object>, string> [] handlers)
65 throw new ArgumentNullException(nameof(getter));
73 _handlers = new PropertyChangedProxy [handlers.Length];
74 for (var i = 0; i < handlers.Length; i++)
75 _handlers [i] = new PropertyChangedProxy(handlers [i].Item1, handlers [i].Item2, this);
78 readonly WeakReference<object> _weakSource = new WeakReference<object>(null);
79 readonly WeakReference<BindableObject> _weakTarget = new WeakReference<BindableObject>(null);
80 BindableProperty _targetProperty;
82 // Applies the binding to a previously set source and target.
83 internal override void Apply(bool fromTarget = false)
85 base.Apply(fromTarget);
87 BindableObject target;
88 #if DO_NOT_CHECK_FOR_BINDING_REUSE
89 if (!_weakTarget.TryGetTarget(out target))
90 throw new InvalidOperationException();
92 if (!_weakTarget.TryGetTarget(out target) || target == null) {
98 if (_weakSource.TryGetTarget(out source) && source != null)
99 ApplyCore(source, target, _targetProperty, fromTarget);
102 // Applies the binding to a new source or target.
103 internal override void Apply(object context, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged = false)
105 _targetProperty = targetProperty;
106 var source = Source ?? Context ?? context;
107 var isApplied = IsApplied;
109 if (Source != null && isApplied && fromBindingContextChanged)
112 base.Apply(source, bindObj, targetProperty, fromBindingContextChanged);
114 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
115 BindableObject prevTarget;
116 if (_weakTarget.TryGetTarget(out prevTarget) && !ReferenceEquals(prevTarget, bindObj))
117 throw new InvalidOperationException("Binding instances can not be reused");
119 object previousSource;
120 if (_weakSource.TryGetTarget(out previousSource) && !ReferenceEquals(previousSource, source))
121 throw new InvalidOperationException("Binding instances can not be reused");
123 _weakSource.SetTarget(source);
124 _weakTarget.SetTarget(bindObj);
126 ApplyCore(source, bindObj, targetProperty);
129 internal override BindingBase Clone()
131 Tuple<Func<TSource, object>, string> [] handlers = _handlers == null ? null : new Tuple<Func<TSource, object>, string> [_handlers.Length];
132 if (handlers != null) {
133 for (var i = 0; i < _handlers.Length; i++)
134 handlers [i] = new Tuple<Func<TSource, object>, string>(_handlers [i].PartGetter, _handlers [i].PropertyName);
136 return new TypedBinding<TSource, TProperty>(_getter, _setter, handlers) {
138 Converter = Converter,
139 ConverterParameter = ConverterParameter,
140 StringFormat = StringFormat,
142 UpdateSourceEventName = UpdateSourceEventName,
146 internal override object GetSourceValue(object value, Type targetPropertyType)
148 if (Converter != null)
149 value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
151 //return base.GetSourceValue(value, targetPropertyType);
152 if (StringFormat != null)
153 return string.Format(StringFormat, value);
158 internal override object GetTargetValue(object value, Type sourcePropertyType)
160 if (Converter != null)
161 value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
163 //return base.GetTargetValue(value, sourcePropertyType);
167 internal override void Unapply(bool fromBindingContextChanged = false)
169 if (Source != null && fromBindingContextChanged && IsApplied)
172 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
173 base.Unapply(fromBindingContextChanged:fromBindingContextChanged);
175 if (_handlers != null)
178 #if (!DO_NOT_CHECK_FOR_BINDING_REUSE)
179 _weakSource.SetTarget(null);
180 _weakTarget.SetTarget(null);
184 // ApplyCore is as slim as it should be:
185 // Setting 100000 values : 17ms.
186 // ApplyCore 100000 (w/o INPC, w/o unnapply) : 20ms.
187 internal void ApplyCore(object sourceObject, BindableObject target, BindableProperty property, bool fromTarget = false)
189 var isTSource = sourceObject != null && sourceObject is TSource;
190 var mode = this.GetRealizedMode(property);
191 if ((mode == BindingMode.OneWay || mode == BindingMode.OneTime) && fromTarget)
194 var needsGetter = (mode == BindingMode.TwoWay && !fromTarget) || mode == BindingMode.OneWay || mode == BindingMode.OneTime;
196 if (isTSource && (mode == BindingMode.OneWay || mode == BindingMode.TwoWay) && _handlers != null)
197 Subscribe((TSource)sourceObject);
200 var value = property.DefaultValue;
203 value = GetSourceValue(_getter((TSource)sourceObject), property.ReturnType);
204 } catch (Exception ex) when (ex is NullReferenceException || ex is KeyNotFoundException) {
207 if (!TryConvert(ref value, property, property.ReturnType, true)) {
208 // Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, property.ReturnType);
211 target.SetValueCore(property, value, SetValueFlags.ClearDynamicResource, BindableObject.SetValuePrivateFlags.Default | BindableObject.SetValuePrivateFlags.Converted, false);
215 var needsSetter = (mode == BindingMode.TwoWay && fromTarget) || mode == BindingMode.OneWayToSource;
216 if (needsSetter && _setter != null && isTSource) {
217 var value = GetTargetValue(target.GetValue(property), typeof(TProperty));
218 if (!TryConvert(ref value, property, typeof(TProperty), false)) {
219 // Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, typeof(TProperty));
222 _setter((TSource)sourceObject, (TProperty)value);
226 static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget)
230 if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value)))
233 object original = value;
235 value = Convert.ChangeType(value, convertTo, CultureInfo.InvariantCulture);
237 } catch (Exception ex ) when (ex is InvalidCastException || ex is FormatException||ex is OverflowException) {
243 class PropertyChangedProxy
245 public Func<TSource, object> PartGetter { get; }
246 public string PropertyName { get; }
247 public BindingExpression.WeakPropertyChangedProxy Listener { get; }
248 WeakReference<INotifyPropertyChanged> _weakPart = new WeakReference<INotifyPropertyChanged>(null);
249 readonly BindingBase _binding;
251 public INotifyPropertyChanged Part {
253 INotifyPropertyChanged target;
254 if (_weakPart.TryGetTarget(out target))
259 _weakPart.SetTarget(value);
260 Listener.SubscribeTo(value, OnPropertyChanged);
264 public PropertyChangedProxy(Func<TSource, object> partGetter, string propertyName, BindingBase binding)
266 PartGetter = partGetter;
267 PropertyName = propertyName;
269 Listener = new BindingExpression.WeakPropertyChangedProxy();
272 void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
274 if (!string.IsNullOrEmpty(e.PropertyName) && string.CompareOrdinal(e.PropertyName, PropertyName) != 0)
276 Device.BeginInvokeOnMainThread(() => _binding.Apply(false));
280 void Subscribe(TSource sourceObject)
282 for (var i = 0; i < _handlers.Length; i++) {
283 var part = _handlers [i].PartGetter(sourceObject);
286 var inpc = part as INotifyPropertyChanged;
289 _handlers [i].Part = (inpc);
295 for (var i = 0; i < _handlers.Length; i++)
296 _handlers [i].Listener.Unsubscribe();