如何使控件背景色支持TransparentKey(at Win2k/WinXP 32bit Color Desktop)

问题:
    在Window 2000和WindowsXP 32bit Color桌面下,自己写了Control,放到DoubleBuffered为True的Form上设置背景颜色(BackColor)为Form的TransparentKey相同的颜色后,发现没有透下去,下面是Demo代码:

控件代码:
 1    public class MyControl : Control
 2    {
 3        protected override void OnPaint(PaintEventArgs e)
 4        {
 5            // Drawing border
 6            if (Application.RenderWithVisualStyles)
 7            {
 8                new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Hot).DrawEdge(e.Graphics,
 9                    this.ClientRectangle,
10                    Edges.Left | Edges.Top | Edges.Right | Edges.Bottom,
11                    EdgeStyle.Raised,
12                    EdgeEffects.Mono);
13            }

14            else
15            {
16                ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle);
17            }

18
19            base.OnPaint(e);
20
21            // Drawing demo string/
22            using (StringFormat sf = new StringFormat())
23            {
24                sf.Alignment = StringAlignment.Center;
25                e.Graphics.DrawString("This is Test Control"this.Font, Brushes.Black, this.ClientRectangle, sf);
26            }

27        }

28
29        protected override void OnPaintBackground(PaintEventArgs pevent)
30        {
31            base.OnPaintBackground(pevent);
32
33            //Here I want fill some shape at Background, for example fill ellipse at half-top client.
34            using (Brush brush = new SolidBrush(Color.Red))
35            {
36                pevent.Graphics.FillEllipse(brush, new Rectangle(00this.Width, this.Height / 2));
37            }

38        }

39    }
下图是在Window XP 32bit Color桌面下的运行效果:

显然这里没有透下去,但是.NET自带Control都可以透下去!

找了很久也没有找到问题所在,只好祭出Reflector,看MS的内部实现了,从
OnPaintBackground(PaintEventArgs pevent)->
PaintBackground(PaintEventArgs e, Rectangle rectangle)->
PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor, Point scrollOffset)->
PaintBackColor(PaintEventArgs e, Rectangle rectangle, Color backColor):
 1private static void PaintBackColor(PaintEventArgs e, Rectangle rectangle, Color backColor) {
 2            // Common case of just painting the background.  For this, we 
 3            // use GDI because it is faster for simple things than creating
 4            // a graphics object, brush, etc.  Also, we may be able to
 5            // use a system brush, avoiding the brush create altogether.
 6            // 
 7            Color color = backColor;
 8 
 9            // we use GDI to paint in most cases using WindowsGraphics 
10            bool painted = false;
11            if (color.A == 255
12                using( WindowsGraphics wg = WindowsGraphics.FromGraphics( e.Graphics ) ) {
13                    color = wg.GetNearestColor(color);
14
15                    using (WindowsBrush brush = new WindowsSolidBrush(wg.DeviceContext, color))  
16                        wg.FillRectangle(brush, rectangle);
17                    }
 
18                }
 
19
20                painted = true
21                /*
22                if (DisplayInformation.BitsPerPixel > 8) {
23                    NativeMethods.RECT r = new NativeMethods.RECT(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
24                    SafeNativeMethods.FillRect(new HandleRef(e, e.HDC), ref r, new HandleRef(this, BackColorBrush)); 
25                    painted = true;
26                }*/
 
27            }
 
28
29            if (!painted) 
30                // don't paint anything from 100% transparent background
31                //
32                if (color.A > 0{
33                    // Color has some transparency or we have no HDC, so we must 
34                    // fall back to using GDI+.
35                    // 
36                    using (Brush brush = new SolidBrush(color)) 
37                        e.Graphics.FillRectangle(brush, rectangle);
38                    }
 
39                }

40            }

41        }


发现MS Code对这里的注释,只说这里使用GDI画的,在12行,这里使用的WindowsGraphics就是对GDI的封装。
所以要想实现和MS Control一样,任何环境都支持TransparentKey,这里的背景只能使用GDI来画,修改MyControl代码如下就可以了:

 1    public class MyControl : Control
 2    {
 3        protected override void OnPaint(PaintEventArgs e)
 4        {
 5            // Drawing border
 6            if (Application.RenderWithVisualStyles)
 7            {
 8                new VisualStyleRenderer(VisualStyleElement.TextBox.TextEdit.Hot).DrawEdge(e.Graphics,
 9                    this.ClientRectangle,
10                    Edges.Left | Edges.Top | Edges.Right | Edges.Bottom,
11                    EdgeStyle.Raised,
12                    EdgeEffects.Mono);
13            }

14            else
15            {
16                ControlPaint.DrawBorder3D(e.Graphics, this.ClientRectangle);
17            }

18
19            base.OnPaint(e);
20
21            // Drawing demo string/
22            using (StringFormat sf = new StringFormat())
23            {
24                sf.Alignment = StringAlignment.Center;
25                e.Graphics.DrawString("This is Test Control"this.Font, Brushes.Black, this.ClientRectangle, sf);
26            }

27        }

28
29        protected override void OnPaintBackground(PaintEventArgs pevent)
30        {
31            base.OnPaintBackground(pevent);
32
33            //Here I want fill some shape at Background, for example fill ellipse at half-top client.
34            //using (Brush brush = new SolidBrush(Color.Red))
35            //{
36            //    pevent.Graphics.FillEllipse(brush, new Rectangle(0, 0, this.Width, this.Height / 2));
37            //}
38            IntPtr hdc = IntPtr.Zero;
39            try
40            {
41                hdc = pevent.Graphics.GetHdc();
42                if (hdc != IntPtr.Zero)
43                {
44                    IntPtr hBrush = CreateSolidBrush(ColorTranslator.ToWin32(Color.Red));
45                    if (hBrush != IntPtr.Zero)
46                    {
47                        IntPtr oldBrush = SelectObject(hdc, hBrush);
48                        Ellipse(hdc, 00this.Right, this.Height / 2);
49                        SelectObject(hdc, oldBrush);
50
51                        DeleteObject(hBrush);
52                        hBrush = IntPtr.Zero;
53                    }

54                }

55            }

56            finally
57            {
58                if (hdc != IntPtr.Zero)
59                {
60                    pevent.Graphics.ReleaseHdc(hdc);
61                }

62            }

63        }

64
65        [DllImport("gdi32.dll")]
66        [return: MarshalAs(UnmanagedType.Bool)]
67        private static extern bool Ellipse(IntPtr hdc, int leftRect, int topRect, int rightRect, int bottomRect);
68
69        [DllImport("gdi32.dll")]
70        private static extern IntPtr SelectObject(IntPtr hDC, IntPtr gdiObject);
71
72        [DllImport("gdi32.dll")]
73        private static extern IntPtr CreateSolidBrush(int color);
74
75        [DllImport("gdi32.dll")]
76        [return: MarshalAs(UnmanagedType.Bool)]
77        private static extern bool DeleteObject(IntPtr gdiObject);
78    }
posted @ 2007-12-26 11:01  winkingzhang  阅读(1139)  评论(0编辑  收藏  举报