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"/>
/// </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>
public DirectProperty<TNewOwner, TValue> AddOwner<TNewOwner>(
Func<TNewOwner, TValue> getter,
Action<TNewOwner, TValue> setter = null,
TValue unsetValue = default(TValue),
BindingMode defaultBindingMode = BindingMode.OneWay)
BindingMode defaultBindingMode = BindingMode.OneWay,
bool enableDataValidation = false)
where TNewOwner : AvaloniaObject
{
var result = new DirectProperty<TNewOwner, TValue>(
this,
getter,
setter,
new DirectPropertyMetadata<TValue>(unsetValue, defaultBindingMode));
new DirectPropertyMetadata<TValue>(
unsetValue: unsetValue,
defaultBindingMode: defaultBindingMode,
enableDataValidation: enableDataValidation));
AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
return result;

26
src/Avalonia.Controls/TextBox.cs

@ -35,6 +35,9 @@ namespace Avalonia.Controls
o => o.CaretIndex,
(o, v) => o.CaretIndex = v);
public static readonly StyledProperty<bool> IsReadOnlyProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
AvaloniaProperty.RegisterDirect<TextBox, int>(
nameof(SelectionStart),
@ -51,7 +54,8 @@ namespace Avalonia.Controls
TextBlock.TextProperty.AddOwner<TextBox>(
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay);
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
TextBlock.TextAlignmentProperty.AddOwner<TextBox>();
@ -65,9 +69,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
AvaloniaProperty.Register<TextBox, bool>("UseFloatingWatermark");
public static readonly StyledProperty<bool> IsReadOnlyProperty =
AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
struct UndoRedoState : IEquatable<UndoRedoState>
{
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
{
get
@ -198,12 +205,6 @@ namespace Avalonia.Controls
set { SetValue(UseFloatingWatermarkProperty, value); }
}
public bool IsReadOnly
{
get { return GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public TextWrapping TextWrapping
{
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)
{
var text = Text;

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

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

Loading…
Cancel
Save