-
-
Save jerone/8799f0ba4092a8df4a6c5a9d8cd2dbf0 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 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.Xaml; | |
namespace MultiBindingExample | |
{ | |
/// <remarks> | |
/// Same version as https://gist.github.com/jerone/8799f0ba4092a8df4a6c5a9d8cd2dbf0 | |
/// Which is fork of https://gist.github.com/Keboo/0d6e42028ea9e4256715 | |
/// Which is based on http://intellitect.com/multibinding-in-xamarin-forms/ | |
/// </remarks> | |
[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():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 | |
{ | |
// ReSharper disable once UnusedMember.Local | |
get => _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)) | |
{ | |
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression | |
if (value is object[] array) | |
{ | |
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(); | |
} | |
} | |
} | |
public interface IMultiValueConverter | |
{ | |
object Convert(object[] values, Type targetType, object parameter, CultureInfo culture); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment