Howto develop independant layers

E

eric

Hello,

I am realizing an application containing a display with two layers.
The first layer is the background layer
and it contains an image refreshed very often (more than once a
second) while the second layer is the transparent
foreground layer. I can draw some geometrical forms on it using
Grahics.Draw(...) methods.
My problem is the following, each time I refresh the image on the
first layer (with layer.BackgroundImage), the
drawing done on the transparent layer disappear.
Is there a solution to have independent refreshing layer ?

Here is my code :

*******
public partial class Form1 : Form
{
private int index = 0;

public Form1()
{
InitializeComponent();
layerImage.Parent = panel1; //layerImage is a classical
panel
layerDrawing.Parent = panel1; //layerDrawing is a
transpanel
layerDrawing.BringToFront(); //Bring it to the foreground
this.timer1.Start();
}

private void checkBox1_CheckedChanged(object sender, EventArgs
e)
{
if (checkBox1.Checked)
{
Graphics g = layerDrawing.CreateGraphics();
g.DrawLine(new Pen(Color.Red), layerDrawing.Location,
new Point(layerDrawing.Location.X + layerDrawing.Width,
layerDrawing.Location.Y + layerDrawing.Height));
g.DrawLine(new Pen(Color.Red), new
Point(layerDrawing.Location.X, layerDrawing.Location.Y +
layerDrawing.Height), new Point(layerDrawing.Location.X +
layerDrawing.Width, layerDrawing.Location.Y));
}
else
layerDrawing.Invalidate();
}

private void timer1_Tick(object sender, EventArgs e)
{
if (index == 4)
index = 0;
layerImage.BackgroundImage = new
Bitmap(Environment.CurrentDirectory + @"\..\..\image\Image" + index++
+ ".jpg");
}
}
*********
*********
public class TransPanel : Panel
{

public TransPanel()
{

}

protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}

protected override void OnPaintBackground(PaintEventArgs
pevent)
{
//do not allow the background to be painted
}
}
*********
 
K

kndg

Hi Eric,

The reason the drawings on the transparent layer disappear is because
when each time your image layer refresh, it cause every control on top
of it to refresh also. So if you need to retain the drawing on your
transparent layer, you have to handle the OnPaint() method on your
transparent layer. My modifications would roughly as below,

Form1.cs (modify the checkBox1_CheckedChanged event handler)

private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
layerDrawing.DrawSomeGraphic();
}
else
layerDrawing.Invalidate();
}


TransPanel.cs (add below three methods)

public void DrawSomeGraphic()
{
Graphics g = CreateGraphics();

DrawCross(g);
}

private void DrawCross(Graphics g)
{
g.DrawLine(new Pen(Color.Blue), Location, new Point(Location.X +
Width, Location.Y + Height));
g.DrawLine(new Pen(Color.Red), new Point(Location.X, Location.Y +
Height), new Point(Location.X + Width, Location.Y));
}

protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;

DrawCross(g);
}

Regards.
 
E

eric

Hi,

Thanks for your help, I tried your method but it doesn't seem to work.
In a debug mode, I noticed that I don't go into layerDrawing.OnPaint
method when the background image of layerImage change.
Also, I do not like the idea of doing my drawing each time the
background image changed because it will change very often and I guess
my drawing will flicker.
Isn't there a way to let the foreground unchanged while the background
oftenly change ?
 
P

Peter Duniho

eric said:
Hi,

Thanks for your help, I tried your method but it doesn't seem to work.
In a debug mode, I noticed that I don't go into layerDrawing.OnPaint
method when the background image of layerImage change.
Also, I do not like the idea of doing my drawing each time the
background image changed because it will change very often and I guess
my drawing will flicker.
Isn't there a way to let the foreground unchanged while the background
oftenly change ?

No. That's just not how screen updating in Forms works (or in pretty
much any GUI system, for that matter).

It's up to your own application to maintain a current state of what it
needs in order to "present" whatever visuals you want, whenever the OS
asks your application to do so (which it does by causing the Paint event
to be raised). The OS never keeps any history of what you've drawn;
your application always has to be able to redraw everything fresh.

