Я хочу отследить, какой символ удален пользователем через, Удаляют или клавиша Backspace.
Я обрабатываю TextBox_ChangedEvent текстового поля.
Я могу извлечь удаленный символ из TextChangedEventArgs e. Изменения и если да, Как я могу сделать это?
Я хочу ограничить пользователя от удаления любых символов от TextBox. Я хочу пользователя, может удалить только два символа (скажем," (" или")")
Предложите.
Ниже вы найдете код для прикрепленного свойства, которое можно использовать таким образом для предотвращения чего-либо, кроме "(" или ") "от удаления из текстового поля, точка.
<TextBox my:TextBoxRestriction.RestrictDeleteTo="()" ... />
Это будет правильно обрабатывать все обновления мыши и клавиатуры, такие как:
Благодаря этому она намного мощнее простого перехвата PreviewKeyDown.
Это также запрещает удаление чего-либо byt "(" или ")" путем непосредственного присвоения свойству .Text, поэтому это не удастся:
textBox.Text = "Good morning";
Из-за этого класс TextBoxRestriction также содержит еще одно присоединенное свойство, называемое UnrestrictedText , который, если установлен, может обновлять свойство Text в обход ограничений. Это может быть установлено в коде с помощью TextBoxRestriction.SetUnrestrictedText
или с привязкой к данным следующим образом:
<TextBox my:TextBoxRestriction.RestrictDeleteTo="()"
my:TextBoxRestriction.UnrestrictedText="{Binding PropertyNameHere}" />
В приведенной ниже реализации UnrestrictedText работает только тогда, когда также установлен RestrictDeleteTo. Можно сделать полную реализацию, которая регистрирует обработчик событий всякий раз, когда установлено какое-либо свойство, и сохраняет обработчик в третьем присоединенном свойстве для последующей отмены регистрации. Но для ваших текущих нужд в этом, вероятно, нет необходимости.
Вот реализация, как и было обещано:
public class TextBoxRestriction : DependencyObject
{
// RestrictDeleteTo: Set this to the characters that may be deleted
public static string GetRestrictDeleteTo(DependencyObject obj) { return (string)obj.GetValue(RestrictDeleteToProperty); }
public static void SetRestrictDeleteTo(DependencyObject obj, string value) { obj.SetValue(RestrictDeleteToProperty, value); }
public static readonly DependencyProperty RestrictDeleteToProperty = DependencyProperty.RegisterAttached("RestrictDeleteTo", typeof(string), typeof(TextBoxRestriction), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var box = (TextBox)obj;
box.TextChanged += (obj2, changeEvent) =>
{
var oldText = GetUnrestrictedText(box);
var allowedChars = GetRestrictDeleteTo(box);
if(box.Text==oldText || allowdChars==null) return;
foreach(var change in changeEvent.Changes)
if(change.RemovedLength>0)
{
string deleted = box.Text.Substring(change.Offset, change.RemovedLength);
if(deleted.Any(ch => !allowedChars.Contains(ch)))
box.Text = oldText;
}
SetUnrestrictedText(box, box.Text);
};
}
});
// UnrestrictedText: Bind or access this property to update the Text property bypassing all restrictions
public static string GetUnrestrictedText(DependencyObject obj) { return (string)obj.GetValue(UnrestrictedTextProperty); }
public static void SetUnrestrictedText(DependencyObject obj, string value) { obj.SetValue(UnrestrictedTextProperty, value); }
public static readonly DependencyProperty UnrestrictedTextProperty = DependencyProperty.RegisterAttached("UnrestrictedText", typeof(string), typeof(TextBoxRestriction), new PropertyMetadata
{
DefaultValue = "",
PropertyChangedCallback = (obj, e) =>
{
var box = (TextBox)obj;
box.Text = (string)e.NewValue;
}
});
}
Как это работает: когда вы устанавливаете UnrestrictedText, он устанавливает текст и наоборот. Обработчик TextChanged проверяет, отличается ли Text от UnrestrictedText.Если это так, он знает, что Text был обновлен каким-то другим механизмом, кроме установки UnrestrictedText, поэтому он сканирует изменения на предмет незаконного удаления. Если он обнаружен, он устанавливает для Text значение, которое все еще хранится в UnrestrictedText, предотвращая изменение.
Я не знаю WPF, но предполагаю, что для этого он такой же, как WinForms (кажется вероятным). Единственный способ, о котором я знаю, - это то, что вы фактически сохраняете текущий текст в переменной и при изменении текста, если это не удаление или возврат, вы обновляете этот текст, в противном случае вы используете его, сравнивайте, что изменилось, и следует ли разрешить это изменение.
Изменить: глядя на TextChangedEventArgs.Changes
, кажется, что способ, который я описал выше, все еще может быть подходящим, но вы могли бы использовать Changes
для сравнения текстов более эффективно.
Вы, возможно, уже думали об этом, но в противном случае не забудьте также обрабатывать вырезание и вставку (и что пользователь может делать это с помощью мыши, а не клавиатуры).
Прикрепленное поведение для его обработки
public static class TextInputBehaviour
{
public static bool GetIsDeleteRestricted(DependencyObject obj)
{
return (bool)obj.GetValue(IsDeleteRestrictedProperty);
}
public static void SetIsDeleteRestricted(DependencyObject obj, bool value)
{
obj.SetValue(IsDeleteRestrictedProperty, value);
}
public static readonly DependencyProperty IsDeleteRestrictedProperty=DependencyProperty.RegisterAttached("IsDeleteRestricted", typeof(bool), typeof(TextInputBehaviour), new UIPropertyMetadata(false, OnIsDeleteRestrictedChanged));
}
private static void OnIsDeleteRestrictedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
bool isDeleteRestricted = (bool)(e.NewValue);
if (isDeleteRestricted)
textBox.PreviewKeyDown += RestrictDeleteKey;
else
textBox.PreviewKeyDown -= RestrictDeleteKey;
}
private static void RestrictDeleteKey(object sender, KeyEventArgs e)
{
e.Handled = (e.Key == Key.Delete);
}
Отбросьте поведение в разделе ресурсов
Затем в блоке разметки текстового поля установите поведение
<TextBox local:TextInputBehaviour.IsDeleteRestricted="True" />