Browse Source

Added basic implementation of MultiBinding.

Just enough to allow TextBox's floating watermark logic to work.
pull/297/head
Steven Kirk 11 years ago
parent
commit
e6351e8269
  1. 12
      samples/XamlTestApplicationPcl/TextBox.paml
  2. 10
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
  3. 2
      src/Markup/Perspex.Markup.Xaml/Data/Binding.cs
  4. 7
      src/Markup/Perspex.Markup.Xaml/Data/IBinding.cs
  5. 90
      src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs
  6. 2
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  7. 19
      src/Markup/Perspex.Markup/BoolConverters.cs
  8. 36
      src/Markup/Perspex.Markup/FuncMultiValueConverter.cs
  9. 4
      src/Markup/Perspex.Markup/FuncValueConverter.cs
  10. 30
      src/Markup/Perspex.Markup/IMultiValueConverter.cs
  11. 3
      src/Markup/Perspex.Markup/Perspex.Markup.csproj
  12. 6
      src/Markup/Perspex.Markup/StringConverters.cs

12
samples/XamlTestApplicationPcl/TextBox.paml

@ -20,8 +20,16 @@
<TextBlock Name="floatingWatermark"
Foreground="#ff007ACC"
FontSize="10"
Text="{TemplateBinding Watermark}"
IsVisible="{TemplateBinding UseFloatingWatermark}">
Text="{TemplateBinding Watermark}">
<TextBlock.IsVisible>
<MultiBinding Converter="{Static BoolConverters.And}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
SourcePropertyPath="UseFloatingWatermark"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
SourcePropertyPath="Text"
Converter="{Static StringConverters.NotNullOrEmpty}"/>
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<Panel>
<TextBlock Name="watermark"

10
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs

