From: Ray Mitchell on
Hello,

I'm revisiting a problem I've never solved satisfactorily. I have a
multi-line RichTextBox that receives text input from a serial port (not the
keyboard); that input frequently contains backspace characters. To handle
the backspace characters I'm currently calculating the offset of the
character to be backspaced over from the beginning of the entire text box,
selecting that character, then replacing it with no character. The "for"
loop in the code below calculates the length of everything in the text box.
While this scheme has worked correctly, it really slows the system down as
the contents of the text box increases. It seems that ideally I should be
able to deal only with the last line in the text box instead of having to
treat everything in the text box as one big long string, but I haven't
figured out a way to do this. Another brutal alternative to recalculating
the entire length of the text box each time a backspace character is
encountered would be to manually keep a running count of the total number of
characters as they are added and subtracted, and this would doubtless speed
things up significantly. However, it seems that there still should be a
better way.

Thanks,
Ray

***** I execute the following code each time a backspace character is
encountered:

int displayLineCount = display.Lines.Length;
if (displayLineCount > 0)
{
string lastLine = display.Lines[displayLineCount - 1];
int lastLineLength = lastLine.Length;
if (lastLineLength > 0)
{
// Get count of all characters from beginning of display up to the
current line.
// For purposes of calculating character offset of the last display
character from
// the beginning of the display, the length of each line is considered
to be 1 plus
// the number of characters on that line.
int length = 0;
for (int lineNo = 0; lineNo < displayLineCount - 1; ++lineNo)
length += display.Lines[lineNo].Length + 1;
display.Select(length + lastLineLength - 1, 1);
display.SelectedText = "";
display.SelectionStart = length + lastLineLength - 1;
}
}

From: Peter Duniho on
Ray Mitchell wrote:
> Hello,
>
> I'm revisiting a problem I've never solved satisfactorily. I have a
> multi-line RichTextBox that receives text input from a serial port (not the
> keyboard); that input frequently contains backspace characters. To handle
> the backspace characters I'm currently calculating the offset of the
> character to be backspaced over from the beginning of the entire text box,
> selecting that character, then replacing it with no character. The "for"
> loop in the code below calculates the length of everything in the text box.
> While this scheme has worked correctly, it really slows the system down as
> the contents of the text box increases.

Even the code you posted could be improved. You should not need to
recalculate the character position of interest from the contents of the
control, since your own code is the code adding text to it (and so can
easily keep track of that information). And you should also only
retrieve the Lines property value once; the array being returned has to
be recreated each time you get the property value, and as the amount of
text increases, that's a lot of data to be copied.

But I think all of that is actually not relevant. It's not clear to me
why you're calculating the text position the way you are, even ignoring
its implicit assumption that the newline is only one character (have you
checked to make sure that if "\r\n" is used as the newline, the text box
control converts that to a single character?). After all, in the end
you are just trying to delete the last character of the text, right?
Why not just use the TextLength property to determine the correct
character offset?

So, assuming the backspace character should always just delete the last
character in the control, it seems to me that the following should work
just as well, and be much more efficient:

int ichDelete = display.TextLength - 1;

display.Select(ichDelete - 1, 1);
display.SelectedText = "";
display.SelectionStart = ichDelete;

