By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,619 Members | 1,534 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,619 IT Pros & Developers. It's quick & easy.

A scrolling text display that does't flash - possible in C#?

P: n/a
Hello,

I know I sound like a one-note Johnny on this but I'm still looking for a
solution. I need to display characters coming in from a serial port or a
socket. I also need to be able to type characters into the display myself -
but that's not the main issue at this time. I've tried a scrolling multiline
text box but once the original viewable area fills up and it starts scrolling
the flashing of the entire area drives me nuts. The vertical scroll bar can
be seen to bounce up and down as the control apparently updates the display
with every string it has including those that are not even in view. I've
limited my string array to just a few hundred strings just so it will have
less to update but that doesn't seem to matter. I've tried setting the
double-buffering property on the form, which accomplishes nothing. I've also
tried not even using a control and merely writing directly onto a form but I
haven't really accomplished much there either.

I've talked to several other C# "programmers" here about the flashing and
they obviously know about as little as I do since they all tell me they have
the same problem and have not found a solution (they just keep a good supply
of headache pills on hand). I've seen other applications such as TeraTerm,
HyperTerm, and others that don't have this problem but I'd like to accomplish
the same thing in C# if possible. A sample application for just adding
characters to a non-flashing display would be appreciated since I'm not very
knowledgeable regarding digging down to the individual character x-y
coordinate and font size level, invalidating only over a single character,
It doesn't seem like that should be necessary anyway (but maybe it is?).

As Always, Thanks,
Ray
Aug 15 '07 #1
Share this Question
Share on Google+
11 Replies


P: n/a


"Ray Mitchell" wrote:
Hello,

I know I sound like a one-note Johnny on this but I'm still looking for a
solution. I need to display characters coming in from a serial port or a
socket. I also need to be able to type characters into the display myself -
but that's not the main issue at this time. I've tried a scrolling multiline
text box but once the original viewable area fills up and it starts scrolling
the flashing of the entire area drives me nuts. The vertical scroll bar can
be seen to bounce up and down as the control apparently updates the display
with every string it has including those that are not even in view. I've
limited my string array to just a few hundred strings just so it will have
less to update but that doesn't seem to matter. I've tried setting the
double-buffering property on the form, which accomplishes nothing. I've also
tried not even using a control and merely writing directly onto a form but I
haven't really accomplished much there either.

I've talked to several other C# "programmers" here about the flashing and
they obviously know about as little as I do since they all tell me they have
the same problem and have not found a solution (they just keep a good supply
of headache pills on hand). I've seen other applications such as TeraTerm,
HyperTerm, and others that don't have this problem but I'd like to accomplish
the same thing in C# if possible. A sample application for just adding
characters to a non-flashing display would be appreciated since I'm not very
knowledgeable regarding digging down to the individual character x-y
coordinate and font size level, invalidating only over a single character,
It doesn't seem like that should be necessary anyway (but maybe it is?).

As Always, Thanks,
Ray

I just discovered something new. Maybe it will give someone a hint as to
what's going on. If I give some other form the focus the complete rewriting
and refreshing of everything in the display control and the bouncing of the
vertical scroll bar stops and the unbearable flashing stops. Instead, there
is just a slight flicker on some of the lines which, although it would be
better not to have, is at least readable. As soon as I give the focus back,
it starts flashing again. Any ideas?
Thanks, Ray
Aug 15 '07 #2

P: n/a
Ray Mitchell wrote:
[...]
I've talked to several other C# "programmers" here about the flashing and
they obviously know about as little as I do since they all tell me they have
the same problem and have not found a solution (they just keep a good supply
of headache pills on hand).
I thought we'd covered this.

Generally:

Flashing occurs when an control is redrawn multiple times for a single
update. The most common scenario is when the control is not
double-buffered, resulting in the background being erased on-screen,
followed by the actual drawing of the new data.

It's not the only scenario though. Anything that causes the graphics
on-screen to change and then change again immediately after will cause
flashing. This can be as simple as clearing the Text property of a
control and then setting it to something else, or any number of other
things.

