Last active
September 17, 2020 07:18
-
-
Save Keboo/0d6e42028ea9e4256715 to your computer and use it in GitHub Desktop.
A simple MultiBinding class for Xamarin.Forms. This "binding" only works when directly applied to an element.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
WARNING: This MultiBinding implementation only works when it is directly applied to its target property. | |
It will fail if used inside of a setter (such is the case when used within a trigger or style). | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Globalization; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Proxy; | |
using Xamarin.Forms.Xaml; | |
namespace MultiBindingExample | |
{ | |
[ContentProperty(nameof(Bindings))] | |
public class MultiBinding : IMarkupExtension<Binding> | |
{ | |
private BindableObject _target; | |
private readonly InternalValue _internalValue = new InternalValue(); | |
private readonly IList<BindableProperty> _properties = new List<BindableProperty>(); | |
public IList<Binding> Bindings { get; } = new List<Binding>(); | |
public string StringFormat { get; set; } | |
public IMultiValueConverter Converter { get; set; } | |
public object ConverterParameter { get; set; } | |
public Binding ProvideValue(IServiceProvider serviceProvider) | |
{ | |
if (string.IsNullOrWhiteSpace(StringFormat) && Converter == null) | |
throw new InvalidOperationException($"{nameof(MultiBinding)} requires a {nameof(Converter)} or {nameof(StringFormat)}"); | |
//Get the object that the markup extension is being applied to | |
var provideValueTarget = (IProvideValueTarget)serviceProvider?.GetService(typeof(IProvideValueTarget)); | |
_target = provideValueTarget?.TargetObject as BindableObject; | |
if (_target == null) return null; | |
foreach (Binding b in Bindings) | |
{ | |
var property = BindableProperty.Create($"Property-{Guid.NewGuid().ToString("N")}", typeof(object), | |
typeof(MultiBinding), default(object), propertyChanged: (_, o, n) => SetValue()); | |
_properties.Add(property); | |
_target.SetBinding(property, b); | |
} | |
SetValue(); | |
var binding = new Binding | |
{ | |
Path = nameof(InternalValue.Value), | |
Converter = new MultiValueConverterWrapper(Converter, StringFormat), | |
ConverterParameter = ConverterParameter, | |
Source = _internalValue | |
}; | |
return binding; | |
} | |
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) | |
{ | |
return ProvideValue(serviceProvider); | |
} | |
private void SetValue() | |
{ | |
if (_target == null) return; | |
_internalValue.Value = _properties.Select(_target.GetValue).ToArray(); | |
} | |
private sealed class InternalValue : INotifyPropertyChanged | |
{ | |
public event PropertyChangedEventHandler PropertyChanged; | |
private object _value; | |
public object Value | |
{ | |
get { return _value; } | |
set | |
{ | |
if (!Equals(_value, value)) | |
{ | |
_value = value; | |
OnPropertyChanged(); | |
} | |
} | |
} | |
private void OnPropertyChanged([CallerMemberName] string propertyName = null) | |
{ | |
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
} | |
private sealed class MultiValueConverterWrapper : IValueConverter | |
{ | |
private readonly IMultiValueConverter _multiValueConverter; | |
private readonly string _stringFormat; | |
public MultiValueConverterWrapper(IMultiValueConverter multiValueConverter, string stringFormat) | |
{ | |
_multiValueConverter = multiValueConverter; | |
_stringFormat = stringFormat; | |
} | |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
if (_multiValueConverter != null) | |
{ | |
value = _multiValueConverter.Convert(value as object[], targetType, parameter, culture); | |
} | |
if (!string.IsNullOrWhiteSpace(_stringFormat)) | |
{ | |
var array = value as object[]; | |
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression | |
if (array != null) | |
{ | |
value = string.Format(_stringFormat, array); | |
} | |
else | |
{ | |
value = string.Format(_stringFormat, value); | |
} | |
} | |
return value; | |
} | |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I use the DataTemplate in "ItemsView"
I use in DataTrigger, but the BindingContext of my control is always null
<DataTemplate> <StackLayout> <Label > <Label.Text> <Binding Path="Index"/> //working normally </Label.Text> </Label> <Button> <Button.Triggers> <DataTrigger TargetType="Button" Value="True"> <DataTrigger.Binding> <local:MultiBinding Converter="{StaticResource tConverter}"> <Binding Source="{x:Reference caruselView}" Path="Position"/> //working normally <Binding Path="Index"/> //Always null </local:MultiBinding> </DataTrigger.Binding> <Setter Property="BackgroundColor" Value="#551133FF"/> </DataTrigger> </Button.Triggers> </Button> </StackLayout> </DataTemplate>