I'm developing an exe where I need to have a transparent background. I have made the image in Photoshop and it has all the neat stuff (shadows/opacity, reflection etc).
I have been struggling to get it working using TransparentColor+BackColor+Background Image, but I always end up with some pixel not being transparent. So I switched to UpdateLayeredWindow which works fine, but no control is being drawn now.
Here is some of my code
private void Form1_Load(object sender, EventArgs e)
{
UpdateFormDisplay(this.BackgroundImage);
}
protected override void OnPaint(PaintEventArgs e)
{
UpdateFormDisplay(this.BackgroundImage);
}
public void UpdateFormDisplay(Image backgroundImage)
{
IntPtr screenDc = API.GetDC(IntPtr.Zero);
IntPtr memDc = API.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
//Display-image
Bitmap bmp = new Bitmap(backgroundImage);
hBitmap = bmp.GetHbitmap(Color.FromArgb(0)); //Set the fact that background is transparent
oldBitmap = API.SelectObject(memDc, hBitmap);
//Display-rectangle
Size size = bmp.Size;
Point pointSource = new Point(0, 0);
Point topPos = new Point(this.Left, this.Top);
//Set up blending options
API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
blend.BlendOp = API.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = API.AC_SRC_ALPHA;
API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, API.ULW_ALPHA);
//Clean-up
bmp.Dispose();
API.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
API.SelectObject(memDc, oldBitmap);
API.DeleteObject(hBitmap);
}
API.DeleteDC(memDc);
}
catch (Exception)
{
}
}
Here are some images to explain better
enter image description here
enter image description here
1 Answer 1
If you want to use regular control inside layered window that uses UpdateLayeredWindow API, you need to override control's OnPaint method to redirect drawing to off-screen bitmap which you later use with UpdateLayeredWindow method to update window look.
If you don't want to dig into controls code, or don't have much custom made controls, WM_PRINT message could be used to force controls to paint themselves into provided device context. Classic window subclassing (SetWindowLong/GetWindowLong) for catching moments when controls invalidate themselves is useful, but may be slightly dangerous - you have to watch for callback chain.
Finally, you can use lightweight (windowless) controls. They use form message queue to receive events and draw themselves, so only form drawing code modifications are necessary. Some of standard winforms controls support this mode.
base.OnPaint(e);from within your overrided OnPaint.UpdateFormDisplayin theOnPaintBackgroundinstead? BTW, you shouldn't try customizingwinformstoo much, if you want a better solution, try usingWPF.