所有者绘制的本机“选项卡控件”的选项卡(TPageControl
在 VCL 中,尽管它的后缀被恰当地命名TCustomTabControl
- 任何人都猜测为什么创意命名..),预计在处理时由其父控件绘制WM_DRAWITEM
消息,如记录在这里 http://msdn.microsoft.com/en-us/library/windows/desktop/bb760550%28v=vs.85%29.aspx#owner_drawn_tabs.
VCL 通过将消息突变为CN_DRAWITEM
消息并将其发送到控件本身。在此过程中VCL没有进一步干预。它只是调用OnDrawTab
消息处理程序(如果它是由用户代码分配的),并传递适当的参数。
因此,绘制选项卡边框的不是 VCL,而是操作系统本身。另外,显然,它在处理过程中不会这样做WM_DRAWITEM
消息,但稍后在绘画过程中。您可以通过输入空来验证这一点WM_DRAWITEM
页面控件父级上的处理程序。结果是,无论我们在事件处理程序中绘制什么,它稍后都会由操作系统获得边框。
我们可能会尝试阻止操作系统绘制的内容生效,毕竟我们有设备上下文(如 Canvas.Handle)。不幸的是,这条路线也是一个死胡同,因为 VCL 在事件处理程序返回后恢复设备上下文的状态。
那么,我们唯一的办法就是完全放弃处理OnDrawTab
事件,并采取行动CN_DRAWITEM
信息。下面的示例代码使用插入器类,但您可以按照自己喜欢的方式对控件进行子类化。确保OwnerDrawn
is set.
type
TPageControl = class(comctrls.TPageControl)
protected
procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
TForm1 = class(TForm)
..
..
procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
Color: TColor;
Rect: TRect;
Rgn: HRGN;
begin
Color := 0;
// draw in different colors so we see where we've drawn
case Message.DrawItemStruct.itemID of
0: Color := $D0C0BF;
1: Color := $D0C0DF;
2: Color := $D0C0FF;
end;
SetDCBrushColor(Message.DrawItemStruct.hDC, Color);
// we don't want to get clipped in the passed rectangle
SelectClipRgn(Message.DrawItemStruct.hDC, 0);
// magic numbers corresponding to where the OS draw the borders
Rect := Message.DrawItemStruct.rcItem;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
Inc(Rect.Left, 2);
// Inc(Rect.Top, 1);
Dec(Rect.Right, 2);
Dec(Rect.Bottom, 3);
end else begin
Dec(Rect.Left, 2);
Dec(Rect.Top, 2);
Inc(Rect.Right, 2);
Inc(Rect.Bottom);
end;
FillRect(Message.DrawItemStruct.hDC, Rect,
GetStockObject(DC_BRUSH));
// just some indication for the active tab
SetROP2(Message.DrawItemStruct.hDC, R2_NOTXORPEN);
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then
Ellipse(Message.DrawItemStruct.hDC, Rect.Left + 4, Rect.Top + 4,
Rect.Left + 12, Rect.Top + 12);
// we want to clip the DC so that the borders to be drawn are out of region
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
DeleteObject(Rgn);
Message.Result := 1;
inherited;
end;
Here is how the above looks here: