测试环境: Win7 + XE2
在开发界面控件中,想加个背景,做的漂亮点。当然会考虑主界面加背景,其他控件设置透明就能所有控件都能正常显示背景。
自己的控件只要在Create时设置,使用背景和设置透明就OK了
ControlStyle := ControlStyle + [csParentBackground] - [csOpaque];
就能正常实现透明化。为了方便观察,把TForm的背景设置为clSkyBlue。
下面是个简单的不透明控件是显示的状态(背景clSkyBlue),TCustomControl,默认会打开ParentColor,所以颜色和Form的相同
(图1) 未设置透明属性的情况的图片,
(图2) 增加 csParentBackground 去除 csOpaque 就看不到刚才那块clSkyBlue。具体可以看代码。
1 //MMWIN:CLASSCOPY 2 unit _MM_Copy_Buffer_; 3 4 interface 5 6 type 7 TmtTest = class(TCustomControl) 8 public 9 constructor Create(AOwner: TComponent); override; 10 end; 11 12 13 implementation 14 15 { TmtTest } 16 constructor TmtTest.Create(AOwner: TComponent); 17 begin 18 inherited; 19 Width := 100; 20 Height := 60; 21 ControlStyle := ControlStyle + [csParentBackground] - [csOpaque]; 22 end; 23 24 end.
当然做控件肯定会遇到屏幕刷新闪烁问题,设置 DoubleBuffered := true; 是很容易想到的一个方法。出状况了,如果你打开Form的双缓冲技术。你会发现上面原来能透明的控件无效了。变成和Form相同颜色和(图1)相同。郁闷啊
上面这张图上当打开TForm的双缓冲技术后,出现的结果。标准的TToolbar 和 TMyToolbar 都出现背景为控件的背景,不是Parent的背景。
在网上查了一些,做背景透明的有不少文章,好像都是一样的。
1.在Create中设定ControlStyle :=ControlStyle - [csOpaque];
2. override 它的CreateParams方法, exstyle 里加上WS_EX_TRANSPARENT.
3. 修改它的parent的window style, 去掉WS_CLIPCHILDREN.4、截获WM_ERASEBKGND, 什么都不做直接返回1.(不搽除背景)
5、大绝招自己画
在用完上面三板斧后,在DoubleBuffered 开启的情况下依然如旧。上面第四部用颜色标出的是,在使用此招时就乱套了,界面面目全非。
鼠标滑过狼藉遍地,由于不擦除背景导致未被处理的背景一并被画出来。
当然了,他还说最终大绝招~~ (=@__@=)! 自己绘制。
还是大绝招好用,不用设置任何东西直接O.K.了。
实现方法:直接拷贝Parent对象的背景
IntersectClipRect 这个函数就能解决问题
1 //MMWIN:CLASSCOPY 2 unit _MM_Copy_Buffer_; 3 4 interface 5 6 type 7 TmtTest = class(TCustomControl) 8 protected 9 procedure Paint; override; 10 public 11 constructor Create(AOwner: TComponent); override; 12 end; 13 14 15 implementation 16 17 18 { TmtTest } 19 20 constructor TmtTest.Create(AOwner: TComponent); 21 begin 22 inherited; 23 Width := 100; 24 Height := 60; 25 end; 26 27 procedure TmtTest.Paint; 28 var 29 SaveIdx: Integer; 30 DC: HDC; 31 p: TPoint; 32 begin 33 inherited; 34 if Parent = nil then 35 Exit; 36 DC := Canvas.Handle; 37 SaveIdx := SaveDC(DC); 38 GetViewportOrgEx(DC, p); 39 SetViewportOrgEx(DC, p.x - Left, p.y - Top, nil); 40 IntersectClipRect(DC, 0, 0, Parent.ClientWidth, Parent.ClientHeight); 41 Parent.Perform(WM_ERASEBKGND, DC, 0); 42 Parent.Perform(WM_PAINT, DC, 0); 43 RestoreDC(DC, SaveIdx); 44 end; 45 46 end.
另外VCL的内部控制机理还是不是很清楚,只能用时间来处理这个问题。有更好更方便的方法希望能找到。