The first thing to do is make sure your control is double-buffered.
Then you can look at what else might be causing the flashing, if that
doesn't fix it.
I've seen other applications such as TeraTerm,
HyperTerm, and others that don't have this problem but I'd like to accomplish
the same thing in C# if possible.
As I'm pretty sure I have mentioned before, the applications you are
talking about don't use a standard, off-the-shelf control to display
their text. They undoubtedly have a custom control so that they have
complete control over how the drawing is done. You can easily do the same.
A sample application for just adding
characters to a non-flashing display would be appreciated since I'm not very
knowledgeable regarding digging down to the individual character x-y
coordinate and font size level, invalidating only over a single character,
It doesn't seem like that should be necessary anyway (but maybe it is?).
For best control over the drawing, it may be. Perhaps if I find some
time, I'll code up a sample.

In the meantime, one thing you need to do is understand why your current
code flashes. If it's not the lack of double-buffering, it must be
something else. What else is that? How are you using the control? Are
you in fact clearing the text? If not, maybe the control does something
like that internally when you change its contents.

One thing you might try is suppressing drawing when you are changing the
value of the control. This will require subclassing the control,
enabling double-buffering, overriding the OnPaint() method, and then
setting a flag while you change the contents of the control. When
you're done updating the control, clear the flag again and invalidate
the entire control so that it redraws.

In the OnPaint() method, only call base.OnPaint() if your flag isn't set.

Finally, you will find that you get the best help if you put together a
concise-but-complete sample of code that illustrates how your code works
now, and what doesn't work for you. Absent such a sample, all we can do
is guess, and there's no guarantee that any sample code someone else
might provide will be successfully integrated by you into your existing
application. Knowing where you stand now would go a long way to helping
make sure the advice is to the point and helpful to you.

Pete
Aug 15 '07 #3

P: n/a


"Peter Duniho" wrote:
Ray Mitchell wrote:
[...]
I've talked to several other C# "programmers" here about the flashing and
they obviously know about as little as I do since they all tell me they have
the same problem and have not found a solution (they just keep a good supply
of headache pills on hand).

I thought we'd covered this.

Generally:

Flashing occurs when an control is redrawn multiple times for a single
update. The most common scenario is when the control is not
double-buffered, resulting in the background being erased on-screen,
followed by the actual drawing of the new data.

It's not the only scenario though. Anything that causes the graphics
on-screen to change and then change again immediately after will cause
flashing. This can be as simple as clearing the Text property of a
control and then setting it to something else, or any number of other
things.

The first thing to do is make sure your control is double-buffered.
Then you can look at what else might be causing the flashing, if that
doesn't fix it.
I've seen other applications such as TeraTerm,
HyperTerm, and others that don't have this problem but I'd like to accomplish
the same thing in C# if possible.

As I'm pretty sure I have mentioned before, the applications you are
talking about don't use a standard, off-the-shelf control to display
their text. They undoubtedly have a custom control so that they have
complete control over how the drawing is done. You can easily do the same.
A sample application for just adding
characters to a non-flashing display would be appreciated since I'm not very
knowledgeable regarding digging down to the individual character x-y
coordinate and font size level, invalidating only over a single character,
It doesn't seem like that should be necessary anyway (but maybe it is?).

For best control over the drawing, it may be. Perhaps if I find some
time, I'll code up a sample.

In the meantime, one thing you need to do is understand why your current
code flashes. If it's not the lack of double-buffering, it must be
something else. What else is that? How are you using the control? Are
you in fact clearing the text? If not, maybe the control does something
like that internally when you change its contents.

One thing you might try is suppressing drawing when you are changing the
value of the control. This will require subclassing the control,
enabling double-buffering, overriding the OnPaint() method, and then
setting a flag while you change the contents of the control. When
you're done updating the control, clear the flag again and invalidate
the entire control so that it redraws.

In the OnPaint() method, only call base.OnPaint() if your flag isn't set.

