Ниже вы найдете код для присоединенного свойства, которое можно использовать таким образом, чтобы предотвратить удаление чего-либо, кроме "(" или ")" из TextBox, точка.
<TextBox my:TextBoxRestriction.RestrictDeleteTo="()" ... />
Это будет правильно обрабатывать все обновления мыши и клавиатуры, такие как:
- Использование клавиши удаления с несколькими выбранными символами
- Использование клавиши Backspace
- Использование Ctrl-X для вырезания
- Нажатие кнопки «Вырезать» в строке меню
Из-за этого он намного мощнее, чем просто перехват PreviewKeyDown.
Это также отключает удаление чего-либо с помощью байта "(" или ")" путем непосредственного присвоения свойству .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. Если это так, он знает, что текст был обновлен с помощью какого-то другого механизма, кроме установки UnrestrictedText, поэтому сканирует изменения на предмет незаконного удаления. Если один из них найден, он возвращает Text значение, все еще сохраненное в UnrestrictedText, предотвращая изменение.