Тройное дублирование текста в TextBox
Хочу сделать небольшой редактор текста: один textbox, две кнопки (для bold и italic) и ничего больше. Важное уточнение: никакого отображения стилизованного текста, только его сырое представление, надеюсь, понятно. Задача тривиальная, но применение стилей ведет себя странно, делая текст больше, чем он был изначально. Пример:
- Исходный текст: hello
- Нажатие кнопки Bold
- Ожидание:
**hello** - Реальность:
hello**hello**hello
Код MyWindows.axaml.cs:
private void BtnItalicOnClick(object? sender, RoutedEventArgs e)
{
if (_isProcessing) return;
_isProcessing = true;
try
{
// Устанавливаем фокус на TextBox
tbPost.Focus();
// Проверяем, есть ли выделенный текст
if (string.IsNullOrWhiteSpace(tbPost.SelectedText))
{
return;
}
int selectionStart = tbPost.SelectionStart;
int selectionEnd = tbPost.SelectionEnd;
string selectedText = tbPost.SelectedText;
// Запрещаем форматирование, если выделение содержит переносы строк
if (selectedText.Contains("\n") || selectedText.Contains("\r"))
{
return;
}
// Проверяем, не отформатирован ли уже текст
string beforeText = selectionStart >= 2 ? tbPost.Text[(selectionStart - 2)..selectionStart] : "";
string afterText = selectionEnd < tbPost.Text.Length - 2 ? tbPost.Text[selectionEnd..(selectionEnd + 2)] : "";
if (beforeText == "__" && afterText == "__")
{
return;
}
// Формируем новый текст
string newText = $"{tbPost.Text[..selectionStart]}__{selectedText}__{tbPost.Text[selectionEnd..]}";
// Обновляем текст через ViewModel
_vm.PostText = newText;
// Восстанавливаем выделение
tbPost.SelectionStart = selectionStart + 2;
tbPost.SelectionEnd = selectionEnd + 2;
}
finally
{
_isProcessing = false;
}
}
private void BtnBoldOnClick(object? sender, RoutedEventArgs e)
{
if (_isProcessing) return;
_isProcessing = true;
try
{
// Устанавливаем фокус на TextBox
tbPost.Focus();
// Проверяем, есть ли выделенный текст
if (string.IsNullOrWhiteSpace(tbPost.SelectedText))
{
return;
}
int selectionStart = tbPost.SelectionStart;
int selectionEnd = tbPost.SelectionEnd;
string selectedText = tbPost.SelectedText;
// Запрещаем форматирование, если выделение содержит переносы строк
if (selectedText.Contains("\n") || selectedText.Contains("\r"))
{
return;
}
// Проверяем, не отформатирован ли уже текст
string beforeText = selectionStart >= 2 ? tbPost.Text[(selectionStart - 2)..selectionStart] : "";
string afterText = selectionEnd < tbPost.Text.Length - 2 ? tbPost.Text[selectionEnd..(selectionEnd + 2)] : "";
if (beforeText == "**" && afterText == "**")
{
return;
}
// Формируем новый текст
string newText = $"{tbPost.Text[..selectionStart]}**{selectedText}**{tbPost.Text[selectionEnd..]}";
// Обновляем текст через ViewModel
_vm.PostText = newText;
// Восстанавливаем выделение
tbPost.SelectionStart = selectionStart + 2;
tbPost.SelectionEnd = selectionEnd + 2;
}
finally
{
_isProcessing = false;
}
}
Код TextBox в MyWindows.axaml:
<TextBox Grid.Row="1"
Name="tbPost"
Text="{Binding PostText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
AcceptsReturn="True"
ClearSelectionOnLostFocus="False"/>
Код свойства из MyWindowsViewModel.cs:
private string _postText;
public string PostText
{
get => _postText;
set => this.RaiseAndSetIfChanged(ref this._postText, value);
}
P.S. Изначально обработчики кнопок были проще, но в попытках исправить проблему добавил много лишних проверок и т.д.
UPD. После комментариев дополняю вопрос
Avalonia 11.2.1, .NET 9
Первоначальная версия методов (писал сам) для простоты понимания:
private void BtnItalicOnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(tbPost.SelectedText)) return;
int from = tbPost.SelectionStart;
int to = tbPost.SelectionEnd;
_vm.PostText = $"{tbPost.Text[..from]}**{tbPost.SelectedText}**{tbPost.Text[to..]}";
}
private void BtnBoldOnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(tbPost.SelectedText)) return;
int from = tbPost.SelectionStart;
int to = tbPost.SelectionEnd;
_vm.PostText = $"{tbPost.Text[..from]}__{tbPost.SelectedText}__{tbPost.Text[to..]}";
}
Остальной код MyWindow.cs
// единственный конструктор
public MyWindow()
{
InitializeComponent();
Loaded += OnLoaded;
btnBold.Click += BtnBoldOnClick;
btnItalic.Click += BtnItalicOnClick;
}
// получаю view model и подписываюсь на вставку в textbox
private void OnLoaded(object? sender, RoutedEventArgs e)
{
_vm = (MyWindowViewMode)DataContext!;
tbPost.PastingFromClipboard += TbPostOnPastingFromClipboard;
}
// отслеживаю вставку Ctrl + V и ищу в буфере изображение
private async void TbPostOnPastingFromClipboard(object? sender, RoutedEventArgs e)
{
var clipboard = this.Clipboard;
var dataObject =
await clipboard.GetDataAsync("image/png")
?? await clipboard.GetDataAsync("image/jpeg")
?? await clipboard.GetDataAsync("PNG")
?? await clipboard.GetDataAsync("Bitmap");
if (dataObject is null)
return;
_vm.PastImage(dataObject);
e.Handled = true;
}