Finally, you will find that you get the best help if you put together a
concise-but-complete sample of code that illustrates how your code works
now, and what doesn't work for you. Absent such a sample, all we can do
is guess, and there's no guarantee that any sample code someone else
might provide will be successfully integrated by you into your existing
application. Knowing where you stand now would go a long way to helping
make sure the advice is to the point and helpful to you.

Pete
Hi Pete,

Yes, you certainly have covered much of this before and I do appreciate your
patience. My problem is that my experience with all this GUI stuff is very
minimal. I primarily code command line C/C++ applications where I spend most
of my time on the actual algorithm rather than on the graphical user
interface. So of course I know that I need to hit the books regarding all
this but I was simply looking for an easy solution to what seemed to me would
be a pretty common application. My hope was to be able to reuse something
that had already been done and placed out there for public use rather than
make it a detailed education in the details of it all. No big deal, however.
Although I appreciate the sentiment, there's no need for you to spend
additional time coding up something that I should take the time to master
myself.

Thanks,
Ray

Aug 16 '07 #4

P: n/a
Ray Mitchell wrote:
Yes, you certainly have covered much of this before and I do appreciate your
patience. My problem is that my experience with all this GUI stuff is very
minimal. I primarily code command line C/C++ applications where I spend most
of my time on the actual algorithm rather than on the graphical user
interface. So of course I know that I need to hit the books regarding all
this but I was simply looking for an easy solution to what seemed to me would
be a pretty common application. My hope was to be able to reuse something
that had already been done and placed out there for public use rather than
make it a detailed education in the details of it all. No big deal, however.
Although I appreciate the sentiment, there's no need for you to spend
additional time coding up something that I should take the time to master
myself.
Okay. For what it's worth, I did put together a little test project
just so I could try to reproduce your problem. I don't know if I
reproduced your exact scenario, but I did reproduce something like it. :)

One thing I learned is that the "easy out" suggestions I provided aren't
going to help. I haven't looked into this too deeply, but it appears to
me that in addition to the TextBox control wrapping a standard Windows
edit control, that edit control itself may contain yet another control
that acts independently, or it does not use the usual "invalidate, wait
for WM_PAINT" mechanism for updating after the contents change (I'm
guessing the former, but I can't rule out the latter, even though it's
pretty much the opposite of what all Windows GUI software should be doing).

The reason I say that is that even overriding the WndProc, which would
normally allow me direct access to the underlying window, I'm unable to
disable the updating while I'm changing the control. The control
redraws immediately as things are changed, and it doesn't do so through
the handling of a WM_PAINT message (at least not directly), so there's
no direct way to simply intercept the WM_PAINT and prevent redrawing.

Anyway, to get you started on writing your own custom control...

It's really not actually all that hard. You'll want to start with a
class that inherits Control. You'll have to, at a minimum, implement
OnPaint() and provide a public API in the class so that you can do
things like add text. And of course, in the class you need to keep
track of the text. How you store the text will depend on what works
best for your implementation, as well as the API. However, you may find
that a linked list of strings, one per line, works well. That way you
can quickly add new strings to the end, as well as remove old ones from
the beginning.

In OnPaint(), the MeasureString() and DrawString() methods will be your
main workhorse methods. In the simplest case, you'll just start drawing
your text at the origin, using MeasureString() to keep track of where
the next text should go. If you want the text to wrap, .NET can help
you with that via flags passed to MeasureString() and DrawString().
Otherwise, it's as simple as updating the y coordinate for each new line
of text as you draw.

You can make things more complicated by including some sort of internal
margins for your control, and by supporting scrolling. These will
involve offsetting your drawing by various amounts representing those
properties. For scrolling, you may find it desirable for performance
reasons to only actually call DrawString() once your iteration through
the list of strings tells you that your next line of text will in fact
intersect the client area of your control. Likewise, once you've gotten
to the bottom of the control, you may want to just stop drawing, rather
than iterate through the rest of the lines that won't show on the screen
anyway.

You can cache other values as well to speed things further (like what
line of text is the first visible line, etc.), but IMHO unless you find
a demonstrable performance issue it's not really worth worrying about
that. Better to get it working first.

