Multithread writes to a Rich Text Box

G

Guest

Hello,

Sorry for the long-winded dissertation - but, I have an application where I
need to write text to a rich text box using a common method, callable from
anywhere in my application. The color of the text will depend upon the first
argument and the text will be contained in the second argumet. As far as I
know, my application is not multithreaded - at least I don't knowingly start
any other threads. Since I don't know what the heck I'm doing, I found an
application that did this type of thing and merely "borrowed" the code to do
it. The code is shown below, with a couple of changes, which I will describe.

My first question involves why the code is written using Invoke rather than
just a simple method call? I do know that Invoke should (must?) be used when
doing multithreading, but I wasn't aware I was doing any multithreading.
However, if I remove the Invoke "wrapper" from around the code it won't even
compile - so I left the "wrapper" in place!

Second, notice that I have a test for the msg parameter being null. This
should never happen but it sometimes does. I've single stepped through it
and sometimes, although msg is not null just prior to entering the Invoke
block, it mysteriously becomes null when that block is entered.

The next issue is the color. Note that I am setting the color with
parameter "msgtype". Most of the time this also works fine. However, once
in a while the parameter value and the font's color property are okay right
up to the rtfTerminal.AppendText statement. But as that statement gets
executed both the parameter and the font property change and the text gets
printed in the wrong color. Sometimes this change occurs in the middle of
the string that's being printed.

The final issue is the jitter that accompanies the ScrollToCaret() method if
the entire message is written as one string followed by a single call to
ScrollToCaret(). It makes no sense to me why doing it the way I have done -
character at a time - should work differently, but it does. I think this is
probably a separate issue from the previous two.

So, my final suspicion on this is that the code I "borrowed" probably has
some inherent philosophical problem that is currently beyond my knowledge.
Is there a more appropriate way to implement a common method to write to a
rich text box?

Thanks,
Ray



public void Log(LogMsgType msgtype, string msg)
{
rtfTerminal.Invoke(new EventHandler(delegate
{
rtfTerminal.SelectedText = string.Empty;
rtfTerminal.SelectionFont = new Font(rtfTerminal.SelectionFont,
FontStyle.Bold);
rtfTerminal.SelectionColor = LogMsgTypeColor[(int)msgtype];
rtfTerminal.Font = new Font(rtfTerminal.SelectionFont,
FontStyle.Bold);
if (msg != null)
{
// This loop prevents scrolling on every character, which causes
jitter.
foreach (char ch in msg)
{
if (ch != '\r')
{
rtfTerminal.AppendText(ch.ToString());
if (ch == '\n')
rtfTerminal.ScrollToCaret();
}
}
Application.DoEvents();
}
else
rtfTerminal.AppendText("\n\n**** ERROR: NULL MESSAGE ****\n\n");
}
));
}
 
J

