2016-04-24 19:25:26 +03:00
|
|
|
using System;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
namespace Xamarin.Forms.Pages
|
|
|
|
{
|
|
|
|
public class DataSourceBinding : BindingBase
|
|
|
|
{
|
|
|
|
internal const string SelfPath = ".";
|
|
|
|
IValueConverter _converter;
|
|
|
|
object _converterParameter;
|
|
|
|
WeakReference _dataSourceRef;
|
|
|
|
|
|
|
|
BindingExpression _expression;
|
|
|
|
string _path;
|
|
|
|
|
|
|
|
public DataSourceBinding()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public DataSourceBinding(string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null, string stringFormat = null)
|
|
|
|
{
|
|
|
|
if (path == null)
|
|
|
|
throw new ArgumentNullException(nameof(path));
|
|
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
|
|
throw new ArgumentException("path can not be an empty string", nameof(path));
|
|
|
|
|
|
|
|
AllowChaining = true;
|
|
|
|
Path = path;
|
|
|
|
Converter = converter;
|
|
|
|
ConverterParameter = converterParameter;
|
|
|
|
Mode = mode;
|
|
|
|
StringFormat = stringFormat;
|
|
|
|
}
|
|
|
|
|
|
|
|
public IValueConverter Converter
|
|
|
|
{
|
|
|
|
get { return _converter; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
ThrowIfApplied();
|
|
|
|
|
|
|
|
_converter = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public object ConverterParameter
|
|
|
|
{
|
|
|
|
get { return _converterParameter; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
ThrowIfApplied();
|
|
|
|
|
|
|
|
_converterParameter = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Path
|
|
|
|
{
|
|
|
|
get { return _path; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
ThrowIfApplied();
|
|
|
|
|
|
|
|
_path = value;
|
|
|
|
_expression = GetBindingExpression($"DataSource[{value}]");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override void Apply(bool fromTarget)
|
|
|
|
{
|
|
|
|
base.Apply(fromTarget);
|
|
|
|
|
|
|
|
if (_expression == null)
|
|
|
|
_expression = new BindingExpression(this, SelfPath);
|
|
|
|
|
|
|
|
_expression.Apply(fromTarget);
|
|
|
|
}
|
|
|
|
|
2017-11-21 15:59:41 +03:00
|
|
|
internal override async void Apply(object newContext, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged)
|
2016-04-24 19:25:26 +03:00
|
|
|
{
|
|
|
|
var view = bindObj as VisualElement;
|
|
|
|
if (view == null)
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
2017-11-21 15:59:41 +03:00
|
|
|
base.Apply(newContext, bindObj, targetProperty, fromBindingContextChanged: fromBindingContextChanged);
|
2016-04-24 19:25:26 +03:00
|
|
|
|
|
|
|
Element dataSourceParent = await FindDataSourceParentAsync(view);
|
|
|
|
|
|
|
|
var dataSourceProviderer = (IDataSourceProvider)dataSourceParent;
|
|
|
|
if (dataSourceProviderer != null)
|
|
|
|
_dataSourceRef = new WeakReference(dataSourceProviderer);
|
|
|
|
|
|
|
|
dataSourceProviderer?.MaskKey(_path);
|
|
|
|
ApplyInner(dataSourceParent, bindObj, targetProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override BindingBase Clone()
|
|
|
|
{
|
|
|
|
return new DataSourceBinding(Path, Mode) { Converter = Converter, ConverterParameter = ConverterParameter, StringFormat = StringFormat };
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override object GetSourceValue(object value, Type targetPropertyType)
|
|
|
|
{
|
|
|
|
if (Converter != null)
|
|
|
|
value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
|
|
|
|
|
|
|
|
return base.GetSourceValue(value, targetPropertyType);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal override object GetTargetValue(object value, Type sourcePropertyType)
|
|
|
|
{
|
|
|
|
if (Converter != null)
|
|
|
|
value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
|
|
|
|
|
|
|
|
return base.GetTargetValue(value, sourcePropertyType);
|
|
|
|
}
|
|
|
|
|
2017-11-21 15:59:41 +03:00
|
|
|
internal override void Unapply(bool fromBindingContextChanged = false)
|
2016-04-24 19:25:26 +03:00
|
|
|
{
|
2017-11-21 15:59:41 +03:00
|
|
|
base.Unapply(fromBindingContextChanged: fromBindingContextChanged);
|
2016-04-24 19:25:26 +03:00
|
|
|
|
2016-11-24 22:34:46 +03:00
|
|
|
var dataSourceProviderer = (IDataSourceProvider)_dataSourceRef?.Target;
|
|
|
|
dataSourceProviderer?.UnmaskKey(_path);
|
2016-04-24 19:25:26 +03:00
|
|
|
|
|
|
|
_expression?.Unapply();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyInner(Element templatedParent, BindableObject bindableObject, BindableProperty targetProperty)
|
|
|
|
{
|
|
|
|
if (_expression == null && templatedParent != null)
|
|
|
|
_expression = new BindingExpression(this, SelfPath);
|
|
|
|
|
|
|
|
_expression?.Apply(templatedParent, bindableObject, targetProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
static async Task<Element> FindDataSourceParentAsync(Element element)
|
|
|
|
{
|
|
|
|
while (!Application.IsApplicationOrNull(element))
|
|
|
|
{
|
|
|
|
if (element is IDataSourceProvider)
|
|
|
|
return element;
|
|
|
|
element = await TemplateUtilities.GetRealParentAsync(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingExpression GetBindingExpression(string path)
|
|
|
|
{
|
|
|
|
return new BindingExpression(this, !string.IsNullOrWhiteSpace(path) ? path : SelfPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|