And of course, the most important thing will be to make sure that your
control's DoubleBuffered property is set to true. :)

Pete
Aug 16 '07 #5

P: n/a
On 16 Aug, 03:23, Peter Duniho <NpOeStPe...@NnOwSlPiAnMk.comwrote:
Ray Mitchell wrote:
Yes, you certainly have covered much of this before and I do appreciate your
patience. My problem is that my experience with all this GUI stuff is very
minimal. I primarily code command line C/C++ applications where I spend most
of my time on the actual algorithm rather than on the graphical user
interface. So of course I know that I need to hit the books regarding all
this but I was simply looking for an easy solution to what seemed to me would
be a pretty common application. My hope was to be able to reuse something
that had already been done and placed out there for public use rather than
make it a detailed education in the details of it all. No big deal, however.
Although I appreciate the sentiment, there's no need for you to spend
additional time coding up something that I should take the time to master
myself.

Okay. For what it's worth, I did put together a little test project
just so I could try to reproduce your problem. I don't know if I
reproduced your exact scenario, but I did reproduce something like it. :)

One thing I learned is that the "easy out" suggestions I provided aren't
going to help. I haven't looked into this too deeply, but it appears to
me that in addition to the TextBox control wrapping a standard Windows
edit control, that edit control itself may contain yet another control
that acts independently, or it does not use the usual "invalidate, wait
for WM_PAINT" mechanism for updating after the contents change (I'm
guessing the former, but I can't rule out the latter, even though it's
pretty much the opposite of what all Windows GUI software should be doing).

The reason I say that is that even overriding the WndProc, which would
normally allow me direct access to the underlying window, I'm unable to
disable the updating while I'm changing the control. The control
redraws immediately as things are changed, and it doesn't do so through
the handling of a WM_PAINT message (at least not directly), so there's
no direct way to simply intercept the WM_PAINT and prevent redrawing.

Anyway, to get you started on writing your own custom control...

It's really not actually all that hard. You'll want to start with a
class that inherits Control. You'll have to, at a minimum, implement
OnPaint() and provide a public API in the class so that you can do
things like add text. And of course, in the class you need to keep
track of the text. How you store the text will depend on what works
best for your implementation, as well as the API. However, you may find
that a linked list of strings, one per line, works well. That way you
can quickly add new strings to the end, as well as remove old ones from
the beginning.

In OnPaint(), the MeasureString() and DrawString() methods will be your
main workhorse methods. In the simplest case, you'll just start drawing
your text at the origin, using MeasureString() to keep track of where
the next text should go. If you want the text to wrap, .NET can help
you with that via flags passed to MeasureString() and DrawString().
Otherwise, it's as simple as updating the y coordinate for each new line
of text as you draw.

You can make things more complicated by including some sort of internal
margins for your control, and by supporting scrolling. These will
involve offsetting your drawing by various amounts representing those
properties. For scrolling, you may find it desirable for performance
reasons to only actually call DrawString() once your iteration through
the list of strings tells you that your next line of text will in fact
intersect the client area of your control. Likewise, once you've gotten
to the bottom of the control, you may want to just stop drawing, rather
than iterate through the rest of the lines that won't show on the screen
anyway.

You can cache other values as well to speed things further (like what
line of text is the first visible line, etc.), but IMHO unless you find
a demonstrable performance issue it's not really worth worrying about
that. Better to get it working first.

And of course, the most important thing will be to make sure that your
control's DoubleBuffered property is set to true. :)

Pete
If you do fancy giving it a go, here's a tiny example. There's one
class : control and one form that shows it working. Create a new forms
app add a class and paste the control into it then paste the form code
over the form in the forms app. Everything is written under the 1.1
framework so it should work everywhere.
Watch for line wrap.

Control first
---------------------------------------------------------------------

using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;

