问题描述
我正在尝试修改.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强制显示更改,暂时休眠线程以使更改可以看到,然后恢复原始选择并添加回事件处理程序。
此代码存在几个问题:
- 每当键入')'或使用箭头键移动光标时,都会正确调用FlashParenthesis,但是匹配的'('仅在使用箭头键时才突出显示(而在键入')'时不突出显示)。
- 在大多数情况下,突出显示'('可以使您看到选择的变化,但是在大约10%到30%的时间中,突出显示只能使您看到快速闪烁。
- 对dispatcher.Invoke的调用是我在网上发现的一些伏都教徒,它们会强制显示选择更改。似乎应该有一种更好的方法来执行此操作,但是如果没有此调用,我无法找到其他任何方法来使代码正常工作。
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);
}