@ -39,9 +39,9 @@ namespace Perspex.Markup.Xaml.Context
public override void SetValue(object instance, object value)
{
if (value is Data.Binding)
if (value is IBinding)
{
HandleXamlBindingDefinition(instance, (Data.Binding)value);
HandleBinding(instance, (IBinding)value);
}
else if (IsPerspexProperty)
{
@ -68,9 +68,9 @@ namespace Perspex.Markup.Xaml.Context
po.SetValue(pp, value);
}
private void HandleXamlBindingDefinition(object instance, Data.Binding binding)
private void HandleBinding(object instance, IBinding binding)
{
if (_xamlMember.XamlType.UnderlyingType == typeof(Data.Binding))
if (typeof(IBinding).GetTypeInfo().IsAssignableFrom(_xamlMember.XamlType.UnderlyingType.GetTypeInfo()))
{
var property = instance.GetType().GetRuntimeProperty(_xamlMember.Name);
@ -88,7 +88,7 @@ namespace Perspex.Markup.Xaml.Context
}
}
private void ApplyBinding(object instance, Data.Binding binding)
private void ApplyBinding(object instance, IBinding binding)
{
var perspexObject = instance as PerspexObject;
var attached = _xamlMember as PerspexAttachableXamlMember;

2
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@ -10,7 +10,7 @@ using Perspex.Markup.Data;
namespace Perspex.Markup.Xaml.Data
{
public class Binding
public class Binding : IBinding
{
private readonly ITypeConverterProvider _typeConverterProvider;

7
src/Markup/Perspex.Markup.Xaml/Data/IBinding.cs

@ -0,0 +1,7 @@
namespace Perspex.Markup.Xaml.Data
{
public interface IBinding
{
void Bind(IObservablePropertyBag instance, PerspexProperty property);
}
}

90
src/Markup/Perspex.Markup.Xaml/Data/MultiBinding.cs

@ -0,0 +1,90 @@
// 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;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using OmniXaml.TypeConversion;
using Perspex.Controls;
using Perspex.Markup.Data;
using Perspex.Metadata;
namespace Perspex.Markup.Xaml.Data
{
public class MultiBinding : IBinding
{
private readonly ITypeConverterProvider _typeConverterProvider;
public MultiBinding()
{
}
public MultiBinding(ITypeConverterProvider typeConverterProvider)
{
_typeConverterProvider = typeConverterProvider;
}
[Content]
public IList<Binding> Bindings { get; } = new List<Binding>();
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<object> CreateSubject(
IObservablePropertyBag instance,
PerspexProperty property)
{
if (Converter == null)
{
throw new NotSupportedException("MultiBinding without Converter not currently supported.");
}
var result = new Subject<object>();
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<object> 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;
}
}
}
}

2
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -38,6 +38,8 @@
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\IBinding.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />
<Compile Include="Data\SourceBindingEndpoint.cs" />
<Compile Include="Data\TargetBindingEndpoint.cs" />

19
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
{
/// <summary>
/// Provides a set of useful <see cref="IValueConverter"/>s for working with string values.
/// </summary>
public static class BoolConverters
{
/// <summary>
/// A multi-value converter that returns true if all inputs are true.
/// </summary>
public static readonly IMultiValueConverter And =
new FuncMultiValueConverter<bool, bool>(x => x.All(y => y));
}
}

36
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
{
/// <summary>
/// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{T1, TResult}"/>
/// to provide the converter logic.
/// </summary>
/// <typeparam name="TIn">The type of the inputs.</typeparam>
/// <typeparam name="TOut">The output type.</typeparam>
public class FuncMultiValueConverter<TIn, TOut> : IMultiValueConverter
{
private Func<IEnumerable<TIn>, TOut> _convert;
/// <summary>
/// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TOut}"/> class.
/// </summary>
/// <param name="convert">The convert function.</param>
public FuncMultiValueConverter(Func<IEnumerable<TIn>, TOut> convert)
{
_convert = convert;
}
/// <inheritdoc/>
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
return _convert(values.OfType<TIn>());
}
}
}

4
src/Markup/Perspex.Markup/FuncValueConverter.cs

@ -11,8 +11,8 @@ namespace Perspex.Markup
/// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{T1, TResult}"/>
/// to provide the converter logic.
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <typeparam name="TIn">The input type.</typeparam>
/// <typeparam name="TOut">The output type.</typeparam>
public class FuncValueConverter<TIn, TOut> : IValueConverter
{
private Func<TIn, TOut> _convert;

30
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
{
/// <summary>
/// Converts multi-binding inputs to a final value.
/// </summary>
public interface IMultiValueConverter
{
/// <summary>
/// Converts multi-binding inputs to a final value.
/// </summary>
/// <param name="values">The values to convert.</param>
/// <param name="targetType">The type of the target.</param>
/// <param name="parameter">A user-defined parameter.</param>
/// <param name="culture">The culture to use.</param>
/// <returns>The converted value.</returns>
/// <remarks>
/// This method should not throw exceptions. If the value is not convertible, return
/// <see cref="PerspexProperty.UnsetValue"/>. Any exception thrown will be treated as
/// an application exception.
/// </remarks>
object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture);
}
}

3
src/Markup/Perspex.Markup/Perspex.Markup.csproj

@ -52,7 +52,10 @@
<Compile Include="Data\PropertyAccessorNode.cs" />
<Compile Include="Data\ExpressionNode.cs" />
<Compile Include="Data\ExpressionObserver.cs" />
<Compile Include="FuncMultiValueConverter.cs" />
<Compile Include="FuncValueConverter.cs" />
<Compile Include="IMultiValueConverter.cs" />
<Compile Include="BoolConverters.cs" />
<Compile Include="StringConverters.cs" />
<Compile Include="DefaultValueConverter.cs" />
<Compile Include="IValueConverter.cs" />

6
src/Markup/Perspex.Markup/StringConverters.cs

@ -17,5 +17,11 @@ namespace Perspex.Markup
/// </summary>
public static readonly IValueConverter NullOrEmpty =
new FuncValueConverter<string, bool>(x => string.IsNullOrEmpty(x));
/// <summary>
/// A value converter that returns true if the input string is not null or empty.
/// </summary>
public static readonly IValueConverter NotNullOrEmpty =
new FuncValueConverter<string, bool>(x => !string.IsNullOrEmpty(x));
}
}

Loading…
Cancel
Save