Browse Source

Don't modify text due to binding when typing.

If a TextBox is e.g. bound to an int and the user types "02" then the
TextBox text should be "02" - not the value that comes back from the
binding which will be "2".
pull/691/head
Steven Kirk 10 years ago
parent
commit
a222fa9b0c
  1. 30
      src/Avalonia.Controls/TextBox.cs
  2. 65
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

30
src/Avalonia.Controls/TextBox.cs

@ -90,6 +90,7 @@ namespace Avalonia.Controls
private bool _canScrollHorizontally;
private TextPresenter _presenter;
private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
private bool _ignoreTextChanges;
static TextBox()
{
@ -184,7 +185,13 @@ namespace Avalonia.Controls
public string Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
set
{
if (!_ignoreTextChanges)
{
SetAndRaise(TextProperty, ref _text, value);
}
}
}
public TextAlignment TextAlignment
@ -247,7 +254,7 @@ namespace Avalonia.Controls
DeleteSelection();
caretIndex = CaretIndex;
text = Text ?? string.Empty;
Text = text.Substring(0, caretIndex) + input + text.Substring(caretIndex);
SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex));
CaretIndex += input.Length;
SelectionStart = SelectionEnd = CaretIndex;
_undoRedoHelper.DiscardRedo();
@ -355,7 +362,7 @@ namespace Avalonia.Controls
case Key.Back:
if (!DeleteSelection() && CaretIndex > 0)
{
Text = text.Substring(0, caretIndex - 1) + text.Substring(caretIndex);
SetTextInternal(text.Substring(0, caretIndex - 1) + text.Substring(caretIndex));
--CaretIndex;
}
@ -364,7 +371,7 @@ namespace Avalonia.Controls
case Key.Delete:
if (!DeleteSelection() && caretIndex < text.Length)
{
Text = text.Substring(0, caretIndex) + text.Substring(caretIndex + 1);
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + 1));
}
break;
@ -598,7 +605,7 @@ namespace Avalonia.Controls
var start = Math.Min(selectionStart, selectionEnd);
var end = Math.Max(selectionStart, selectionEnd);
var text = Text;
Text = text.Substring(0, start) + text.Substring(end);
SetTextInternal(text.Substring(0, start) + text.Substring(end));
SelectionStart = SelectionEnd = CaretIndex = start;
return true;
}
@ -645,6 +652,19 @@ namespace Avalonia.Controls
return i;
}
private void SetTextInternal(string value)
{
try
{
_ignoreTextChanges = true;
SetAndRaise(TextProperty, ref _text, value);
}
finally
{
_ignoreTextChanges = false;
}
}
UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState
{
get { return new UndoRedoState(Text, CaretIndex); }

65
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -1,7 +1,14 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -15,5 +22,63 @@ namespace Avalonia.Controls.UnitTests
BindingMode.TwoWay,
TextBox.TextProperty.GetMetadata(typeof(TextBox)).DefaultBindingMode);
}
[Fact]
public void Typing_Beginning_With_0_Should_Not_Modify_Text_When_Bound_To_Int()
{
using (UnitTestApplication.Start(Services))
{
var source = new Class1();
var target = new TextBox
{
DataContext = source,
Template = CreateTemplate(),
};
target.ApplyTemplate();
target.Bind(TextBox.TextProperty, new Binding(nameof(Class1.Foo), BindingMode.TwoWay));
Assert.Equal("0", target.Text);
target.CaretIndex = 1;
target.RaiseEvent(new TextInputEventArgs
{
RoutedEvent = InputElement.TextInputEvent,
Text = "2",
});
Assert.Equal("02", target.Text);
}
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());
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 Class1 : NotifyingBase
{
private int _foo;
public int Foo
{
get { return _foo; }
set { _foo = value; RaisePropertyChanged(); }
}
}
}
}

Loading…
Cancel
Save