在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(0, 0, this.Width, this.Height / 2));
37 }
38 }
39 }
下图是在Window XP 32bit Color桌面下的运行效果: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 }
39 }
显然这里没有透下去,但是.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 }
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, 0, 0, this.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 }
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, 0, 0, this.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 }