分享
 
 
 

用c++ builder 创建具有吸附效果的窗口

王朝c/c++·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

在许多程序中,窗口可以被拖放到另一个窗口中,并溶合为一体,例如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重画被吸附的窗口。

//--------------------------------------------------------------------------------------

void __fastcall TMainWin::DockPanelUnDock(TObject *Sender,TControl *Client, TWinControl *NewTarget, bool &Allow)

{

TPanel* SenderPanel = dynamic_cast<TPanel*>(Sender);

if (SenderPanel == NULL)

throw EInvalidCast("");

if (SenderPanel->DockClientCount == 1) // 确保当前吸附面板中包含了被吸附对象,然后调用ShowPanel方法"解放"它

ShowPanel(SenderPanel, false, NULL);

}

//---------------------------------------------------------------------------

OnUndock事件是在吸附对象被"解放"之前被调用的,所以这里可以为用户提供一个不"解放"吸附对象的机会。

接下来自定义一个函数ShowPanel()用来控制吸附对象

其中:各参数说明:APanel--吸附面板对象,MakeVisible--面板可见标志,Client--重新显示面板时要显示的吸附对象。

//---------------------------------------------------------------------------

void TMainWin::ShowPanel(TPanel* APanel, bool MakeVisible, TControl* Client)

{

if (!MakeVisible && (APanel->VisibleDockClientCount > 1))//判断如果面板不可见,并且其中包含一个以上的被吸附对象,则直接返回

return;

if (APanel == DockPanel)//判断如果调用函数时得到的Panel对象是吸附面板,则将Splliter组件的可见性与Panel对象保持一致

VSplitter->Visible = MakeVisible;

if (MakeVisible)//在吸附面板可见的情况下,将面板的宽度设为窗口客户区的1/3,并同时将Splliter移动到面板的右边

{

APanel->Width = ClientWidth / 3;

VSplitter->Left = APanel->Width + VSplitter->Width;

}

else

APanel->Width = 0;//面板不可见时,维护面板宽度为0

if (MakeVisible && (Client != NULL))

Client->Show();//显示被吸附对象,并且要确保面板可见

}

//---------------------------------------------------------------------------------------------

下面的工作就是实现菜单命令的功能,"红色窗口"命令生成红色窗口,"兰色窗口"命令生成兰色窗口,"关闭"命令关闭整个程序。下面只给出动态生成红色窗口的事件句柄如下:(兰色窗口跟这个代码类似,这里省略)

//---------------------------------------------------------------------------------------------

//"红色窗口"命令的OnClick事件句柄:

void __fastcall TMainWin::CMredwindowClick(TObject *Sender)

{

TDockableForm *redform=new TDockableForm(this);

redform->Memo1->Color=clRed;

//要实现兰色窗口的动态生成,将上句改为 redform->Memo1->Color=clBlue;

redform->Show();

}

接下来在被吸附窗口DockWindow的OnClose事件添加如下代码:

//---------------------------------------------------------------------------

void __fastcall TDockableForm::FormClose(TObject *Sender,

TCloseAction &Action)

{

if (dynamic_cast<TPanel*>(HostDockSite) != NULL)

MainWin->ShowPanel(static_cast<TPanel*>(HostDockSite), false, NULL);

Action = caHide;

}

接下来在被吸附窗口DockWindow的OnDockOver事件中添加如下代码:

//---------------------------------------------------------------------------

void __fastcall TDockableForm::FormDockOver(TObject *Sender,

TDragDockObject *Source, int X, int Y, TDragState State,

bool &Accept)

{

TRect ARect;

Accept = (dynamic_cast<TDockableForm*>(Source->Control) != NULL);

// Draw dock preview depending on where the cursor is relative to our client area

if (Accept && (ComputerDockAlign(ARect, Point(X, Y)) != alNone))

Source->DockRect = ARect;

}

//---------------------------------------------------------------------------

接下来在被吸附窗口DockWindow的OnDockAlign事件中添加如下代码:

TAlign TDockableForm::ComputerDockAlign(TRect & DockRect, const TPoint & MousePos)

{

Windows::TRect DockTopRect,

DockLeftRect,

DockBottomRect,

DockRightRect,

DockCenterRect;

Windows::TPoint TopLeft,

BottomRight;

TAlign Result = alNone;

// Divide form up into docking "Zones"

TopLeft = Windows::TPoint(0, 0);

BottomRight = Windows::TPoint(ClientWidth / 5, ClientHeight);

DockLeftRect = Windows::TRect(TopLeft, BottomRight);

TopLeft = Windows::TPoint(ClientWidth / 5, 0);

BottomRight = Windows::TPoint(ClientWidth / 5 * 4, ClientHeight / 5);

DockTopRect = Windows::TRect(TopLeft, BottomRight);

TopLeft = Windows::TPoint(ClientWidth / 5 * 4, 0);

BottomRight = Windows::TPoint(ClientWidth, ClientHeight);

DockRightRect = Windows::TRect(TopLeft, BottomRight);

TopLeft = Windows::TPoint(ClientWidth / 5, ClientHeight / 5 * 4);

BottomRight = Windows::TPoint(ClientWidth / 5 * 4, ClientHeight);

DockBottomRect = Windows::TRect(TopLeft, BottomRight);

TopLeft = Windows::TPoint(ClientWidth / 5, ClientHeight / 5);

BottomRight = Windows::TPoint(ClientWidth / 5 * 4, ClientHeight / 5 * 4);

DockCenterRect = Windows::TRect(TopLeft, BottomRight);

// Find out where the mouse cursor is,

// to decide where to draw dock preview.

if (PtInRect(&DockLeftRect, MousePos))

{

Result = alLeft;

DockRect = DockLeftRect;

DockRect.Right = ClientWidth / 2;

}

else

if (PtInRect(&DockTopRect, MousePos))

{

Result = alTop;

DockRect = DockTopRect;

DockRect.Left = 0;

DockRect.Right = ClientWidth;

DockRect.Bottom = ClientHeight / 2;

}

else

if (PtInRect(&DockRightRect, MousePos))

{

Result = alRight;

DockRect = DockRightRect;

DockRect.Left = ClientWidth / 2;

}

else

if (PtInRect(&DockBottomRect, MousePos))

{

Result = alBottom;

DockRect = DockBottomRect;

DockRect.Left = 0;

DockRect.Right = ClientWidth;

DockRect.Top = ClientHeight / 2;

}

else

if (PtInRect(&DockCenterRect, MousePos))

{

Result = alClient;

DockRect = DockCenterRect;

}

if (Result != alNone)

{

// DockRect is in screen coordinates.

TopLeft = ClientToScreen(Windows::TPoint(DockRect.Left, DockRect.Top));

BottomRight = ClientToScreen(Windows::TPoint(DockRect.Right, DockRect.Bottom));

DockRect = TRect(TopLeft, BottomRight);

}

return Result;

}

至此,就可以实现窗口吸附的基本功能。在C++ Builder的例程中,有一个实现复杂"吸附"功能的例程,大家可以多花一些时间来研究研究。路径为:Program Files/Borland/C++Builder 5/Examples/Docking

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有