namespace WindowsApplication23
{
[System.ComponentModel.DesignerCategory("Code")]
public class UC : Control
{
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.AliceBlue,new Rectangle(Left, Top,
Width, Height));
Font f = new Font("Arial",8.25F);
SizeF s = e.Graphics.MeasureString("this is a test", f,
new SizeF(Width - Left, Height - Top ));
RectangleF r = new RectangleF(Left, Top, s.Width, s.Height);
e.Graphics.FillRectangle(System.Drawing.Brushes.Cy an, r);
e.Graphics.DrawString("this is a test", f, Brushes.Black, r);
r = new RectangleF(Left, Top + r.Height, s.Width, s.Height);
e.Graphics.FillRectangle(System.Drawing.Brushes.Co rnflowerBlue, r);
e.Graphics.DrawString("this is a test", f, Brushes.Black, r);
}
protected override void OnResize(EventArgs e)
{
this.Invalidate();
base.OnResize (e);
}
}
}

Form
----------------------------------------------------------------------------------------

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace WindowsApplication23
{
public class Form2 : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;
private System.Windows.Forms.Button button1;
private UC _uc;

public Form2()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this._uc = new WindowsApplication23.UC();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(88, 128);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(48, 24);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// _uc
//
this._uc.Location = new System.Drawing.Point(10, 10);
this._uc.Name = "_uc";
this._uc.Size = new System.Drawing.Size(46, 102);
this._uc.TabIndex = 0;
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Controls.Add(this._uc);
this.Name = "Form2";
this.Text = "Form2";
this.ResumeLayout(false);

}
#endregion
private void button1_Click(object sender, System.EventArgs e)
{
_uc.Width +=10;
}
[STAThread]
static void Main()
{
Application.Run(new Form2());
}
}
}

Aug 16 '07 #6

P: n/a

I should probably add:

public UC()
{
this.SetStyle(ControlStyles.DoubleBuffer,true);
}

I've just modified it to scroll the string and the above removes any
flicker.
Aug 16 '07 #7

P: n/a


"Peter Duniho" wrote:
Ray Mitchell wrote:
Yes, you certainly have covered much of this before and I do appreciate your
patience. My problem is that my experience with all this GUI stuff is very
minimal. I primarily code command line C/C++ applications where I spend most
of my time on the actual algorithm rather than on the graphical user
interface. So of course I know that I need to hit the books regarding all
this but I was simply looking for an easy solution to what seemed to me would
be a pretty common application. My hope was to be able to reuse something
that had already been done and placed out there for public use rather than
make it a detailed education in the details of it all. No big deal, however.
Although I appreciate the sentiment, there's no need for you to spend
additional time coding up something that I should take the time to master
myself.

Okay. For what it's worth, I did put together a little test project
just so I could try to reproduce your problem. I don't know if I
reproduced your exact scenario, but I did reproduce something like it. :)

One thing I learned is that the "easy out" suggestions I provided aren't
going to help. I haven't looked into this too deeply, but it appears to
me that in addition to the TextBox control wrapping a standard Windows
edit control, that edit control itself may contain yet another control
that acts independently, or it does not use the usual "invalidate, wait
for WM_PAINT" mechanism for updating after the contents change (I'm
guessing the former, but I can't rule out the latter, even though it's
pretty much the opposite of what all Windows GUI software should be doing).

The reason I say that is that even overriding the WndProc, which would
normally allow me direct access to the underlying window, I'm unable to
disable the updating while I'm changing the control. The control
redraws immediately as things are changed, and it doesn't do so through
the handling of a WM_PAINT message (at least not directly), so there's
no direct way to simply intercept the WM_PAINT and prevent redrawing.

Anyway, to get you started on writing your own custom control...

It's really not actually all that hard. You'll want to start with a
class that inherits Control. You'll have to, at a minimum, implement
OnPaint() and provide a public API in the class so that you can do
things like add text. And of course, in the class you need to keep
track of the text. How you store the text will depend on what works
best for your implementation, as well as the API. However, you may find
that a linked list of strings, one per line, works well. That way you
can quickly add new strings to the end, as well as remove old ones from
the beginning.

