Browse Source

Send a single Text change nofication when TextBlock.SelectedText is changed. (#5508)

* Added failing tests for #838.

* Fix Text events when setting SelectedText.

Only raise a single property changed event on `Text` when setting `SelectedText`. It's a bit ugly but the whole of `TextBox` needs a refactor...

Fixes #838.
pull/7094/head
Steven Kirk 5 years ago
committed by GitHub
parent
commit
931a79dfb7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 55
      src/Avalonia.Controls/TextBox.cs
  2. 48
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

55
src/Avalonia.Controls/TextBox.cs

@ -642,15 +642,31 @@ namespace Avalonia.Controls
if (!string.IsNullOrEmpty(input))
{
DeleteSelection();
caretIndex = CaretIndex;
text = Text ?? string.Empty;
SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex));
CaretIndex += input.Length;
ClearSelection();
if (IsUndoEnabled)
var oldText = _text;
_ignoreTextChanges = true;
try
{
_undoRedoHelper.DiscardRedo();
DeleteSelection(false);
caretIndex = CaretIndex;
text = Text ?? string.Empty;
SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex));
CaretIndex += input.Length;
ClearSelection();
if (IsUndoEnabled)
{
_undoRedoHelper.DiscardRedo();
}
if (_text != oldText)
{
RaisePropertyChanged(TextProperty, oldText, _text);
}
}
finally
{
_ignoreTextChanges = false;
}
}
}
@ -1285,7 +1301,7 @@ namespace Avalonia.Controls
CaretIndex = SelectionEnd;
}
private bool DeleteSelection()
private bool DeleteSelection(bool raiseTextChanged = true)
{
if (!IsReadOnly)
{
@ -1297,7 +1313,7 @@ namespace Avalonia.Controls
var start = Math.Min(selectionStart, selectionEnd);
var end = Math.Max(selectionStart, selectionEnd);
var text = Text;
SetTextInternal(text.Substring(0, start) + text.Substring(end));
SetTextInternal(text.Substring(0, start) + text.Substring(end), raiseTextChanged);
CaretIndex = start;
ClearSelection();
return true;
@ -1348,16 +1364,23 @@ namespace Avalonia.Controls
return i;
}
private void SetTextInternal(string value)
private void SetTextInternal(string value, bool raiseTextChanged = true)
{
try
if (raiseTextChanged)
{
_ignoreTextChanges = true;
SetAndRaise(TextProperty, ref _text, value);
try
{
_ignoreTextChanges = true;
SetAndRaise(TextProperty, ref _text, value);
}
finally
{
_ignoreTextChanges = false;
}
}
finally
else
{
_ignoreTextChanges = false;
_text = value;
}
}

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

@ -784,6 +784,54 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Setting_SelectedText_Should_Fire_Single_Text_Changed_Notification()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "0123",
AcceptsReturn = true,
AcceptsTab = true,
SelectionStart = 1,
SelectionEnd = 3,
};
var values = new List<string>();
target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
target.SelectedText = "A";
Assert.Equal(new[] { "0123", "0A3" }, values);
}
}
[Fact]
public void Entering_Text_With_SelectedText_Should_Fire_Single_Text_Changed_Notification()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "0123",
AcceptsReturn = true,
AcceptsTab = true,
SelectionStart = 1,
SelectionEnd = 3,
};
var values = new List<string>();
target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
RaiseTextEvent(target, "A");
Assert.Equal(new[] { "0123", "0A3" }, values);
}
}
private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice(),

Loading…
Cancel
Save