在.NET C#TextBox中暂时突出显示文本

问题描述

我正在尝试修改.NET C#TextBox类以实现括号平衡,以便每当键入右括号(或将光标移动到一个的右端)时,相应的左括号就会立即突出显示。 / p>

为此,我创建了TextBox的子类,每当更改选择时都会得到通知

public BalanceTextBox() : base()
  {         
   this.AcceptsReturn = true;
   this.IsReadOnly = false;
   this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
  }

如果光标位于右括号的右端,事件处理程序将调用一个方法,该方法寻找匹配的左括号:

private void TextBoxSelectionChanged(object sender,RoutedEventArgs e)
   {
    BalanceCheck();
   }

如果找到匹配的左括号,则将调用FlashParenthesis方法暂时突出显示它。

public void FlashParenthesis(
  int selStart,int selLength,int leftParenLocation)
  {
   this.SelectionChanged -= new RoutedEventHandler(TextBoxSelectionChanged);

   this.Select(leftParenLocation,1);
   this.dispatcher.Invoke(dispatcherPriority.Render,new Action(delegate { }));
   Thread.Sleep(200);
   this.Select(selStart,selLength);

   this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
  }

方法临时删除事件处理程序(以便不递归调用),将光标选择更改为突出显示左括号,调用dispatcher.Invoke强制显示更改,暂时休眠线程以使更改可以看到,然后恢复原始选择并添加回事件处理程序。

代码存在几个问题:

  1. 每当键入')'或使用箭头键移动光标时,都会正确调用FlashParenthesis,但是匹配的'('仅在使用箭头键时才突出显示(而在键入')'时不突出显示)。
  2. 在大多数情况下,突出显示'('可以使您看到选择的变化,但是在大约10%到30%的时间中,突出显示只能使您看到快速闪烁。
  3. dispatcher.Invoke的调用是我在网上发现的一些伏都教徒,它们会强制显示选择更改。似乎应该有一种更好的方法来执行此操作,但是如果没有此调用,我无法找到其他任何方法来使代码正常工作。

BalanceTextBoxClass的完整代码

using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace BALANCE
  {
   public partial class BalanceTextBox : TextBox 
     {
      public BalanceTextBox() : base()
        {         
         this.AcceptsReturn = true;
         this.IsReadOnly = false;
         this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
        }
        
    private void TextBoxSelectionChanged(object sender,RoutedEventArgs e)
        {
         BalanceCheck();
        }

    public void FlashParenthesis(
        int selStart,int leftParenLocation)
        {
         Console.WriteLine("Balance Left Parenthesis at " + leftParenLocation);

         this.SelectionChanged -= new RoutedEventHandler(TextBoxSelectionChanged);

         this.Select(leftParenLocation,1);
         this.dispatcher.Invoke(dispatcherPriority.Render,new Action(delegate { }));
         Thread.Sleep(200);
         this.Select(selStart,selLength);

         this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
        }
                       
    public void BalanceCheck()
        {
         char charactertocheck;
         
         if (this.Text.Length == 0)
            { return; }

         int selStart = this.SelectionStart;
         int selLength = this.SelectionLength;

         if (selLength != 0)
            { return; }

         int cursorLocation = this.CaretIndex;
         if (cursorLocation == 0) 
           { return; }

         cursorLocation--;
         charactertocheck = this.Text[cursorLocation];
         if (charactertocheck != ')')
           { return; }
         
         int nestingDepth = 1;
         while (cursorLocation-- != 0)
           {
            charactertocheck = this.Text[cursorLocation];
            
            if (charactertocheck != '(')
              {
               if (charactertocheck == ')')
                 { nestingDepth++; }
               continue; 
              }
            
            nestingDepth--;
            if (nestingDepth != 0)
              { continue; }
              
            FlashParenthesis(selStart,selLength,cursorLocation);

            return;
           }
        }
     }
  }

解决方法

我能够通过在构造函数中设置IsReadOnlyCaretVisible属性来解决此问题:

public BalanceTextBox() : base()
  {         
   this.AcceptsReturn = true;
   this.IsReadOnly = false;
   this.IsReadOnlyCaretVisible = true;
   this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
  }

,然后将FlashParenthesis修改为使用await Task.Delay,并使TextBox暂时为只读,以便在临时移动光标时不能更改文本。

public async void FlashParenthesis(
  int selStart,int selLength,int leftParenLocation)
  {
   this.SelectionChanged -= new RoutedEventHandler(TextBoxSelectionChanged);

   this.IsReadOnly = true;
   this.Select(leftParenLocation,1);
   await Task.Delay(150);
   this.Select(selStart,selLength);
   this.IsReadOnly = false;

   this.SelectionChanged += new RoutedEventHandler(TextBoxSelectionChanged);
  }