在许多程序中,窗口可以被拖放到另一个窗口中,并溶合为一体,例如c++ builder中的Class Explorer与其它窗口就是典型一例,在c++ builder中,这种特性被称为\"窗口吸附\"。
在bcb中的许多组件都具有与\"吸附\"功能相关的属性,例如:DockSite属性、UseDockManage属性,以及DragKing属性,它的属性可以设置为dkDock。如果将这些属性值简单设置一下,窗口将自动具有吸附功能,不过这样的吸附功能非常死板,没有实用价值。下面笔者就给大家介绍一下如何在c++ builder中实现真正的窗口吸附效果。
一、设计一个主窗口
新建一工程,主窗体命名为MainForm,单元文件命名为Main。添加一个TMainMenu命名为MainMenu1,双击该主菜单添加两个子菜单分别为\"红色窗口\"和\"蓝色窗口\",这两个子菜单分别用来显示不同颜色的窗口。接着在窗口的最右端添加一个宽度为0,高度为整个窗口高的Panel组件并命名为DockPanel,建立Panel组件是因为被\"吸附\"的窗口必须要有一个\"吸附区\",这种吸附区可以是窗口,也可以是窗口化组件,如Panel。再在Panel右边添加一个Splliter组件,命名为VSplliter宽度设为4,高度为窗体的高度。建立Splliter组件是为了有效地建立被吸附窗口的间隔区,并且可以方便用户调整被吸附窗口所占据的宽度。(如图1所示)
二、设计\"被吸附\"窗口
作为主窗口的设计窗体,除了一般的属性设置外,有关Dock的属性均无需修改,只要将\"被吸附\"窗体的属性进行修改就可以了。
选择\"File | New Form\"命令,建立一个新窗体,将新窗体命令为DockWindow和吸附功能相关的属性修改如下:
属性 值
DockSite True
DockKind dkDock
DockMode dmAutomatic
在窗体中放置一个Memo组件,并将其Align属性修改为alClient,使其充满整个窗口区域。放置Memo组件的目的是使窗口在被吸附时有明显的边界特征,并且可以赋予窗口不同颜色。
三、设计实现\"吸附\"功能
实际上,产生吸附作用的并不是主窗体,而是主窗体中的Panel组件,所以Panel组件中与吸附功能有关的组件也必须和吸附窗口一样进行修改,修改值如上表所示。接着为Panel添加如下事件:OnDockOver、OnGetSiteinfo、OnDockDrop、OnUndock代码与解释如下:
//--------------------------------------------------------------------------------------
void __fastcall TMainWin::DockPanelDockOver(TObject *Sender,TDragDockObject *Source, int X, int Y, TDragState State,bool &Accept)
{
Accept = (dynamic_cast<TDockableForm*>(Source->Control) != NULL); // (1)
if (Accept) // (2)
{
Windows::TPoint TopLeft = DockPanel->ClientToScreen(Point(0, 0)); // (3)
Windows::TPoint BottomRight = DockPanel->ClientToScreen(Point(this->ClientWidth / 3, DockPanel->Height)); // (4)
Source->DockRect = Windows::TRect(TopLeft, BottomRight); (5)
}
}
//--------------------------------------------------------------------------------------
解释:OnDockOver事件是在被吸附窗口拖动经过吸附面板时产生的,也就是说,当用户拖动被吸附窗口经过DockPanel组件时,就会产生这个事件。第(1)句作用是首先将句柄接受的参数Source->Control(代表吸附窗口)强制转换为TDockableForm类型(可吸附窗体);然后判断强制转换是否成功或者Source中是否包含可吸附对象;最后将判断结果保存到Accept变量中。第(2)句的作用是如果上述转换成功后,才可设置吸附区域的虚框。第(3)~(4)句用来设置要显示的吸附区域,其中使用了Panel组件的Client To Screen方法,该方法可将面板从标系下的点转换为目前屏幕坐标系中的点。(5)句是将上面设置的区域指定给被吸附的对象。
//--------------------------------------------------------------------------------------
void __fastcall TMainWin::DockPanelGetSiteInfo(TObject *Sender,TControl *DockClient, TRect &InfluenceRect, TPoint &MousePos,bool &CanDock)
{
CanDock = (dynamic_cast<TDockableForm*>(DockClient) != NULL);
}
//---------------------------------------------------------------------------
解释:当一个吸附组件的DockSite属性为True时,OnGetSiteInfo事件将在OnDockDrop事件之前产生,其中的代码可以用被吸附对象进行一些初始化。
//--------------------------------------------------------------------------------------
void __fastcall TMainWin::DockPanelDockDrop(TObject *Sender,TDragDockObject *Source, int X, int Y)
{
TPanel* SenderPanel = dynamic_cast<TPanel*>(Sender);//将调用该方法的对象强制转换为TPanel类型组件
if (SenderPanel == NULL) //判断上句的转换是否成功,若不成功则给出一个提示
throw EInvalidCast(\"\");
if (SenderPanel->DockClientCount == 1)//判断是否包含被吸附窗口,如果存在,那么调用ShowPanel重新显示Panel组件
ShowPanel(SenderPanel, true, NULL);
SenderPanel->DockManager->ResetBounds(true);//重新画被吸附窗口
}
//---------------------------------------------------------------------------
解释:OnDockDrop事件是在被吸附窗口吸附到面板上之后产生的事件,吸附之后,程序应该调用后面定义的ShowPanel函数,使Panel组件按照新的大小显示出来(包含被吸附窗口),然后利用它的DockManager重画被吸附的窗口。
//--------------------------------------------------------------------------------------