In OnPaint(), the MeasureString() and DrawString() methods will be your
main workhorse methods. In the simplest case, you'll just start drawing
your text at the origin, using MeasureString() to keep track of where
the next text should go. If you want the text to wrap, .NET can help
you with that via flags passed to MeasureString() and DrawString().
Otherwise, it's as simple as updating the y coordinate for each new line
of text as you draw.

You can make things more complicated by including some sort of internal
margins for your control, and by supporting scrolling. These will
involve offsetting your drawing by various amounts representing those
properties. For scrolling, you may find it desirable for performance
reasons to only actually call DrawString() once your iteration through
the list of strings tells you that your next line of text will in fact
intersect the client area of your control. Likewise, once you've gotten
to the bottom of the control, you may want to just stop drawing, rather
than iterate through the rest of the lines that won't show on the screen
anyway.

You can cache other values as well to speed things further (like what
line of text is the first visible line, etc.), but IMHO unless you find
a demonstrable performance issue it's not really worth worrying about
that. Better to get it working first.

And of course, the most important thing will be to make sure that your
control's DoubleBuffered property is set to true. :)

Pete
Pete,

Thanks for taking so much time to try this out then write such a detailed
explanation. I will take the time to look into your observations and
suggestions. Doesn't it seem a little strange to you that there is not
already a control that just works properly for this purpose? Maybe I'm in
the minority, doing embedded communications monitoring and such low level
work but it still seems like there would be some demand for some sort of
interactive real-time data display that doesn't bounce all over the place.
I've actually looked at the source code for TeraTerm (freely, legally,
available) but it's fairly complex and for me it's difficult to pick out the
portion I'm actually looking for. I figure that if I could, however, I could
get some more ideas. I think it's written with either just Win32 APIs or
maybe MFC, so it might be necessary to invoke some of it as unmanaged code if
there were no managed C# equivalent. Anyway, I want to stick with standard
..NET stuff if possible so I'll take a closer look at using the methods you
suggest for measuring and drawing strings, etc. Thanks again for all your
help and suggestions over the past few weeks/months.

Ray
Aug 17 '07 #8

P: n/a


"DeveloperX" wrote:
>
I should probably add:

public UC()
{
this.SetStyle(ControlStyles.DoubleBuffer,true);
}

I've just modified it to scroll the string and the above removes any
flicker.
Hey DeveloperX,

Thanks for the code. I'll take a look and see what I do with it.

Ray
Aug 17 '07 #9

P: n/a
Peter Duniho wrote:
[...]
Finally, in the realm of serious hacking, I had a couple of other ideas
I haven't had a chance to try out yet, but which I think I will at some
point. One is to do a sort of double-buffering technique, except using
two complete copies of the control in a double-buffered form. The other
is to somehow use the DrawBitmap method to more completely control how
the textbox control gets drawn on the form.

Both of these hacks have serious problems when it comes to user
interaction, I suspect, but if all you want to do is get the text on the
screen, they might just do the trick. They definitely qualify as ugly
hacks though. :)

I'll post back here if anything comes of them.
Okay, the double-copy technique didn't pan out. Even with the main form
double-buffered, drawing for the child controls doesn't actually get
double-buffered and so there's still flicker as each textbox is swapped
to the front.

However, using DrawBitmap worked a lot better. The basic idea is to
have a custom control exactly covering the real control, and when the
real control gets updated, copy the results to a bitmap and then use
that to draw the custom control. With the custom control on top of the
real control, drawing by the real control is completely obscured, and so
redrawing of the visible graphics is controlled completely by the custom
control. Of course, by setting DoubleBuffered to true in the custom
control, flicker can be prevented.

Warning: I got a bit of an icky feeling just writing this code. It
seems kind of evil to me. :) But it does completely stop the flickering.

I've copied the code here. To use it, you'll need to add a new Custom
Control to your project, and then completely replace the non-Designer
part of the implementation with this code. Once you've gotten it
compiled, then you can drag-and-drop a new NoFlashMirror instance from
the Designer toolbox onto your form with the actual textbox control. In
the Designer, set the MirroredControl property of the NoFlashMirror
control instance to the textbox control you want it to cover.