(And actually, I haven't tested...it's possible that last line isn't
required, since the selection has to wind up _somewhere_ after the
delete and it's likely that the control will just put the text cursor in
the location where you've just deleted the text).

> It seems that ideally I should be
> able to deal only with the last line in the text box instead of having to
> treat everything in the text box as one big long string, but I haven't
> figured out a way to do this. Another brutal alternative to recalculating
> the entire length of the text box each time a backspace character is
> encountered would be to manually keep a running count of the total number of
> characters as they are added and subtracted, and this would doubtless speed
> things up significantly. However, it seems that there still should be a
> better way.

See above.

Beyond that, you may recall that a couple of years ago, I pointed you to
a simple custom control implementation I'd written as a demo but which
could be modified to suit your needs. In particular, by writing a
custom control, you then have direct access to the internal data
structures, and can provide whatever public API to the control class
that best fits your usage scenarios. I think that you can do much
better with the RichTextBox class than you currently are, but it may
well be that eventually you find it's just too limited.

If so, you may want to revisit this discussion:
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/d4c6ff994e2286d7/ae29018b8c504219

Pete
From: Ray Mitchell on
"Peter Duniho" wrote:

> Ray Mitchell wrote:
> > Hello,
> >
> > I'm revisiting a problem I've never solved satisfactorily. I have a
> > multi-line RichTextBox that receives text input from a serial port (not the
> > keyboard); that input frequently contains backspace characters. To handle
> > the backspace characters I'm currently calculating the offset of the
> > character to be backspaced over from the beginning of the entire text box,
> > selecting that character, then replacing it with no character. The "for"
> > loop in the code below calculates the length of everything in the text box.
> > While this scheme has worked correctly, it really slows the system down as
> > the contents of the text box increases.
>
> Even the code you posted could be improved. You should not need to
> recalculate the character position of interest from the contents of the
> control, since your own code is the code adding text to it (and so can
> easily keep track of that information). And you should also only
> retrieve the Lines property value once; the array being returned has to
> be recreated each time you get the property value, and as the amount of
> text increases, that's a lot of data to be copied.
>
> But I think all of that is actually not relevant. It's not clear to me
> why you're calculating the text position the way you are, even ignoring
> its implicit assumption that the newline is only one character (have you
> checked to make sure that if "\r\n" is used as the newline, the text box
> control converts that to a single character?). After all, in the end
> you are just trying to delete the last character of the text, right?
> Why not just use the TextLength property to determine the correct
> character offset?
>
> So, assuming the backspace character should always just delete the last
> character in the control, it seems to me that the following should work
> just as well, and be much more efficient:
>
> int ichDelete = display.TextLength - 1;
>
> display.Select(ichDelete - 1, 1);
> display.SelectedText = "";
> display.SelectionStart = ichDelete;
>

As usual Pete, very helpful answer. Thanks very much. The code above
worked great, except that "ichDelete - 1" should be "ichDelete".

> (And actually, I haven't tested...it's possible that last line isn't
> required, since the selection has to wind up _somewhere_ after the
> delete and it's likely that the control will just put the text cursor in
> the location where you've just deleted the text).
>
> > It seems that ideally I should be
> > able to deal only with the last line in the text box instead of having to
> > treat everything in the text box as one big long string, but I haven't
> > figured out a way to do this. Another brutal alternative to recalculating
> > the entire length of the text box each time a backspace character is
> > encountered would be to manually keep a running count of the total number of
> > characters as they are added and subtracted, and this would doubtless speed
> > things up significantly. However, it seems that there still should be a
> > better way.
>
> See above.
>
> Beyond that, you may recall that a couple of years ago, I pointed you to
> a simple custom control implementation I'd written as a demo but which
> could be modified to suit your needs. In particular, by writing a
> custom control, you then have direct access to the internal data
> structures, and can provide whatever public API to the control class
> that best fits your usage scenarios. I think that you can do much
> better with the RichTextBox class than you currently are, but it may
> well be that eventually you find it's just too limited.
>
> If so, you may want to revisit this discussion:
> http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/d4c6ff994e2286d7/ae29018b8c504219
>
> Pete
> .
>
From: Peter Duniho on
Ray Mitchell wrote:
> [...]
>> int ichDelete = display.TextLength - 1;
>>
>> display.Select(ichDelete - 1, 1);
>> display.SelectedText = "";
>> display.SelectionStart = ichDelete;
>>
>
> As usual Pete, very helpful answer. Thanks very much. The code above
> worked great, except that "ichDelete - 1" should be "ichDelete".

Quite right. Sorry about that. Managed to apply the "one character
from the end" logic twice. :)