From ffbd694bf71a4b2fb9f2d20e37725a63ce9b4144 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 3 Feb 2016 12:18:17 +0100 Subject: [PATCH] Implemented Binding.FallbackValue. Closes #238. --- .../Perspex.Markup.Xaml/Data/Binding.cs | 8 ++++- .../MarkupExtensions/BindingExtension.cs | 1 + .../TemplateBindingExtension.cs | 1 + .../Perspex.Markup/Data/ExpressionSubject.cs | 36 ++++++++++++++----- .../Data/BindingTests.cs | 34 ++++++++++++++++++ 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs index c01a8c29f2..8efdc04b8d 100644 --- a/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Perspex.Markup.Xaml/Data/Binding.cs @@ -31,6 +31,11 @@ namespace Perspex.Markup.Xaml.Data /// public string ElementName { get; set; } + /// + /// Gets or sets the value to use when the binding is unable to produce a value. + /// + public object FallbackValue { get; set; } + /// /// Gets or sets the binding mode. /// @@ -104,7 +109,8 @@ namespace Perspex.Markup.Xaml.Data observer, targetProperty?.PropertyType ?? typeof(object), Converter ?? DefaultValueConverter.Instance, - ConverterParameter); + ConverterParameter, + FallbackValue); } private static PathInfo ParsePath(string path) diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs index 20efff1e81..c32a08cb5d 100644 --- a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs +++ b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/BindingExtension.cs @@ -34,6 +34,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions public IValueConverter Converter { get; set; } public object ConverterParameter { get; set; } public string ElementName { get; set; } + public object FallbackValue { get; set; } public BindingMode Mode { get; set; } public string Path { get; set; } public BindingPriority Priority { get; set; } = BindingPriority.LocalValue; diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs index cec5146c71..a4b808f9c0 100644 --- a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs +++ b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs @@ -33,6 +33,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions public IValueConverter Converter { get; set; } public string ElementName { get; set; } + public object FallbackValue { get; set; } public BindingMode Mode { get; set; } public string Path { get; set; } public BindingPriority Priority { get; set; } = BindingPriority.TemplatedParent; diff --git a/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs b/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs index c69865bacc..6b12c6dede 100644 --- a/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs +++ b/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs @@ -17,6 +17,7 @@ namespace Perspex.Markup.Data { private readonly ExpressionObserver _inner; private readonly Type _targetType; + private readonly object _fallbackValue; /// /// Initializes a new instance of the class. @@ -34,12 +35,18 @@ namespace Perspex.Markup.Data /// The . /// The type to convert the value to. /// The value converter to use. - /// A parameter to pass to . + /// + /// A parameter to pass to . + /// + /// + /// The value to use when the binding is unable to produce a value. + /// public ExpressionSubject( ExpressionObserver inner, Type targetType, IValueConverter converter, - object converterParameter = null) + object converterParameter = null, + object fallbackValue = null) { Contract.Requires(inner != null); Contract.Requires(targetType != null); @@ -49,6 +56,7 @@ namespace Perspex.Markup.Data _targetType = targetType; Converter = converter; ConverterParameter = converterParameter; + _fallbackValue = fallbackValue; } /// @@ -99,13 +107,23 @@ namespace Perspex.Markup.Data /// public IDisposable Subscribe(IObserver observer) { - return _inner - .Select(x => Converter.Convert( - x, - _targetType, - ConverterParameter, - CultureInfo.CurrentUICulture)) - .Subscribe(observer); + return _inner.Select(ConvertValue).Subscribe(observer); + } + + private object ConvertValue(object value) + { + var converted = Converter.Convert( + value, + _targetType, + ConverterParameter, + CultureInfo.CurrentUICulture); + + if (converted == PerspexProperty.UnsetValue && _fallbackValue != null) + { + converted = _fallbackValue; + } + + return converted; } } } diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs index aaac0be2d0..e70023fa35 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -206,6 +206,40 @@ namespace Perspex.Markup.Xaml.UnitTests.Data Assert.Same("foo", ((ExpressionSubject)result).ConverterParameter); } + [Fact] + public void Should_Return_FallbackValue_When_Path_Not_Resolved() + { + var target = new TextBlock(); + var source = new Source(); + var binding = new Binding + { + Source = source, + Path = "BadPath", + FallbackValue = "foofallback", + }; + + target.Bind(TextBlock.TextProperty, binding); + + Assert.Equal("foofallback", target.Text); + } + + [Fact] + public void Should_Return_FallbackValue_When_Invalid_Source_Type() + { + var target = new ProgressBar(); + var source = new Source { Foo = "foo" }; + var binding = new Binding + { + Source = source, + Path = "Foo", + FallbackValue = 42, + }; + + target.Bind(ProgressBar.ValueProperty, binding); + + Assert.Equal(42, target.Value); + } + /// /// Tests a problem discovered with ListBox with selection. ///