Browse Source

Started to implement data validation on TextBox.

Not working still - added a failing test to demonstrate it.
pull/691/head
Steven Kirk 10 years ago
parent
commit
941246e75c
  1. 11
      src/Avalonia.Base/DirectProperty.cs
  2. 26
      src/Avalonia.Controls/TextBox.cs
  3. 84
      tests/Avalonia.Controls.UnitTests/TextBoxTests_ValidationState.cs

11
src/Avalonia.Base/DirectProperty.cs

@ -85,19 +85,26 @@ namespace Avalonia
/// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/> /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
/// </param> /// </param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param> /// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="enableDataValidation">
/// Whether the property is interested in data validation.
/// </param>
/// <returns>The property.</returns> /// <returns>The property.</returns>
public DirectProperty<TNewOwner, TValue> AddOwner<TNewOwner>( public DirectProperty<TNewOwner, TValue> AddOwner<TNewOwner>(
Func<TNewOwner, TValue> getter, Func<TNewOwner, TValue> getter,
Action<TNewOwner, TValue> setter = null, Action<TNewOwner, TValue> setter = null,
TValue unsetValue = default(TValue), TValue unsetValue = default(TValue),
BindingMode defaultBindingMode = BindingMode.OneWay) BindingMode defaultBindingMode = BindingMode.OneWay,
bool enableDataValidation = false)
where TNewOwner : AvaloniaObject where TNewOwner : AvaloniaObject
{ {
var result = new DirectProperty<TNewOwner, TValue>( var result = new DirectProperty<TNewOwner, TValue>(
this, this,
getter, getter,
setter, setter,
new DirectPropertyMetadata<TValue>(unsetValue, defaultBindingMode)); new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode,
enableDataValidation: enableDataValidation));
AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result); AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result; return result;

26
src/Avalonia.Controls/TextBox.cs

