Partial transparency with C# .NET 3.5 WinForms?

2019-01-27 00:48发布

I'm making a .NET 3.5 app with a form that draws a partially transparent black background. I'm overriding OnPaintBackground to accomplish this:

protected override void OnPaintBackground( PaintEventArgs e )
{
    using ( Brush brush = new SolidBrush( Color.FromArgb( 155, Color.Black ) ) )
    {
        e.Graphics.FillRectangle( brush, e.ClipRectangle );
    } 
}

It works, but occasionally the form draws over itself without clearing the screen, making the transparency darker than it should be. I've tried playing with Graphics.Flush() and Graphics.Clear(), but it either doesn't help or completely removes transparency. Any suggestions?

Edit: Here's what it looks like, after starting the app on the left, and after the form redraws itself a few times (in response to tabbing from one control to another) on the right:

Transparency Issue http://www.quicksnapper.com/files/5085/17725729384A10347269148_m.png

Edit 2: I was trying a few things out this morning and noticed that when the desktop behind the transparent portions change, it's not actually being redrawn. For example, if I open Task Manager and put it behind the window, you don't see it refreshing itself. This makes sense with what I've been seeing with the transparency levels. Is there a function to make Windows redraw the area behind your window?

Edit 3: I've tried changing a few properties on the form, but they all result in the form drawing non-transparent black:

this.AllowTransparency = true;
this.DoubleBuffered = true;
this.Opacity = .99;

I'm going to try creating a separate window for the transparent portion as overslacked mentioned, but any other ideas are still welcome.

2条回答
虎瘦雄心在
2楼-- · 2019-01-27 01:06

Is Graphics.CompositingMode set to CompositingMode.SourceCopy? That should cause painting the background twice to be equivalent to painting it once, since it will replace the existing alpha/color data instead of compositing over it.

查看更多
Summer. ? 凉城
3楼-- · 2019-01-27 01:16

I think I would call this expected behavior, actually. What I would do is render my background to an in-memory bitmap and, in the paint event, copy that to the form (basic double-buffering).

If I'm way off base, could you post a screenshot? I don't know that I'm imagining what you're describing correctly.

EDIT:

I'm wondering about your use of OnPaintBackground... pre-.NET, if you were doing double-buffering you'd catch and ignore the WM_ERASKBKGND message (to prevent flicker), render your image to an offscreen buffer, and copy from the buffer to the screen on WM_PAINT. So, try changing from the OnPaintBackground to OnPaint.

I haven't done too much of this kind of thing in .NET, but I had a pretty good handle on it before; I just don't know if it'll translate well or not!

EDIT 2:

Marc, the more I think about what you're trying to do, the more problems appear. I was going to suggest creating a background thread dedicated to capturing the screen and rendering it darkened; however, in order to remove your own form you'd have to set the visibility to false which would create other problems....

If you're unwilling to give up, I would suggest creating two windows and "binding" them together. Create a semi-opaque window (by setting opacity) for your background window, and create a second "normal" window for the foreground. Use SetWindowRgn on the foreground window to cut away the background and position them on top of each other.

Good luck!

查看更多
登录 后发表回答