Also note that the Forms API doesn't do transparency very well. In
general, when you set a control to be "transparent", all that really
happens is that it paints its background the same color as its parent.

Based on your description, it sounds to me as though your best bet is to
handle all of the drawing inside a single custom Control subclass, in
which you've overridden the OnPaint() and OnPaintBackground() methods
(*), have set the control style to UserPaint | AllPaintingInWmPaint |
DoubleBuffer (see Control.SetStyle()), and then do all of your drawing
in the OnPaint() method as necessary (i.e. always draw the background
first, then the foreground).

(*) In theory, you should not have to override OnPaintBackground();
setting the AllPaintingInWmPaint style bit should prevent the
OnPaintBackground() method from being called. However, I have found
that in practice, for some reason this doesn't work right. So you need
to override OnPaintBackground() and provide an empty method body (yes,
this means that subscribers to the PaintBackground event won't get
called…but that's what should happen when you set AllPaintingInWmPaint
anyway).

I have found that applying the above technique results in good visual
results. Depending on what you're actually drawing, additional work may
be necessary in order to achieve good performance (e.g. caching scaled
bitmaps you might be using, or even maintaining off-screen bitmaps
representing your foreground and background and then in the OnPaint()
method just drawing each bitmap in order).

Finally, all of the above said: if you expect to be doing a lot of
compositing and transparency-based drawing, you may find that the Forms
API isn't the best choice. It's a relatively old API, based on pre-.NET
concepts from when true transparency and image composing was
computationally impractical and generally not done. It may be that you
would find WPF to be a better match for your needs. It's very different
from the Forms paradigm, and will take some getting used to; but it
provides a much richer feature set in terms of the GUI rendering.

Pete
 
E

eric

No.  That's just not how screen updating in Forms works (or in pretty
much any GUI system, for that matter).

It's up to your own application to maintain a current state of what it
needs in order to "present" whatever visuals you want, whenever the OS
asks your application to do so (which it does by causing the Paint event
to be raised).  The OS never keeps any history of what you've drawn;
your application always has to be able to redraw everything fresh.

Also note that the Forms API doesn't do transparency very well.  In
general, when you set a control to be "transparent", all that really
happens is that it paints its background the same color as its parent.

Based on your description, it sounds to me as though your best bet is to
handle all of the drawing inside a single custom Control subclass, in
which you've overridden the OnPaint() and OnPaintBackground() methods
(*), have set the control style to UserPaint | AllPaintingInWmPaint |
DoubleBuffer (see Control.SetStyle()), and then do all of your drawing
in the OnPaint() method as necessary (i.e. always draw the background
first, then the foreground).

(*) In theory, you should not have to override OnPaintBackground();
setting the AllPaintingInWmPaint style bit should prevent the
OnPaintBackground() method from being called.  However, I have found
that in practice, for some reason this doesn't work right.  So you need
to override OnPaintBackground() and provide an empty method body (yes,
this means that subscribers to the PaintBackground event won't get
called but that's what should happen when you set AllPaintingInWmPaint
anyway).

I have found that applying the above technique results in good visual
results.  Depending on what you're actually drawing, additional work may
be necessary in order to achieve good performance (e.g. caching scaled
bitmaps you might be using, or even maintaining off-screen bitmaps
representing your foreground and background and then in the OnPaint()
method just drawing each bitmap in order).

Finally, all of the above said: if you expect to be doing a lot of
compositing and transparency-based drawing, you may find that the Forms
API isn't the best choice.  It's a relatively old API, based on pre-.NET
concepts from when true transparency and image composing was
computationally impractical and generally not done.  It may be that you
would find WPF to be a better match for your needs.  It's very different
from the Forms paradigm, and will take some getting used to; but it
provides a much richer feature set in terms of the GUI rendering.

Pete

Thanks Pete,

Your explanation is very clear and is helping me a lot. Thanks to you
and Bob Powell website I just found (http://www.bobpowell.net), I
think I'll succeed in doing my application. IOne day, I'll take a look
at WPF but forms should be enough since I just have some basic shapes
to draw.
 

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