Finally, anywhere in your code that you do something that would change
the visual aspect of the actual textbox control, call
NoFlashMirror.UpdateMirror();

That _should_ be all there is to it.

Of course, this method completely prevents any user interaction with the
underlying control. You can't click with the mouse, select text, use
the arrow keys to move around in the textbox, type into the textbox,
etc. (Well, technically the last two are possible, but because the
NoFlashMirror control relies on the controlling application to notify it
of changes, redrawing to deal with blinking and movement of the caret
isn't handled).

It is _possible_ that there might be some way hook into the underlying
edit control class so that these sorts of things are handled, but I
don't know off the top of my head how that would be done. If the
textbox control were all managed, I think it would be simply a matter of
forwarding various input events (mouse, key, etc.) and subscribing to
the Paint event. But it's not, and so you'd have to do all that stuff
at a lower level, which is trickier from managed code.

Anyway, without further ado...

public partial class NoFlashMirror : Control
{
Control _ctl;
Bitmap _bmp;

public Control MirroredControl
{
get { return _ctl; }
set
{
if (_ctl != value)
{
if (_ctl != null)
{
_ctl.SizeChanged -= _SizeChangedHandler;
_ctl.LocationChanged -= _LocationChangedHandler;
}

_ctl = value;

if (_ctl != null)
{
_ctl.SizeChanged += _SizeChangedHandler;
_ctl.LocationChanged += _LocationChangedHandler;

_SizeChangedHandler(this, new EventArgs());
_LocationChangedHandler(this, new EventArgs());

}

UpdateMirror();
}
}
}

private void _SizeChangedHandler(object sender, EventArgs e)
{
Size = _ctl.Size;
}

private void _LocationChangedHandler(object sender, EventArgs e)
{
Location = _ctl.Location;
}

public void UpdateMirror()
{
if (_ctl != null)
{
if (_bmp == null ||
_bmp.Width != _ctl.Width ||
_bmp.Height != _ctl.Height)
{
_bmp = new Bitmap(_ctl.Width, _ctl.Height);
}

_ctl.DrawToBitmap(_bmp,
new Rectangle(new Point(), _bmp.Size));
}
else
{
_bmp = null;
}

Invalidate();
}

public NoFlashMirror()
{
InitializeComponent();

DoubleBuffered = true;
}

protected override void OnPaint(PaintEventArgs pe)
{
// Calling the base class OnPaint
base.OnPaint(pe);

if (_bmp != null)
{
pe.Graphics.DrawImage(_bmp, new Point());
}
}
}
Aug 17 '07 #10

P: n/a
On 17 Aug, 03:55, Peter Duniho <NpOeStPe...@NnOwSlPiAnMk.comwrote:
Peter Duniho wrote:

[...]
Finally, in the realm of serious hacking, I had a couple of other ideas
I haven't had a chance to try out yet, but which I think I will at some
point. One is to do a sort of double-buffering technique, except using
two complete copies of the control in a double-buffered form. The other
is to somehow use the DrawBitmap method to more completely control how
the textbox control gets drawn on the form.
Both of these hacks have serious problems when it comes to user
interaction, I suspect, but if all you want to do is get the text on the
screen, they might just do the trick. They definitely qualify as ugly
hacks though. :)
I'll post back here if anything comes of them.

Okay, the double-copy technique didn't pan out. Even with the main form
double-buffered, drawing for the child controls doesn't actually get
double-buffered and so there's still flicker as each textbox is swapped
to the front.

However, using DrawBitmap worked a lot better. The basic idea is to
have a custom control exactly covering the real control, and when the
real control gets updated, copy the results to a bitmap and then use
that to draw the custom control. With the custom control on top of the
real control, drawing by the real control is completely obscured, and so
redrawing of the visible graphics is controlled completely by the custom
control. Of course, by setting DoubleBuffered to true in the custom
control, flicker can be prevented.