@ -35,6 +35,9 @@ namespace Avalonia.Controls
o => o.CaretIndex, o => o.CaretIndex,
(o, v) => o.CaretIndex = v); (o, v) => o.CaretIndex = v);
public static readonly StyledProperty<bool> IsReadOnlyProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
public static readonly DirectProperty<TextBox, int> SelectionStartProperty = public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
AvaloniaProperty.RegisterDirect<TextBox, int>( AvaloniaProperty.RegisterDirect<TextBox, int>(
nameof(SelectionStart), nameof(SelectionStart),
@ -51,7 +54,8 @@ namespace Avalonia.Controls
TextBlock.TextProperty.AddOwner<TextBox>( TextBlock.TextProperty.AddOwner<TextBox>(
o => o.Text, o => o.Text,
(o, v) => o.Text = v, (o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay); defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
public static readonly StyledProperty<TextAlignment> TextAlignmentProperty = public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
TextBlock.TextAlignmentProperty.AddOwner<TextBox>(); TextBlock.TextAlignmentProperty.AddOwner<TextBox>();
@ -65,9 +69,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty = public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
AvaloniaProperty.Register<TextBox, bool>("UseFloatingWatermark"); AvaloniaProperty.Register<TextBox, bool>("UseFloatingWatermark");
public static readonly StyledProperty<bool> IsReadOnlyProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
struct UndoRedoState : IEquatable<UndoRedoState> struct UndoRedoState : IEquatable<UndoRedoState>
{ {
public string Text { get; } public string Text { get; }
@ -145,6 +146,12 @@ namespace Avalonia.Controls
} }
} }
public bool IsReadOnly
{
get { return GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public int SelectionStart public int SelectionStart
{ {
get get
@ -198,12 +205,6 @@ namespace Avalonia.Controls
set { SetValue(UseFloatingWatermarkProperty, value); } set { SetValue(UseFloatingWatermarkProperty, value); }
} }
public bool IsReadOnly
{
get { return GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public TextWrapping TextWrapping public TextWrapping TextWrapping
{ {
get { return GetValue(TextWrappingProperty); } get { return GetValue(TextWrappingProperty); }
@ -461,6 +462,11 @@ namespace Avalonia.Controls
} }
} }
protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification status)
{
((IPseudoClasses)Classes).Set(":error", status != null && status.ErrorType != BindingErrorType.None);
}
private int CoerceCaretIndex(int value) private int CoerceCaretIndex(int value)
{ {
var text = Text; var text = Text;

84
tests/Avalonia.Controls.UnitTests/TextBoxTests_ValidationState.cs

@ -5,8 +5,13 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Markup.Xaml.Data; using Avalonia.Markup.Xaml.Data;
using Avalonia.Platform;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq;
using Xunit; using Xunit;
namespace Avalonia.Controls.UnitTests namespace Avalonia.Controls.UnitTests
@ -14,63 +19,44 @@ namespace Avalonia.Controls.UnitTests
public class TextBoxTests_ValidationState public class TextBoxTests_ValidationState
{ {
[Fact] [Fact]
public void Setter_Exceptions_Should_Set_ValidationState() public void Setter_Exceptions_Should_Set_Error_Pseudoclass()
{ {
using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) using (UnitTestApplication.Start(Services))
{ {
var target = new TextBox(); var target = new TextBox
var binding = new Binding(nameof(ExceptionTest.LessThan10)); {
binding.Source = new ExceptionTest(); DataContext = new ExceptionTest(),
////binding.EnableValidation = true; [!TextBox.TextProperty] = new Binding(nameof(ExceptionTest.LessThan10), BindingMode.TwoWay),
target.Bind(TextBox.TextProperty, binding); Template = CreateTemplate(),
};
Assert.True(false); target.ApplyTemplate();
//Assert.True(target.ValidationStatus.IsValid);
//target.Text = "20";
//Assert.False(target.ValidationStatus.IsValid);
//target.Text = "1";
//Assert.True(target.ValidationStatus.IsValid);
}
}
[Fact(Skip = "TODO: Not yet passing")] Assert.False(target.Classes.Contains(":error"));
public void Unconvertable_Value_Should_Set_ValidationState() target.Text = "20";
{ Assert.True(target.Classes.Contains(":error"));
using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) target.Text = "1";
{ Assert.False(target.Classes.Contains(":error"));
var target = new TextBox();
var binding = new Binding(nameof(ExceptionTest.LessThan10));
binding.Source = new ExceptionTest();
////binding.EnableValidation = true;
target.Bind(TextBox.TextProperty, binding);
Assert.True(false);
//Assert.True(target.ValidationStatus.IsValid);
//target.Text = "foo";
//Assert.False(target.ValidationStatus.IsValid);
//target.Text = "1";
//Assert.True(target.ValidationStatus.IsValid);
} }
} }
[Fact] private static TestServices Services => TestServices.MockThreadingInterface.With(
public void Indei_Should_Set_ValidationState() standardCursorFactory: Mock.Of<IStandardCursorFactory>());
{
using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
{
var target = new TextBox();
var binding = new Binding(nameof(ExceptionTest.LessThan10));
binding.Source = new IndeiTest();
////binding.EnableValidation = true;
target.Bind(TextBox.TextProperty, binding);
Assert.True(false); private IControlTemplate CreateTemplate()
//Assert.True(target.ValidationStatus.IsValid); {
//target.Text = "20"; return new FuncControlTemplate<TextBox>(control =>
//Assert.False(target.ValidationStatus.IsValid); new TextPresenter
//target.Text = "1"; {
//Assert.True(target.ValidationStatus.IsValid); Name = "PART_TextPresenter",
} [!!TextPresenter.TextProperty] = new Binding
{
Path = "Text",
Mode = BindingMode.TwoWay,
Priority = BindingPriority.TemplatedParent,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
},
});
} }
private class ExceptionTest private class ExceptionTest

Loading…
Cancel
Save