Making parts of a panel transparent
-
I was having an idea of a program I want to make. I was just sitting and thinking about it and came up with an idea to a solution where I would use lots of small panels on top of large panel (canvas). The problem is that these small panels arent necessarily completely used. Maybe only parts of them are used. They can also be on top of each other. When they are on top of each other it wont look good if the top panel overdraws panels belowin parts that arent actually used. So is there a way to say that a part of a panel is supposed to be transparent so that things belo are shown instead of the empy panel?
-
I was having an idea of a program I want to make. I was just sitting and thinking about it and came up with an idea to a solution where I would use lots of small panels on top of large panel (canvas). The problem is that these small panels arent necessarily completely used. Maybe only parts of them are used. They can also be on top of each other. When they are on top of each other it wont look good if the top panel overdraws panels belowin parts that arent actually used. So is there a way to say that a part of a panel is supposed to be transparent so that things belo are shown instead of the empy panel?
Using multiple panels contained within another panel has another problem: unnecessary overhead. Each is a control with its own window handle, window procedure, and more - all consuming memory and ticking away at the number of possible window handles allocated by the OS. There are a couple ways you could do this. The easiest way is - for Windows 2000 and newer - to use layered Windows. This is what the
Opacity
property of aForm
uses. In this case, however, you'll need to code something yourself, which I'll get to in a moment. The other way is supported on any Windows platforms (perhaps other platforms, too) that supports the .NET Framework. You exclude a region of your control from painting. This is known as clipping. Anything below it is sent aWM_PAINT
message to redraw that portion of itself. This can be expensive (in terms of resources), which is why layered Windows are nice (but only supported on Windows 2000 and newer). The first way requires that you P/InvokeSetLayeredWindowAttributes
and - upon creation of your control (overrideOnHandleCreated
, and be sure to callbase.OnHandleCreated
) - you pass it theHandle
property, the color of your transparent color, andLWA_COLORKEY
(0x1) to tell the function to use your color, not an alpa value (which is what theForm.Opacity
property actually uses). Then you assign an image as yourBackgroundImage
for yourPanel
that contains the color you want to mask out. See the example below, where Example.jpg is just an image with white that I mask out:using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
class Example : Form
{
Example()
{
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.ResizeRedraw, true);
using (Image bg = Image.FromStream(
GetType().Assembly.GetManifestResourceStream("Example.jpg")))
{
BackgroundImage = (Image)bg.Clone();
Size = bg.Size;
}
FormBorderStyle = FormBorderStyle.None;
}
void SetTransparentColor(Color c)
{
int key = ColorTranslator.ToWin32(c);
SetLayeredWindowAttributes(Handle, key, 0, LWA_COLORKEY);
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams cp = base.CreateParams;
cp.ExStyle = WS_EX_L -
Using multiple panels contained within another panel has another problem: unnecessary overhead. Each is a control with its own window handle, window procedure, and more - all consuming memory and ticking away at the number of possible window handles allocated by the OS. There are a couple ways you could do this. The easiest way is - for Windows 2000 and newer - to use layered Windows. This is what the
Opacity
property of aForm
uses. In this case, however, you'll need to code something yourself, which I'll get to in a moment. The other way is supported on any Windows platforms (perhaps other platforms, too) that supports the .NET Framework. You exclude a region of your control from painting. This is known as clipping. Anything below it is sent aWM_PAINT
message to redraw that portion of itself. This can be expensive (in terms of resources), which is why layered Windows are nice (but only supported on Windows 2000 and newer). The first way requires that you P/InvokeSetLayeredWindowAttributes
and - upon creation of your control (overrideOnHandleCreated
, and be sure to callbase.OnHandleCreated
) - you pass it theHandle
property, the color of your transparent color, andLWA_COLORKEY
(0x1) to tell the function to use your color, not an alpa value (which is what theForm.Opacity
property actually uses). Then you assign an image as yourBackgroundImage
for yourPanel
that contains the color you want to mask out. See the example below, where Example.jpg is just an image with white that I mask out:using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
class Example : Form
{
Example()
{
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.ResizeRedraw, true);
using (Image bg = Image.FromStream(
GetType().Assembly.GetManifestResourceStream("Example.jpg")))
{
BackgroundImage = (Image)bg.Clone();
Size = bg.Size;
}
FormBorderStyle = FormBorderStyle.None;
}
void SetTransparentColor(Color c)
{
int key = ColorTranslator.ToWin32(c);
SetLayeredWindowAttributes(Handle, key, 0, LWA_COLORKEY);
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams cp = base.CreateParams;
cp.ExStyle = WS_EX_LWow! That is one long and very useful reply. Many thanks! So basically, both methods you mentioned above is better than the solution I proposed I guess from your reply. I dont want to limit the solution to just Win 2000 and above so I guess your second solution is the way I need to go. You mentioned that both my solution and your second solution was in many ways expensive. Is it correct to say that my proposed solution was expensive in terms of memory and window handle usage, and your second solution was expensive in terms of execution speed? One more question is that when I was playing with the code of your second solution I notices that if I removed the line t.Dock = DockStyle.Fill; or set the docking to None in Main the tringle isnt visible. Why is this? In your example the triangle fills the complete area, but I wanted a smaller triangle in the middle, so I thought that docking felt incorrect. Do you have to dock a control to make it visible in the main window or is there another way? One more thing I noticed is that when I run the code above, the triangle responds to clicks outside the triangle itself. If I understood you correctly, you said it would not respond to those clicks.
-
Wow! That is one long and very useful reply. Many thanks! So basically, both methods you mentioned above is better than the solution I proposed I guess from your reply. I dont want to limit the solution to just Win 2000 and above so I guess your second solution is the way I need to go. You mentioned that both my solution and your second solution was in many ways expensive. Is it correct to say that my proposed solution was expensive in terms of memory and window handle usage, and your second solution was expensive in terms of execution speed? One more question is that when I was playing with the code of your second solution I notices that if I removed the line t.Dock = DockStyle.Fill; or set the docking to None in Main the tringle isnt visible. Why is this? In your example the triangle fills the complete area, but I wanted a smaller triangle in the middle, so I thought that docking felt incorrect. Do you have to dock a control to make it visible in the main window or is there another way? One more thing I noticed is that when I run the code above, the triangle responds to clicks outside the triangle itself. If I understood you correctly, you said it would not respond to those clicks.
When I say "expensive" I mean in terms of resources, and not necessarily anything you can control. When you use clipping regions the windows underneath your control are sent a
WM_PAINT
message by the system and must draw themselves. When you start moving your application around like a crazy man there's a lot of painting going on. And many third-party controls, unfortunately, don't paint only what needs to be painted (or at least a suitable sub-region) but the whole control (this can lead to flickering or slow refresh times, even with double buffering). G.Ringbom wrote: or set the docking to None in Main the tringle isnt visible. Why is this? Because I set no default size for the control when I extended it. Give the control a set size (something I didn't do since I was docking it anyway) and you should see the control. G.Ringbom wrote: One more thing I noticed is that when I run the code above, the triangle responds to clicks outside the triangle itself. If I understood you correctly, you said it would not respond to those clicks. Either I wrote it wrong (sorry) or you mis-interpreted. With clipping regions the mouse messages are still sent to the control because the control (a window, actually) still occupies a rectangular region. When you set the clipping region for aGraphics
object, however, it only defines the region not to paint. The control is still there. If I said otherwise I do apologize (the site is running way too slow right now for me to want to go back and check). Layered windows can produce non-rectangular windows, however. Mouse messages are allowed to pass through to the underlying window. I think your best approach is to use both. Use clipping regions when the current platform is not Win2K or newer, and layered windows when the current platform is Win2K or newer. You can determine the OS easily by usingEnvironment.OSVersion
. If you search for previous comments, I believe I posted an example once using this approach - at least for part of the code necessary for such a control. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog] -
When I say "expensive" I mean in terms of resources, and not necessarily anything you can control. When you use clipping regions the windows underneath your control are sent a
WM_PAINT
message by the system and must draw themselves. When you start moving your application around like a crazy man there's a lot of painting going on. And many third-party controls, unfortunately, don't paint only what needs to be painted (or at least a suitable sub-region) but the whole control (this can lead to flickering or slow refresh times, even with double buffering). G.Ringbom wrote: or set the docking to None in Main the tringle isnt visible. Why is this? Because I set no default size for the control when I extended it. Give the control a set size (something I didn't do since I was docking it anyway) and you should see the control. G.Ringbom wrote: One more thing I noticed is that when I run the code above, the triangle responds to clicks outside the triangle itself. If I understood you correctly, you said it would not respond to those clicks. Either I wrote it wrong (sorry) or you mis-interpreted. With clipping regions the mouse messages are still sent to the control because the control (a window, actually) still occupies a rectangular region. When you set the clipping region for aGraphics
object, however, it only defines the region not to paint. The control is still there. If I said otherwise I do apologize (the site is running way too slow right now for me to want to go back and check). Layered windows can produce non-rectangular windows, however. Mouse messages are allowed to pass through to the underlying window. I think your best approach is to use both. Use clipping regions when the current platform is not Win2K or newer, and layered windows when the current platform is Win2K or newer. You can determine the OS easily by usingEnvironment.OSVersion
. If you search for previous comments, I believe I posted an example once using this approach - at least for part of the code necessary for such a control. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog]