【问题标题】:TextBox - Identify Whether Data Entered is by the userTextBox - 识别输入的数据是否由用户
【发布时间】:2014-01-06 21:35:58
【问题描述】:

我们使用 TextBox 来显示从 IO 系统接收到的数据。如果用户在文本框中输入了一些数据,该值将被写入 IO 系统。

我们正在使用 OnTextChanged 事件将用户输入的数据写入 IO 系统。

问题是当我们更新从 IO 系统接收到的值到文本框(来自代码)时,我们得到了这个事件。

是否可以知道TextBox的值是用户修改的还是代码修改的?

【问题讨论】:

    标签: wpf wpf-controls


    【解决方案1】:

    我不知道这是否适合您的情况,但您可以尝试使用KeyUpKeyDown 事件。仅当用户向 TextBox 键入内容时才会引发该事件,如果文本被代码更改则不会引发该事件。 KeyUp 事件在TextChanged 之后引发,因此您可以在 TextBox 中准备好新键入的文本。并且 KeyDownTextChanged 之前提出,因此您需要在 TextBox 中附加当前文本并按下字符/键。使用KeyUp/KeyDown 的缺点是,如果用户使用上下文菜单(右键单击> 粘贴)通过复制/剪切粘贴输入文本,您将不会收到通知。解决方法是禁用默认上下文菜单,如下所示(或通过样式):

    <TextBox ContextMenu="{x:Null}"/>
    

    用户仍然可以使用键盘快捷键进行剪切/复制/粘贴,并且您的程序会在按下某些键盘键时收到通知。

    希望这个想法适合您的需求。

    【讨论】:

      【解决方案2】:

      由于我不确定你到底在追求什么,所以我会给你两个解决方案。

      解决方案 1:使用 MVVM 进行绑定。

      不要选择 OnTextChanged 事件,而是将 TextBox 绑定到视图模型上的属性。这将通过属性更新文本。从代码更新时,调用一个方法来更新支持字段。 ViewModel 看起来像

      public class MyViewModel : INotifyPropertyChanged
      {
          private string text;
      
          public string Text
          {
              get { return text; }
              set
              {
                  if (value != text)
                  {
                      text = value;
                      OnPropertyChanged();
                      Debug.WriteLine("Binding Example - Keyboard entry");
                  }
              }
          }
      
          public void AddText(string extraText)
          {
              this.text += extraText;
              this.OnPropertyChanged("Text");
              Debug.WriteLine("Binding Example - Code entry");
          }
      
          public void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              var handler = PropertyChanged;
              if (handler != null)
              {
                  handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
              }
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
      }
      

      请注意,即使我在 AddText 方法中更新支持字段,我仍然调用 OnPropertyChange 来更新与 TextBox 的绑定。 TextBox 的 xaml 将是

      <TextBox Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" />
      

      如果您希望在键入时保存,则 UpdateSourceTrigger 很重要。

      虽然这是我的首选解决方案,但大量文本可能会导致性能问题。

      场景 2:OnTextChanged

      您已经在处理一个事件,处理所有相关的键盘事件(可能只有 Keydown)。您的 TextBox Xaml 将是

      <TextBox x:Name="EntryControl" PreviewKeyDown="UIElement_OnPreviewKeyDown" PreviewKeyUp="UIElement_OnPreviewKeyUp" KeyDown="UIElement_OnKeyDown" KeyUp="UIElement_OnKeyUp" TextChanged="TextBoxBase_OnTextChanged" />
      

      如果每个事件都将其名称输出到输出窗口,您将按以下顺序获取事件

      • UIElement_OnPreviewKeyDown
      • UIElement_OnKeyDown
      • TextBoxBase_OnTextChanged
      • UIElement_OnPreviewKeyUp
      • UIElement_OnKeyUp

      通过在 OnKeyDown 事件中设置一个布尔值并在 OnTextChanged 事件中检查它,您可以测试键盘输入。

      注意:当按住一个键时,您将获得多个 KeyDown 事件到单个 KeyUp 事件。事件触发的顺序是

      -UIElement_OnPreviewKeyDown -UIElement_OnKeyDown -TextBoxBase_OnTextChanged -UIElement_OnPreviewKeyDown -UIElement_OnKeyDown -TextBoxBase_OnTextChanged -UIElement_OnPreviewKeyDown -UIElement_OnKeyDown -TextBoxBase_OnTextChanged -UIElement_OnPreviewKeyUp -UIElement_OnKeyUp

      所以解决方案仍然有效。

      测试解决方案

      如果您希望在示例应用中尝试这 2 个场景,我的示例中的 xaml 是

      <Window x:Class="StackOverflow._20949513.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
              Title="MainWindow" Height="350" Width="525">
          <Grid>
              <Grid.RowDefinitions>
                  <RowDefinition />
                  <RowDefinition />
              </Grid.RowDefinitions>
              <DockPanel>
                  <Button Content="Add Text" DockPanel.Dock="Top" Click="BindingButton_OnClick" />
                  <TextBox Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" />
              </DockPanel>
              <DockPanel Grid.Row="1">
                  <Button Content="Add Text" DockPanel.Dock="Top" Click="EventButton_OnClick" />
                  <TextBox x:Name="EntryControl" PreviewKeyDown="UIElement_OnPreviewKeyDown" PreviewKeyUp="UIElement_OnPreviewKeyUp" KeyDown="UIElement_OnKeyDown" KeyUp="UIElement_OnKeyUp" TextChanged="TextBoxBase_OnTextChanged" />
              </DockPanel>
          </Grid>
      </Window>
      

      后面的代码是

      public partial class MainWindow : Window
      {
          public MainWindow()
          {
              ViewModel = new MyViewModel();
              InitializeComponent();
          }
      
          public MyViewModel ViewModel { get; set; }
      
          private void BindingButton_OnClick(object sender, RoutedEventArgs e) { ViewModel.AddText("More Text"); }
      
      
          private bool keyboardPressed;
      
          private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e) { DisplayEvent(); }
      
          private void UIElement_OnPreviewKeyUp(object sender, KeyEventArgs e) { DisplayEvent(); }
      
          private void UIElement_OnKeyDown(object sender, KeyEventArgs e)
          {
              DisplayEvent();
              keyboardPressed = true;
          }
      
          private void UIElement_OnKeyUp(object sender, KeyEventArgs e) { DisplayEvent(); }
      
          private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
          {
              try
              {
                  if (keyboardPressed)
                  {
                      DisplayEvent("TextBoxBase_OnTextChanged from Keyboard");
                  }
                  else
                  {
                      DisplayEvent("TextBoxBase_OnTextChanged from Code");
                  }
              }
              finally
              {
                  keyboardPressed = false;
              }
          }
      
          private void DisplayEvent([CallerMemberName] string caller = null) { Debug.WriteLine(caller); }
      
          private void EventButton_OnClick(object sender, RoutedEventArgs e) { EntryControl.Text += "More Text"; }
      }
      

      ViewModel 在答案开头完整显示。

      为简洁起见,排除了适当的异常处理。

      我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-07
        • 1970-01-01
        • 1970-01-01
        • 2013-12-03
        • 2017-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多