Warning: I got a bit of an icky feeling just writing this code. It
seems kind of evil to me. :) But it does completely stop the flickering.

I've copied the code here. To use it, you'll need to add a new Custom
Control to your project, and then completely replace the non-Designer
part of the implementation with this code. Once you've gotten it
compiled, then you can drag-and-drop a new NoFlashMirror instance from
the Designer toolbox onto your form with the actual textbox control. In
the Designer, set the MirroredControl property of the NoFlashMirror
control instance to the textbox control you want it to cover.

Finally, anywhere in your code that you do something that would change
the visual aspect of the actual textbox control, call
NoFlashMirror.UpdateMirror();

That _should_ be all there is to it.

Of course, this method completely prevents any user interaction with the
underlying control. You can't click with the mouse, select text, use
the arrow keys to move around in the textbox, type into the textbox,
etc. (Well, technically the last two are possible, but because the
NoFlashMirror control relies on the controlling application to notify it
of changes, redrawing to deal with blinking and movement of the caret
isn't handled).

It is _possible_ that there might be some way hook into the underlying
edit control class so that these sorts of things are handled, but I
don't know off the top of my head how that would be done. If the
textbox control were all managed, I think it would be simply a matter of
forwarding various input events (mouse, key, etc.) and subscribing to
the Paint event. But it's not, and so you'd have to do all that stuff
at a lower level, which is trickier from managed code.

Anyway, without further ado...

public partial class NoFlashMirror : Control
{
Control _ctl;
Bitmap _bmp;

public Control MirroredControl
{
get { return _ctl; }
set
{
if (_ctl != value)
{
if (_ctl != null)
{
_ctl.SizeChanged -= _SizeChangedHandler;
_ctl.LocationChanged -= _LocationChangedHandler;
}

_ctl = value;

if (_ctl != null)
{
_ctl.SizeChanged += _SizeChangedHandler;
_ctl.LocationChanged += _LocationChangedHandler;

_SizeChangedHandler(this, new EventArgs());
_LocationChangedHandler(this, new EventArgs());

}

UpdateMirror();
}
}
}

private void _SizeChangedHandler(object sender, EventArgs e)
{
Size = _ctl.Size;
}

private void _LocationChangedHandler(object sender, EventArgs e)
{
Location = _ctl.Location;
}

public void UpdateMirror()
{
if (_ctl != null)
{
if (_bmp == null ||
_bmp.Width != _ctl.Width ||
_bmp.Height != _ctl.Height)
{
_bmp = new Bitmap(_ctl.Width, _ctl.Height);
}

_ctl.DrawToBitmap(_bmp,
new Rectangle(new Point(), _bmp.Size));
}
else
{
_bmp = null;
}

Invalidate();
}

public NoFlashMirror()
{
InitializeComponent();

DoubleBuffered = true;
}

protected override void OnPaint(PaintEventArgs pe)
{
// Calling the base class OnPaint
base.OnPaint(pe);

if (_bmp != null)
{
pe.Graphics.DrawImage(_bmp, new Point());
}
}
}
Probably the easiest way to add edit/copy/paste etc would be to hide
the mirror control when the mouse passes over it. The user is then
interacting directly with the textbox it covered. Simply make it
visible again as soon as the user leaves the area.

Aug 17 '07 #11

P: n/a
DeveloperX wrote:
Probably the easiest way to add edit/copy/paste etc would be to hide
the mirror control when the mouse passes over it. The user is then
interacting directly with the textbox it covered. Simply make it
visible again as soon as the user leaves the area.
True. And for that matter, it may be that the user should not be
interacting with the control as long as it's being the target of the
input data stream, the updates for which are what cause the flashing in
the first place.

So, it may actually be suitable to do the above, except that instead of
keying off of the mouse move messages, just switch the mode when the
state of the input data stream is changed.

One hopes that one or some combination of these refinements are useful
to Ray. If not, I guess he's stuck rolling his own. :)

Pete
Aug 18 '07 #12

This discussion thread is closed

Replies have been disabled for this discussion.