Gre*_*reg 17 wpf events wpf-controls
我想区分以编程方式更改文本(例如在按钮单击处理程序事件中)和用户输入(键入,剪切和粘贴文本).
可能吗?
Fre*_*lad 25
a中的用户输入TextBox可以用
将这三个事件与bool标志组合以指示在TextChanged事件之前是否发生了上述任何事件,并且您将知道更新的原因.
键入和粘贴很容易,但Backspace并不总是触发TextChanged(如果没有选择文本,光标位于0位置).因此,PreviewTextInput需要一些逻辑.
这是一个附加行为,它实现上面的逻辑并在TextChanged引发时执行带有bool标志的命令.
<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
}
与 JHunz 的答案类似,只需将布尔成员变量添加到您的控件中即可:
bool programmaticChange = false;
当您进行程序更改时,请执行以下操作:
programmaticChange = true;
// insert changes to the control text here
programmaticChange = false;
在事件处理程序中,您只需检查 的值programmaticChange即可确定是否是编程更改。
相当明显,不是很优雅,但它也可行且简单。
根据您的确切需求,您可以TextBox.IsFocused在TextChanged事件中使用来确定手动输入。这显然不会涵盖所有的编程更改方式,但适用于很多示例就好了,并且是一种非常干净且保存的方式。
基本上这在以下情况下有效:
...程序更改全部基于手动更改(例如按下按钮)。
如果出现以下情况,它将不起作用:
...编程更改完全基于代码(例如计时器)。  
代码示例:
textBox.TextChanged += (sender, args) =>
    if (textBox.IsFocused)
    {
        //do something for manual input
    }
    else
    {
        //do something for programmatical input
    }
}
如果您只想使用内置的WPF文本框,那么我认为这是不可能的。
在Silverlight论坛上也有类似的讨论:http : //forums.silverlight.net/p/119128/268453.aspx 这不是完全相同的问题,但我认为与原始帖子中类似的想法可能会解决这个问题。给你的把戏。在子类TextBox上有一个SetText方法,该方法在更改文本之前先设置一个标志,然后再将其重新设置。然后,您可以检查TextChanged事件中的标志。当然,这将需要对所有程序化文本进行更改才能使用该方法,但是如果您对项目有足够的控制权以强制执行,我认为它将起作用。
| 归档时间: | 
 | 
| 查看次数: | 23780 次 | 
| 最近记录: |