Procházet zdrojové kódy

Implemented Binding.FallbackValue.

Closes #238.
Steven Kirk před 9 roky
rodič
revize
ffbd694bf7

+ 7 - 1
src/Markup/Perspex.Markup.Xaml/Data/Binding.cs

@@ -31,6 +31,11 @@ namespace Perspex.Markup.Xaml.Data
         /// </summary>
         public string ElementName { get; set; }
 
+        /// <summary>
+        /// Gets or sets the value to use when the binding is unable to produce a value.
+        /// </summary>
+        public object FallbackValue { get; set; }
+
         /// <summary>
         /// Gets or sets the binding mode.
         /// </summary>
@@ -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)

+ 1 - 0
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;

+ 1 - 0
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;

+ 27 - 9
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;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@@ -34,12 +35,18 @@ namespace Perspex.Markup.Data
         /// <param name="inner">The <see cref="ExpressionObserver"/>.</param>
         /// <param name="targetType">The type to convert the value to.</param>
         /// <param name="converter">The value converter to use.</param>
-        /// <param name="converterParameter">A parameter to pass to <paramref name="converter"/>.</param>
+        /// <param name="converterParameter">
+        /// A parameter to pass to <paramref name="converter"/>.
+        /// </param>
+        /// <param name="fallbackValue">
+        /// The value to use when the binding is unable to produce a value.
+        /// </param>
         public ExpressionSubject(
             ExpressionObserver inner, 
             Type targetType, 
             IValueConverter converter,
-            object converterParameter = null)
+            object converterParameter = null,
+            object fallbackValue = null)
         {
             Contract.Requires<ArgumentNullException>(inner != null);
             Contract.Requires<ArgumentNullException>(targetType != null);
@@ -49,6 +56,7 @@ namespace Perspex.Markup.Data
             _targetType = targetType;
             Converter = converter;
             ConverterParameter = converterParameter;
+            _fallbackValue = fallbackValue;
         }
 
         /// <summary>
@@ -99,13 +107,23 @@ namespace Perspex.Markup.Data
         /// <inheritdoc/>
         public IDisposable Subscribe(IObserver<object> 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;
         }
     }
 }

+ 34 - 0
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);
+        }
+
         /// <summary>
         /// Tests a problem discovered with ListBox with selection.
         /// </summary>