Пользовательский ввод в TextBox
может быть идентифицирован как
- Ввод: Событие PreviewTextInput
- Backspace, Delete, Enter: Событие PreviewKeyDown
- Вставка: DataObject.PastingEvent
Объединение этих трех событий с флагом bool для указания того, произошло ли что-либо из вышеперечисленного до события TextChanged , и вы будете знать причину обновления.
Печатать и вставлять легко, но Backspace не всегда запускает TextChanged
(если текст не выделен и курсор находится в позиции 0, например). Таким образом, некоторая логика необходима в PreviewTextInput .
Вот присоединенное поведение, которое реализует приведенную выше логику и выполняет команду с флагом bool, когда TextChanged
поднято.
<TextBox ex:TextChangedBehavior.TextChangedCommand="{Binding TextChangedCommand}" />
И в коде вы можете узнать источник обновления, например
private void TextChanged_Executed(object parameter)
{
object[] parameters = parameter as object[];
object sender = parameters[0];
TextChangedEventArgs e = (TextChangedEventArgs)parameters[1];
bool userInput = (bool)parameters[2];
if (userInput == true)
{
// User input update..
}
else
{
// Binding, Programatic update..
}
}
Вот небольшой пример проекта, демонстрирующий эффект: SourceOfTextChanged.zip
TextChangedBehavior
public class TextChangedBehavior
{
public static DependencyProperty TextChangedCommandProperty =
DependencyProperty.RegisterAttached("TextChangedCommand",
typeof(ICommand),
typeof(TextChangedBehavior),
new UIPropertyMetadata(TextChangedCommandChanged));
public static void SetTextChangedCommand(DependencyObject target, ICommand value)
{
target.SetValue(TextChangedCommandProperty, value);
}
// Subscribe to the events if we have a valid command
private static void TextChangedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = target as TextBox;
if (textBox != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
textBox.PreviewKeyDown += textBox_PreviewKeyDown;
textBox.PreviewTextInput += textBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, textBox_TextPasted);
textBox.TextChanged += textBox_TextChanged;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
textBox.PreviewKeyDown -= textBox_PreviewKeyDown;
textBox.PreviewTextInput -= textBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, textBox_TextPasted);
textBox.TextChanged -= textBox_TextChanged;
}
}
}
// Catches User input
private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
TextBox textBox = sender as TextBox;
SetUserInput(textBox, true);
}
// Catches Backspace, Delete, Enter
private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.Key == Key.Return)
{
if (textBox.AcceptsReturn == true)
{
SetUserInput(textBox, true);
}
}
else if (e.Key == Key.Delete)
{
if (textBox.SelectionLength > 0 || textBox.SelectionStart < textBox.Text.Length)
{
SetUserInput(textBox, true);
}
}
else if (e.Key == Key.Back)
{
if (textBox.SelectionLength > 0 || textBox.SelectionStart > 0)
{
SetUserInput(textBox, true);
}
}
}
// Catches pasting
private static void textBox_TextPasted(object sender, DataObjectPastingEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.SourceDataObject.GetDataPresent(DataFormats.Text, true) == false)
{
return;
}
SetUserInput(textBox, true);
}
private static void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
TextChangedFired(textBox, e);
SetUserInput(textBox, false);
}
private static void TextChangedFired(TextBox sender, TextChangedEventArgs e)
{
ICommand command = (ICommand)sender.GetValue(TextChangedCommandProperty);
object[] arguments = new object[] { sender, e, GetUserInput(sender) };
command.Execute(arguments);
}
#region UserInput
private static DependencyProperty UserInputProperty =
DependencyProperty.RegisterAttached("UserInput",
typeof(bool),
typeof(TextChangedBehavior));
private static void SetUserInput(DependencyObject target, bool value)
{
target.SetValue(UserInputProperty, value);
}
private static bool GetUserInput(DependencyObject target)
{
return (bool)target.GetValue(UserInputProperty);
}
#endregion // UserInput
}