如何在WPF中跟踪TextBox中删除的字符?

时间:2010-06-16 08:08:34

标签: wpf textbox wpf-controls

我想跟踪用户通过Delete或BackSpace Key删除哪个字符。

我正在处理文本框的TextBox_ChangedEvent。

我可以从 TextChangedEventArgs e.Changes 中提取已删除的字符,如果是,我该怎么做?

我想限制用户删除TextBox中的任何字符。我希望用户只能删除两个字符(让我们说“(或”)“)

请建议。

3 个答案:

答案 0 :(得分:9)

下面你会找到一个附加属性的代码,可以像这样用来防止从TextBox中删除除“(”或“)”之外的任何东西。

<TextBox my:TextBoxRestriction.RestrictDeleteTo="()" ... />

这将正确处理所有鼠标和键盘更新,例如:

  1. 使用包含所选多个字符的Delete键
  2. 使用退格键
  3. 使用Ctrl-X剪切
  4. 点击菜单栏上的“剪切”按钮
  5. 因此,它比简单地拦截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时,它会设置Text,反之亦然。 TextChanged处理程序检查Text是否与UnrestrictedText不同。如果是这样,它知道Text已经通过设置UnrestrictedText之外的其他机制进行了更新,因此会扫描更改以进行非法删除。如果找到一个,它会将Text设置回仍然存储在UnrestrictedText中的值,从而阻止更改。

答案 1 :(得分:1)

处理它的附加行为

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" />

答案 2 :(得分:0)

我不知道WPF,但假设它与WinForms相同(似乎很可能)。我知道的唯一方法是你实际上将当前文本保存在变量和文本更改中,如果它不是删除或退格,则更新该文本,否则使用它比较更改的内容以及是否应该允许更改。

编辑:看TextChangedEventArgs.Changes,我上面描述的方式似乎仍然是可行的方法,但您可以使用Changes来更有效地比较文本。

您可能已经考虑过了,但除此之外,请记住也要处理剪切和粘贴(并且用户可能使用鼠标而不是键盘来执行此操作)。