Jon Skeet [C# MVP]

Ray Mitchell said:
Sorry for the long-winded dissertation - but, I have an application where I
need to write text to a rich text box using a common method, callable from
anywhere in my application. The color of the text will depend upon the first
argument and the text will be contained in the second argumet. As far as I
know, my application is not multithreaded - at least I don't knowingly start
any other threads. Since I don't know what the heck I'm doing, I found an
application that did this type of thing and merely "borrowed" the code to do
it. The code is shown below, with a couple of changes, which I will describe.

My first question involves why the code is written using Invoke rather than
just a simple method call? I do know that Invoke should (must?) be used when
doing multithreading, but I wasn't aware I was doing any multithreading.
However, if I remove the Invoke "wrapper" from around the code it won't even
compile - so I left the "wrapper" in place!

Unfortunately you haven't shown any of the rest of your code, so we've
no idea why you might be using multiple threads. If you dump the stack
trace out somewhere (eg the rich text box!) having captured it *before*
the call to Invoke (i.e. not within the anonymous method) then you may
well see a bit more of what's going on.

Using Invoke is correct here. The call to Application.DoEvents() is
unnecessary, however, and I'm not at all sure about appending a single
character at a time.
Second, notice that I have a test for the msg parameter being null. This
should never happen but it sometimes does. I've single stepped through it
and sometimes, although msg is not null just prior to entering the Invoke
block, it mysteriously becomes null when that block is entered.

Hmm. That just shouldn't happen - I can't think of any reason off-hand
why it would. Again, we'd need more information.
The next issue is the color. Note that I am setting the color with
parameter "msgtype". Most of the time this also works fine. However, once
in a while the parameter value and the font's color property are okay right
up to the rtfTerminal.AppendText statement. But as that statement gets
executed both the parameter and the font property change and the text gets
printed in the wrong color. Sometimes this change occurs in the middle of
the string that's being printed.

Again, that sounds very odd.
The final issue is the jitter that accompanies the ScrollToCaret() method if
the entire message is written as one string followed by a single call to
ScrollToCaret(). It makes no sense to me why doing it the way I have done -
character at a time - should work differently, but it does. I think this is
probably a separate issue from the previous two.

Yes, I suspect so.

<snip>
 
N

Nicholas Paldino [.NET/C# MVP]

Ray,

I would address the issue of multithreading first. Since you are not
making this call on another thread, I would say to take out the call to
Invoke. In regards to the part that has:

delegate
{
// Code here.
}

You can take the delegate and the brackets away, and just call the code
directly, and it should work.

You should also probably take out the call to DoEvents, as it's just
pointless in this case.

As for the msgtype parameter changing (the colors and whatnot) if you
don't have a multithreaded program, then nothing should be changing this
once you get into your method.

Also, if msg is null, or blank, then you should look at the call stack
when this method is called to try and determine the path the code took to
make msg null. Parameters don't just disappear.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Ray Mitchell said:
Hello,

Sorry for the long-winded dissertation - but, I have an application where
I
need to write text to a rich text box using a common method, callable from
anywhere in my application. The color of the text will depend upon the
first
argument and the text will be contained in the second argumet. As far as
I
know, my application is not multithreaded - at least I don't knowingly
start
any other threads. Since I don't know what the heck I'm doing, I found an
application that did this type of thing and merely "borrowed" the code to
do
it. The code is shown below, with a couple of changes, which I will
describe.

My first question involves why the code is written using Invoke rather
than
just a simple method call? I do know that Invoke should (must?) be used
when
doing multithreading, but I wasn't aware I was doing any multithreading.
However, if I remove the Invoke "wrapper" from around the code it won't
even
compile - so I left the "wrapper" in place!

Second, notice that I have a test for the msg parameter being null. This
should never happen but it sometimes does. I've single stepped through it
and sometimes, although msg is not null just prior to entering the Invoke
block, it mysteriously becomes null when that block is entered.

The next issue is the color. Note that I am setting the color with
parameter "msgtype". Most of the time this also works fine. However,
once
in a while the parameter value and the font's color property are okay
right
up to the rtfTerminal.AppendText statement. But as that statement gets
executed both the parameter and the font property change and the text gets
printed in the wrong color. Sometimes this change occurs in the middle of
the string that's being printed.

The final issue is the jitter that accompanies the ScrollToCaret() method
if
the entire message is written as one string followed by a single call to
ScrollToCaret(). It makes no sense to me why doing it the way I have
done -
character at a time - should work differently, but it does. I think this
is
probably a separate issue from the previous two.

So, my final suspicion on this is that the code I "borrowed" probably has
some inherent philosophical problem that is currently beyond my knowledge.
Is there a more appropriate way to implement a common method to write to a
rich text box?

Thanks,
Ray



public void Log(LogMsgType msgtype, string msg)
{
rtfTerminal.Invoke(new EventHandler(delegate
{
rtfTerminal.SelectedText = string.Empty;
rtfTerminal.SelectionFont = new Font(rtfTerminal.SelectionFont,
FontStyle.Bold);
rtfTerminal.SelectionColor = LogMsgTypeColor[(int)msgtype];
rtfTerminal.Font = new Font(rtfTerminal.SelectionFont,
FontStyle.Bold);
if (msg != null)
{
// This loop prevents scrolling on every character, which
causes
jitter.
foreach (char ch in msg)
{
if (ch != '\r')
{
rtfTerminal.AppendText(ch.ToString());
if (ch == '\n')
rtfTerminal.ScrollToCaret();
}
}
Application.DoEvents();
}
else
rtfTerminal.AppendText("\n\n**** ERROR: NULL MESSAGE
****\n\n");
}
));
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top