如何使控件背景色支持TransparentKey(at Win2k/WinXP 32bit Color Desktop)
问题:
在Window 2000和WindowsXP 32bit Color桌面下,自己写了Control,放到DoubleBuffered为True的Form上设置背景颜色(BackColor)为Form的TransparentKey相同的颜色后,发现没有透下去,下面是Demo代码:
控件代码:
![]()
显然这里没有透下去,但是.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):
在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桌面下的运行效果:
public class MyControl : Control2
{3
protected override void OnPaint(PaintEventArgs e)4
{5
// Drawing border6
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
else15
{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):
1
private 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
}
private 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 creating4
// a graphics object, brush, etc. Also, we may be able to5
// 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 background31
//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
}
public class MyControl : Control2
{3
protected override void OnPaint(PaintEventArgs e)4
{5
// Drawing border6
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
else15
{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
try40
{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
finally57
{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
}To be the apostrophe which changed “Impossible” into “I’m possible”
----------------------------------------------------
WinkingZhang's Blog (http://winkingzhang.cnblogs.com)
GCDN(http://gcdn.grapecity.com/cs)

浙公网安备 33010602011771号