Browse Source

Merge branch 'master' into fixComboBoxDataValidationSelected

Max Katz 4 years ago
parent
commit
5665417a71

+ 5 - 3
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -483,7 +483,9 @@ namespace Avalonia.Controls
             AvaloniaProperty.RegisterDirect<AutoCompleteBox, object>(
                 nameof(SelectedItem),
                 o => o.SelectedItem,
-                (o, v) => o.SelectedItem = v);
+                (o, v) => o.SelectedItem = v,
+                defaultBindingMode: BindingMode.TwoWay,
+                enableDataValidation: true);
 
         /// <summary>
         /// Identifies the
@@ -1333,7 +1335,7 @@ namespace Avalonia.Controls
 
             base.OnApplyTemplate(e);
         }
-        
+
         /// <summary>
         /// Called to update the validation state for properties for which data validation is
         /// enabled.
@@ -1342,7 +1344,7 @@ namespace Avalonia.Controls
         /// <param name="value">The new binding value for the property.</param>
         protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
         {
-            if (property == TextProperty)
+            if (property == TextProperty || property == SelectedItemProperty)
             {
                 DataValidationErrors.SetError(this, value.Error);
             }

+ 16 - 2
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@@ -91,14 +91,14 @@ namespace Avalonia.Controls
         /// </summary>
         public static readonly DirectProperty<NumericUpDown, string> TextProperty =
             AvaloniaProperty.RegisterDirect<NumericUpDown, string>(nameof(Text), o => o.Text, (o, v) => o.Text = v,
-                defaultBindingMode: BindingMode.TwoWay);
+                defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
 
         /// <summary>
         /// Defines the <see cref="Value"/> property.
         /// </summary>
         public static readonly DirectProperty<NumericUpDown, double> ValueProperty =
             AvaloniaProperty.RegisterDirect<NumericUpDown, double>(nameof(Value), updown => updown.Value,
-                (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay);
+                (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
 
         /// <summary>
         /// Defines the <see cref="Watermark"/> property.
@@ -370,6 +370,20 @@ namespace Avalonia.Controls
             }
         }
 
+        /// <summary>
+        /// Called to update the validation state for properties for which data validation is
+        /// enabled.
+        /// </summary>
+        /// <param name="property">The property.</param>
+        /// <param name="value">The new binding value for the property.</param>
+        protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
+        {
+            if (property == TextProperty || property == ValueProperty)
+            {
+                DataValidationErrors.SetError(this, value.Error);
+            }
+        }
+
         /// <summary>
         /// Called when the <see cref="CultureInfo"/> property value changed.
         /// </summary>

+ 32 - 0
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@@ -14,6 +14,8 @@ using Avalonia.UnitTests;
 using Moq;
 using Xunit;
 using System.Collections.ObjectModel;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
 
 namespace Avalonia.Controls.UnitTests
 {
@@ -396,6 +398,36 @@ namespace Avalonia.Controls.UnitTests
                 Assert.Equal(control.Text, control.ItemSelector(input, selectedItem));
             });
         }
+        
+        [Fact]
+        public void Text_Validation()
+        {
+            RunTest((control, textbox) =>
+            {
+                var exception = new InvalidCastException("failed validation");
+                var textObservable = new BehaviorSubject<BindingNotification>(new BindingNotification(exception, BindingErrorType.DataValidationError));
+                control.Bind(AutoCompleteBox.TextProperty, textObservable);
+                Dispatcher.UIThread.RunJobs();
+
+                Assert.Equal(DataValidationErrors.GetHasErrors(control), true);
+                Assert.Equal(DataValidationErrors.GetErrors(control).SequenceEqual(new[] { exception }), true);
+            });
+        }
+        
+        [Fact]
+        public void SelectedItem_Validation()
+        {
+            RunTest((control, textbox) =>
+            {
+                var exception = new InvalidCastException("failed validation");
+                var itemObservable = new BehaviorSubject<BindingNotification>(new BindingNotification(exception, BindingErrorType.DataValidationError));
+                control.Bind(AutoCompleteBox.SelectedItemProperty, itemObservable);
+                Dispatcher.UIThread.RunJobs();
+
+                Assert.Equal(DataValidationErrors.GetHasErrors(control), true);
+                Assert.Equal(DataValidationErrors.GetErrors(control).SequenceEqual(new[] { exception }), true);
+            });
+        }
 
         /// <summary>
         /// Retrieves a defined predicate filter through a new AutoCompleteBox 

+ 95 - 0
tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs

@@ -0,0 +1,95 @@
+using System;
+using System.Linq;
+using System.Reactive.Subjects;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Threading;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests
+{
+    public class NumericUpDownTests
+    {
+        private static TestServices Services => TestServices.StyledWindow;
+
+        [Fact]
+        public void Text_Validation()
+        {
+            RunTest((control, textbox) =>
+            {
+                var exception = new InvalidCastException("failed validation");
+                var textObservable = new BehaviorSubject<BindingNotification>(new BindingNotification(exception, BindingErrorType.DataValidationError));
+                control.Bind(NumericUpDown.TextProperty, textObservable);
+                Dispatcher.UIThread.RunJobs();
+
+                Assert.True(DataValidationErrors.GetHasErrors(control));
+                Assert.True(DataValidationErrors.GetErrors(control).SequenceEqual(new[] { exception }));
+            });
+        }
+
+        [Fact]
+        public void Value_Validation()
+        {
+            RunTest((control, textbox) =>
+            {
+                var exception = new InvalidCastException("failed validation");
+                var valueObservable = new BehaviorSubject<BindingNotification>(new BindingNotification(exception, BindingErrorType.DataValidationError));
+                control.Bind(NumericUpDown.ValueProperty, valueObservable);
+                Dispatcher.UIThread.RunJobs();
+
+                Assert.True(DataValidationErrors.GetHasErrors(control));
+                Assert.True(DataValidationErrors.GetErrors(control).SequenceEqual(new[] { exception }));
+            });
+        }
+
+        private void RunTest(Action<NumericUpDown, TextBox> test)
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var control = CreateControl();
+                TextBox textBox = GetTextBox(control);
+                var window = new Window { Content = control };
+                window.ApplyTemplate();
+                window.Presenter.ApplyTemplate();
+                Dispatcher.UIThread.RunJobs();
+                test.Invoke(control, textBox);
+            }
+        }
+
+        private NumericUpDown CreateControl()
+        {
+            var control = new NumericUpDown
+            {
+                Template = CreateTemplate()
+            };
+
+            control.ApplyTemplate();
+            return control;
+        }
+        private TextBox GetTextBox(NumericUpDown control)
+        {
+            return control.GetTemplateChildren()
+                          .OfType<ButtonSpinner>()
+                          .Select(b => b.Content)
+                          .OfType<TextBox>()
+                          .First();
+        }
+        private IControlTemplate CreateTemplate()
+        {
+            return new FuncControlTemplate<NumericUpDown>((control, scope) =>
+            {
+                var textBox =
+                    new TextBox
+                    {
+                        Name = "PART_TextBox"
+                    }.RegisterInNameScope(scope);
+                return new ButtonSpinner
+                    {
+                        Name = "PART_Spinner",
+                        Content = textBox,
+                    }.RegisterInNameScope(scope);
+            });
+        }
+    }
+}