From e6351e82697798355552bf6ca60e0e68ae30df6e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 27 Oct 2015 00:06:46 +0100 Subject: [PATCH] Added basic implementation of MultiBinding. Just enough to allow TextBox's floating watermark logic to work. --- samples/XamlTestApplicationPcl/TextBox.paml | 12 ++- .../Context/PerspexXamlMemberValuePlugin.cs | 10 +-- .../Perspex.Markup.Xaml/Data/Binding.cs | 2 +- .../Perspex.Markup.Xaml/Data/IBinding.cs | 7 ++ .../Perspex.Markup.Xaml/Data/MultiBinding.cs | 90 +++++++++++++++++++ .../Perspex.Markup.Xaml.csproj | 2 + src/Markup/Perspex.Markup/BoolConverters.cs | 19 ++++ .../Perspex.Markup/FuncMultiValueConverter.cs | 36 ++++++++ .../Perspex.Markup/FuncValueConverter.cs | 4 +- .../Perspex.Markup/IMultiValueConverter.cs | 30 +++++++ .../Perspex.Markup/Perspex.Markup.csproj | 3 + src/Markup/Perspex.Markup/StringConverters.cs | 6 ++ 12 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 src/Markup/Perspex.Markup.Xaml/Data/IBinding.cs create mode 100644 src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs create mode 100644 src/Markup/Perspex.Markup/BoolConverters.cs create mode 100644 src/Markup/Perspex.Markup/FuncMultiValueConverter.cs create mode 100644 src/Markup/Perspex.Markup/IMultiValueConverter.cs diff --git a/samples/XamlTestApplicationPcl/TextBox.paml b/samples/XamlTestApplicationPcl/TextBox.paml index 4bf04f9bee..63805d8b8f 100644 --- a/samples/XamlTestApplicationPcl/TextBox.paml +++ b/samples/XamlTestApplicationPcl/TextBox.paml @@ -20,8 +20,16 @@ + Text="{TemplateBinding Watermark}"> + + + + + + Bindings { get; } = new List(); + public IMultiValueConverter Converter { get; set; } + public BindingMode Mode { get; set; } + public BindingPriority Priority { get; set; } + public RelativeSource RelativeSource { get; set; } + public string SourcePropertyPath { get; set; } + + public void Bind(IObservablePropertyBag instance, PerspexProperty property) + { + var subject = CreateSubject(instance, property); + + if (subject != null) + { + Bind(instance, property, subject); + } + } + + public ISubject CreateSubject( + IObservablePropertyBag instance, + PerspexProperty property) + { + if (Converter == null) + { + throw new NotSupportedException("MultiBinding without Converter not currently supported."); + } + + var result = new Subject(); + var children = Bindings.Select(x => x.CreateExpressionSubject(instance, property)); + var input = Observable.CombineLatest(children).Select(x => + Converter.Convert(x, property.PropertyType, null, CultureInfo.CurrentUICulture)); + input.Subscribe(result); + return result; + } + + internal void Bind(IObservablePropertyBag target, PerspexProperty property, ISubject subject) + { + var mode = Mode == BindingMode.Default ? + property.DefaultBindingMode : Mode; + + switch (mode) + { + case BindingMode.Default: + case BindingMode.OneWay: + target.Bind(property, subject, Priority); + break; + case BindingMode.TwoWay: + throw new NotSupportedException("TwoWay MultiBinding not currently supported."); + case BindingMode.OneTime: + target.GetObservable(Control.DataContextProperty).Subscribe(dataContext => + { + subject.Take(1).Subscribe(x => target.SetValue(property, x, Priority)); + }); + break; + case BindingMode.OneWayToSource: + target.GetObservable(property).Subscribe(subject); + break; + } + } + } +} \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj index 99e914609a..600e3c4fb3 100644 --- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj +++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj @@ -38,6 +38,8 @@ Properties\SharedAssemblyInfo.cs + + diff --git a/src/Markup/Perspex.Markup/BoolConverters.cs b/src/Markup/Perspex.Markup/BoolConverters.cs new file mode 100644 index 0000000000..80380c12da --- /dev/null +++ b/src/Markup/Perspex.Markup/BoolConverters.cs @@ -0,0 +1,19 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Linq; + +namespace Perspex.Markup +{ + /// + /// Provides a set of useful s for working with string values. + /// + public static class BoolConverters + { + /// + /// A multi-value converter that returns true if all inputs are true. + /// + public static readonly IMultiValueConverter And = + new FuncMultiValueConverter(x => x.All(y => y)); + } +} diff --git a/src/Markup/Perspex.Markup/FuncMultiValueConverter.cs b/src/Markup/Perspex.Markup/FuncMultiValueConverter.cs new file mode 100644 index 0000000000..2113e1a084 --- /dev/null +++ b/src/Markup/Perspex.Markup/FuncMultiValueConverter.cs @@ -0,0 +1,36 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Perspex.Markup +{ + /// + /// A general purpose that uses a + /// to provide the converter logic. + /// + /// The type of the inputs. + /// The output type. + public class FuncMultiValueConverter : IMultiValueConverter + { + private Func, TOut> _convert; + + /// + /// Initializes a new instance of the class. + /// + /// The convert function. + public FuncMultiValueConverter(Func, TOut> convert) + { + _convert = convert; + } + + /// + public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + { + return _convert(values.OfType()); + } + } +} diff --git a/src/Markup/Perspex.Markup/FuncValueConverter.cs b/src/Markup/Perspex.Markup/FuncValueConverter.cs index 6b64b48821..74059a3c3e 100644 --- a/src/Markup/Perspex.Markup/FuncValueConverter.cs +++ b/src/Markup/Perspex.Markup/FuncValueConverter.cs @@ -11,8 +11,8 @@ namespace Perspex.Markup /// A general purpose that uses a /// to provide the converter logic. /// - /// - /// + /// The input type. + /// The output type. public class FuncValueConverter : IValueConverter { private Func _convert; diff --git a/src/Markup/Perspex.Markup/IMultiValueConverter.cs b/src/Markup/Perspex.Markup/IMultiValueConverter.cs new file mode 100644 index 0000000000..68dd5f0098 --- /dev/null +++ b/src/Markup/Perspex.Markup/IMultiValueConverter.cs @@ -0,0 +1,30 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Perspex.Markup +{ + /// + /// Converts multi-binding inputs to a final value. + /// + public interface IMultiValueConverter + { + /// + /// Converts multi-binding inputs to a final value. + /// + /// The values to convert. + /// The type of the target. + /// A user-defined parameter. + /// The culture to use. + /// The converted value. + /// + /// This method should not throw exceptions. If the value is not convertible, return + /// . Any exception thrown will be treated as + /// an application exception. + /// + object Convert(IList values, Type targetType, object parameter, CultureInfo culture); + } +} diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index 702f3f9a82..de0d8e6ce3 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -52,7 +52,10 @@ + + + diff --git a/src/Markup/Perspex.Markup/StringConverters.cs b/src/Markup/Perspex.Markup/StringConverters.cs index 3cc3c68b9f..da4be76a90 100644 --- a/src/Markup/Perspex.Markup/StringConverters.cs +++ b/src/Markup/Perspex.Markup/StringConverters.cs @@ -17,5 +17,11 @@ namespace Perspex.Markup /// public static readonly IValueConverter NullOrEmpty = new FuncValueConverter(x => string.IsNullOrEmpty(x)); + + /// + /// A value converter that returns true if the input string is not null or empty. + /// + public static readonly IValueConverter NotNullOrEmpty = + new FuncValueConverter(x => !string.IsNullOrEmpty(x)); } }