[CB]将窗体从属于主窗体
生活随笔
收集整理的這篇文章主要介紹了
[CB]将窗体从属于主窗体
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
幾乎所有正式一點(diǎn)的C++ Builder程序除了主窗體外都還有從屬窗體,有時(shí)是對(duì)話框,有時(shí)是無(wú)模式窗口。VCL使得創(chuàng)建和顯示從屬窗體都易如反掌。但不是所有程序都適于采用無(wú)模式窗體,有些程序需要在一個(gè)主窗體內(nèi)顯示不同的內(nèi)容。本文討論如何將一個(gè)從屬窗體“寄居”于主窗體中,從屬窗體看上去是主窗體的一部分,用戶甚至不知道一個(gè)從窗體正被顯示。圖A顯示了一個(gè)主窗體,其客戶區(qū)是一個(gè)從窗體。
圖A 客戶區(qū)包含從屬窗體的主窗體
理解子/父聯(lián)系
這類程序的基本思路是讓所有從屬窗體都作主窗體的子窗體,這種設(shè)計(jì)在其他框架(如OWL或MFC)中很常見(jiàn),但在VCL程序中卻不常見(jiàn)。VCL不允許簡(jiǎn)單地指定一下屬性就使一個(gè)窗體從屬于另一窗體,要做到這一點(diǎn)還得付出點(diǎn)小小的勞動(dòng)。你得告訴Microsoft Windows從屬窗體是主窗體的子對(duì)象,在C++ Builder編程中一般趨于認(rèn)為窗體是窗口,元件是子對(duì)象,實(shí)際上從Windows的觀點(diǎn)來(lái)看,窗體和元件都是窗口。可以將任一窗口
(窗體和元件)指定為另一窗口的子對(duì)象,只要你暫時(shí)跳出VCL圈子。
更好的“鼠夾”
將一個(gè)窗體附屬于一個(gè)主窗體的一個(gè)好處是你可以象設(shè)計(jì)任何其他從屬窗體一樣設(shè)計(jì)子窗體,就是說(shuō)你創(chuàng)建一個(gè)新的窗體,在其上添加元件并書(shū)寫(xiě)這個(gè)窗體的代碼。這樣使得設(shè)計(jì)你的子窗體變得容易,并將所有操縱子窗體的代碼集中在一個(gè)地方。
程序設(shè)計(jì)范例
先給出一些程序的背景,程序名叫PARENTING,有一個(gè)主窗體,主窗體的頂部和底部各有一個(gè)工具條(Tool Bar)和狀態(tài)條(Status bar),除主窗體外,還有兩個(gè)子窗體,一個(gè)叫TTableForm,用柵格顯示ANIMAL.DBF數(shù)據(jù)表,ANIMAL表是C++ Builder帶的數(shù)據(jù)庫(kù)樣本的一個(gè)表。另一個(gè)子窗體TChartForm用TChart顯示ANIMAL表。(如果你購(gòu)買的C++ Builder是標(biāo)準(zhǔn)版則沒(méi)有數(shù)據(jù)庫(kù)元件)你可以通過(guò)點(diǎn)擊菜單項(xiàng)或工具按鈕來(lái)選擇顯示表單還是圖形窗體,在你作出選擇時(shí),活動(dòng)窗體被摧毀而被選窗體被顯示,子窗體在主窗體的工具條下方、狀態(tài)條上方的客戶區(qū)顯示,而且隨主窗體大小變動(dòng)而隨時(shí)保持充滿客戶區(qū)。
重載CreateParams()
如前所述,為讓主窗體控制從屬窗體,需要將主窗體設(shè)為從窗體的“父”,這可以通過(guò)重載CreateParams()方法來(lái)完成。CreateParams()在VCL創(chuàng)建與窗體聯(lián)系的窗口時(shí)調(diào)用,CreateParams()的聲明如下:
void __fastcall CreateParams(TCreateParams& Params);
CreateParams()的唯一參數(shù)是對(duì)一個(gè)TCreateParams結(jié)構(gòu)體的引用。在VCL中TCreateParams定義如下:
struct TCreateParams
{
char *Caption;
int Style;
int ExStyle;
int X;
int Y;
int Width;
int Height;
HWND WndParent;
void *Param;
tagWNDCLASSA WindowClass;
char WinClassName[64];
};
此結(jié)構(gòu)包含了Windows創(chuàng)建一個(gè)窗口所需的所有信息(如果你曾用API進(jìn)行Windows編程,你一定能意識(shí)到TCreateParams的成員對(duì)Windows CREATESTRUCT結(jié)構(gòu)的映射)。在重載CreateParams()時(shí),首先調(diào)用基類的CreateParams()方法,然后修改TCreateParams結(jié)構(gòu)的個(gè)別成員變量。一個(gè)重載過(guò)的CreateParams()方法看起來(lái)大致如下:
void __fastcall TChartForm::CreateParams(TCreateParams& Params)
{
TForm::CreateParams(Params);
Params.Style=WS_CHILD|WS_CLIPSIBLINGS;
Params.WndParent=MainForm->Handle;
Params.X=0;
Params.Y=0;
Params.Width=MainForm->ClientRect.Right;
Params.Height=MainForm->ClientRect.Bottom;
}
程序的關(guān)鍵是設(shè)置TCreateParams結(jié)構(gòu)體的Style和WndParent成員,Style設(shè)WS_CHILD 和WS_CLIPSIBLINGS窗口式樣,WS_CHILD指定窗口為另一窗口的子窗口。根據(jù)定義,一個(gè)子窗口是沒(méi)有標(biāo)題棒的,設(shè)計(jì)時(shí)的標(biāo)題棒在Windows在運(yùn)行時(shí)創(chuàng)建窗體會(huì)被去掉。WS_CLIPSIBLINGS保證主窗口的不同子窗口在窗體繪制時(shí)不互相干涉。很明顯,一個(gè)子窗口必須得有一個(gè)父對(duì)象,通過(guò)將父窗口句柄指定給TCreateParams結(jié)構(gòu)體WndParent 成員就指定了父對(duì)象,正如前面代碼所示,WndParent成員設(shè)置為主窗體的Handle屬性。鑒于指定父對(duì)象屬性相對(duì)直接明了,我不在這個(gè)主題上深入更多。
設(shè)置子窗體的屬性
除了CreateParams()方法中的代碼外,還得設(shè)置一些子窗體的屬性,多數(shù)屬性可以保留缺省值,但AutoScroll屬性應(yīng)設(shè)為false,當(dāng)然,前提是你的窗體要設(shè)計(jì)為不必卷動(dòng)的窗體風(fēng)格。因?yàn)樽哟翱诘拇笮『臀恢迷贑reateParams()中設(shè)定,所以Position屬性可置為poDefault。Caption和BorderIcon屬性將被忽略,故而無(wú)需指定。確保BorderStyle置為bsSizeable,還有BorderWidth置為0,如果此二屬性設(shè)成其它值,子窗體與主窗體會(huì)不協(xié)調(diào)。
窗體的其它元件
很多時(shí)候,除了從屬窗體外,主窗體還包含別的元件,比如工具條和狀態(tài)條,此時(shí)設(shè)置TCreateParams結(jié)構(gòu)體的X、Y、Width和Height成員時(shí)得考慮到工具條和狀態(tài)條,子窗體應(yīng)與頂部的工具條和底部隊(duì)狀態(tài)條協(xié)調(diào)。因此設(shè)置TCreateParams結(jié)構(gòu)體的成員的代碼應(yīng)該是:
Params.X=0;
Params.Y=MainForm->ToolBar->Height+1;
Params.Width=MainForm->ClientRect.Right;
Params.Height=(MainForm->StatusBar->Top-1)-Params.Y;
注意Y設(shè)為工具條底部加1,子窗體寬度置為主窗體客戶區(qū)寬度,高度根據(jù)子窗體和狀態(tài)條的頂部計(jì)算,基本上為界于工具條底部和狀態(tài)條底部之間的主窗體客戶區(qū)。這些就是使從屬窗體“寄居”于主窗體的所有要求,你可能還有其它想在子窗體中實(shí)現(xiàn)的特征,我將把這些特征的討論留到后面。
設(shè)置主窗體
主窗體也得進(jìn)行設(shè)置以控制其“收留”的子窗體。首先需要將子窗體從自動(dòng)創(chuàng)建窗體列表中去掉,需要時(shí)再創(chuàng)建它們。如果不從列表中去掉,則當(dāng)你的應(yīng)用程序啟動(dòng)時(shí)它們會(huì)自動(dòng)顯示。你還需要一個(gè)變量來(lái)跟蹤當(dāng)前的活動(dòng)子窗體,在主窗體的Public區(qū)聲明如下變量,TForm * ActiveChild; ActiveChild是公有的,因?yàn)樽哟绑w要訪問(wèn)這個(gè)變量。我馬上會(huì)演示如何使用這一變量。現(xiàn)在來(lái)書(shū)寫(xiě)顯示子窗體的代碼,先看下面程序行,然后我來(lái)解釋。
void __fastcall TMainForm::Chart1Click(TObject *Sender)
{
if(ActiveChild)
delete ActiveChild;
TChartForm * form=new TChartForm(this);
ActiveChild=form;
form->Show();
Chart1->Checked=true;
Table1->Checked=false;
}
此方法是主窗體的菜單項(xiàng)的OnClick處理句柄,猜的沒(méi)錯(cuò),這是在顯示TChartForm子窗體。首先檢查ActiveChild變量是否非0,如果有激活動(dòng)子窗體ActiveChild將非0。如果ActiveChild不為0,刪除此變量關(guān)聯(lián)的指針以摧毀活動(dòng)子窗體,否則程序會(huì)將子窗體一個(gè)接一個(gè)地堆疊在一起。接著創(chuàng)建一個(gè)TChartForm類的實(shí)例,將new操作返回到指針賦予ActiveChild變量,這樣ActiveChild總是包含一個(gè)指向當(dāng)前子窗體的指針。最后調(diào)用Sow()方法顯示子窗體。最后兩行代碼確保代表表單或圖形顯示的菜單項(xiàng)顯示一個(gè)選中標(biāo)記。為完成ActiveChild變量的討論,我得帶你回到子窗體單元一會(huì)兒。每個(gè)子窗體都含有一個(gè)如下的OnClose事件的事件句柄:
void __fastcall TChartForm::FormClose(TObject *Sender,
TCloseAction &Action)
{
MainForm->ActiveChild=0;
MainForm->Chart1->Checked=false;
Action=caFree;
}
注意當(dāng)窗體被摧毀時(shí),主窗體的ActiveChild被置為0,并將與子窗體關(guān)聯(lián)的菜單項(xiàng)置為未選中。Action參數(shù)置為caFree,以通知VCL釋放與此窗體關(guān)聯(lián)的內(nèi)存。你或許會(huì)疑惑為何FormClose句柄包含上面最后兩行。答案是每個(gè)子窗體都有一個(gè)Close按鈕用來(lái)關(guān)閉窗體,如果用Close按鈕來(lái)關(guān)閉窗體,就需要釋放內(nèi)存和uncheck菜單項(xiàng)。
額外特征
例子程序至少還有一個(gè)尚未討論的特征,就是如果子窗體比主窗體大,要重新調(diào)整主窗體大小以容納子窗體。這些語(yǔ)句放在子窗體的CreateParams()方法中。之前我展示過(guò)一個(gè)簡(jiǎn)單的CreateParams()例子,但沒(méi)有放進(jìn)調(diào)整主窗體大小的語(yǔ)句。列表B中有完整的CreateParams()方法,和先前展示唯一不同的是包含以下語(yǔ)句:
if(Width>MainForm->ClientWidth)
MainForm->ClientWidth=Width;
if(Height>(MainForm->StatusBar->Top-MainForm->ToolBar->Height))
MainForm->ClientHeight=Height+
MainForm->ToolBar->Height+
MainForm->StatusBar->Height ;
這幾條語(yǔ)句檢查子窗體的寬度是否大于主窗體的ClientWidth屬性,如果是,主窗體的ClientWidth置為子窗體的寬度。余下的幾行作的是同樣的事情,只不過(guò)針對(duì)的是主窗體的客戶區(qū)高度而已。這些語(yǔ)句的結(jié)果是主窗體總是被調(diào)整到能完全容納被顯示的子窗體。范例程序還考慮了主窗體調(diào)整大小時(shí)的情況。如果主窗口大小有變化,子窗體大小也必須變化以進(jìn)行充滿主窗體的客戶區(qū)。以下語(yǔ)句演示了主窗體的OnResize事件句柄:
void __fastcall TMainForm::FormResize(TObject *Sender)
{
if(ActiveChild)
{
ActiveChild->Width=ClientRect.Right;
ActiveChild->Height=(MainForm->StatusBar->Top-1)-
ActiveChild->Top;
}
}
這些語(yǔ)句相當(dāng)直接,無(wú)需我逐條解釋。注意首先檢查ActiveChild變量確保非0(即指向某子窗體),很明顯如果當(dāng)前沒(méi)有激活任何子窗體,在OnResize中就什么都不用干。其余語(yǔ)句是CreateParams()中看到語(yǔ)句的變種,只是簡(jiǎn)單地計(jì)算子窗體的新尺寸并設(shè)置相應(yīng)的Width和Height屬性。
結(jié)語(yǔ)
列表A包含了例子程序主窗體的代碼。列表B示出了TChartForm單元的源碼。頭文件都沒(méi)有給出因?yàn)闆](méi)有什么有意義的語(yǔ)句,也未給出TTableForm單元的語(yǔ)句,因?yàn)榕cChatForm單元類似。你可以在www.reisdorph.com下載例子程序。將子窗體“寄居”于主窗體提供了一種清晰的替代MDI的方法,對(duì)那些只能以無(wú)模式窗體形式向用戶顯示數(shù)據(jù)的程序也是一種替代,使用子窗體允許你使用窗體設(shè)計(jì)器設(shè)計(jì)你的從窗口,并將操縱子窗體的代碼放在一個(gè)地方。
列表A:MAINU.CPP
#include <vcl.h>
#pragma hdrstop
#include "MainU.h"
#include "ChartU.h"
#include "TableU.h"
#pragma resource "*.dfm"
TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
//清0以防包含隨機(jī)數(shù)
ActiveChild=0;
//打開(kāi)數(shù)據(jù)表
Table->Active=true;
}
void __fastcall TMainForm::Table1Click(TObject *Sender)
{
if(Table1->Checked) return;
if(ActiveChild){
delete ActiveChild;
ActiveChild=0;
}
TTableForm *form=new TTableForm(this);
//將DBGrid的DBGrid::DataSource屬性賦給數(shù)據(jù)源
form->DBGrid->DataSource=DataSource;
//跟蹤活動(dòng)子窗體
Active=form;
form->Show();
Table1->Checked=true;
Chart1->Checked=false;
}
void __fastcall TMainForm::Chart1Click(TObject *Sender)
{
if(Chart1->Checked) return;
if(ActiveChild){
delete ActiveChild;
ActiveChild=0;
}
TChartForm *form=new TChartForm(this);
Active=form;
form->Show();
Chart1->Checked=true;
Table1->Checked=false;
}
void __fastcall TMainForm::FormResize(TObject *Sender)
{
if(ActiveChild){
ActiveChild->Width=ClientRect.Right;
ActiveChild->Height=(MainForm->StatusBar->Top-1)-
ActiveChild->Top;
}
}
列表B:CHARTU.CPP
#include <vcl.h>
#pragma hdrstop
#include "ChartU.h"
#include "MainU.h"
#pragma resource "*.dfm"
TChartForm *ChartForm;
__fastcall TChartForm::TChartForm(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TChartForm::CreateParams(TCreateParams& Params)
{
//調(diào)用基類CreateParams方法
TForm::CreateParams(Params);
//子窗口類型
Params.Style=WS_CHILD|WS_CLIPSIBLINGS;
//設(shè)置父為主窗體
Params.WndParent=MainForm->Handle;
Params.X=0;
if(Width>MainForm->ClientWidth)
MainForm->ClientWidth=Width;
if(Height>(MainForm->StatusBar->Top-MainForm->ToolBar->Height))
MainForm->ClientHeight=Height+
MainForm->ToolBar->Height+
MainForm->StatusBar->Height;
Params.Y=MainForm->ToolBar->Height+1;
Params.Width=MainForm->ClientRect.Right;
Params.Height=(MainForm->StatusBar->Top-1)-Params.Y;
}
void __fastcall TChartForm::FormClose(TObject *Sender,
TCloseAction &Action)
{
MainForm->ActiveChild=0;
MainForm->Chart1->Checked=false;
Action=caFree;
}
void __fastcall TChartForm::CloseBtnClick(TObject *Sender)
{
Close();
}
圖A 客戶區(qū)包含從屬窗體的主窗體
理解子/父聯(lián)系
這類程序的基本思路是讓所有從屬窗體都作主窗體的子窗體,這種設(shè)計(jì)在其他框架(如OWL或MFC)中很常見(jiàn),但在VCL程序中卻不常見(jiàn)。VCL不允許簡(jiǎn)單地指定一下屬性就使一個(gè)窗體從屬于另一窗體,要做到這一點(diǎn)還得付出點(diǎn)小小的勞動(dòng)。你得告訴Microsoft Windows從屬窗體是主窗體的子對(duì)象,在C++ Builder編程中一般趨于認(rèn)為窗體是窗口,元件是子對(duì)象,實(shí)際上從Windows的觀點(diǎn)來(lái)看,窗體和元件都是窗口。可以將任一窗口
(窗體和元件)指定為另一窗口的子對(duì)象,只要你暫時(shí)跳出VCL圈子。
更好的“鼠夾”
將一個(gè)窗體附屬于一個(gè)主窗體的一個(gè)好處是你可以象設(shè)計(jì)任何其他從屬窗體一樣設(shè)計(jì)子窗體,就是說(shuō)你創(chuàng)建一個(gè)新的窗體,在其上添加元件并書(shū)寫(xiě)這個(gè)窗體的代碼。這樣使得設(shè)計(jì)你的子窗體變得容易,并將所有操縱子窗體的代碼集中在一個(gè)地方。
程序設(shè)計(jì)范例
先給出一些程序的背景,程序名叫PARENTING,有一個(gè)主窗體,主窗體的頂部和底部各有一個(gè)工具條(Tool Bar)和狀態(tài)條(Status bar),除主窗體外,還有兩個(gè)子窗體,一個(gè)叫TTableForm,用柵格顯示ANIMAL.DBF數(shù)據(jù)表,ANIMAL表是C++ Builder帶的數(shù)據(jù)庫(kù)樣本的一個(gè)表。另一個(gè)子窗體TChartForm用TChart顯示ANIMAL表。(如果你購(gòu)買的C++ Builder是標(biāo)準(zhǔn)版則沒(méi)有數(shù)據(jù)庫(kù)元件)你可以通過(guò)點(diǎn)擊菜單項(xiàng)或工具按鈕來(lái)選擇顯示表單還是圖形窗體,在你作出選擇時(shí),活動(dòng)窗體被摧毀而被選窗體被顯示,子窗體在主窗體的工具條下方、狀態(tài)條上方的客戶區(qū)顯示,而且隨主窗體大小變動(dòng)而隨時(shí)保持充滿客戶區(qū)。
重載CreateParams()
如前所述,為讓主窗體控制從屬窗體,需要將主窗體設(shè)為從窗體的“父”,這可以通過(guò)重載CreateParams()方法來(lái)完成。CreateParams()在VCL創(chuàng)建與窗體聯(lián)系的窗口時(shí)調(diào)用,CreateParams()的聲明如下:
void __fastcall CreateParams(TCreateParams& Params);
CreateParams()的唯一參數(shù)是對(duì)一個(gè)TCreateParams結(jié)構(gòu)體的引用。在VCL中TCreateParams定義如下:
struct TCreateParams
{
char *Caption;
int Style;
int ExStyle;
int X;
int Y;
int Width;
int Height;
HWND WndParent;
void *Param;
tagWNDCLASSA WindowClass;
char WinClassName[64];
};
此結(jié)構(gòu)包含了Windows創(chuàng)建一個(gè)窗口所需的所有信息(如果你曾用API進(jìn)行Windows編程,你一定能意識(shí)到TCreateParams的成員對(duì)Windows CREATESTRUCT結(jié)構(gòu)的映射)。在重載CreateParams()時(shí),首先調(diào)用基類的CreateParams()方法,然后修改TCreateParams結(jié)構(gòu)的個(gè)別成員變量。一個(gè)重載過(guò)的CreateParams()方法看起來(lái)大致如下:
void __fastcall TChartForm::CreateParams(TCreateParams& Params)
{
TForm::CreateParams(Params);
Params.Style=WS_CHILD|WS_CLIPSIBLINGS;
Params.WndParent=MainForm->Handle;
Params.X=0;
Params.Y=0;
Params.Width=MainForm->ClientRect.Right;
Params.Height=MainForm->ClientRect.Bottom;
}
程序的關(guān)鍵是設(shè)置TCreateParams結(jié)構(gòu)體的Style和WndParent成員,Style設(shè)WS_CHILD 和WS_CLIPSIBLINGS窗口式樣,WS_CHILD指定窗口為另一窗口的子窗口。根據(jù)定義,一個(gè)子窗口是沒(méi)有標(biāo)題棒的,設(shè)計(jì)時(shí)的標(biāo)題棒在Windows在運(yùn)行時(shí)創(chuàng)建窗體會(huì)被去掉。WS_CLIPSIBLINGS保證主窗口的不同子窗口在窗體繪制時(shí)不互相干涉。很明顯,一個(gè)子窗口必須得有一個(gè)父對(duì)象,通過(guò)將父窗口句柄指定給TCreateParams結(jié)構(gòu)體WndParent 成員就指定了父對(duì)象,正如前面代碼所示,WndParent成員設(shè)置為主窗體的Handle屬性。鑒于指定父對(duì)象屬性相對(duì)直接明了,我不在這個(gè)主題上深入更多。
設(shè)置子窗體的屬性
除了CreateParams()方法中的代碼外,還得設(shè)置一些子窗體的屬性,多數(shù)屬性可以保留缺省值,但AutoScroll屬性應(yīng)設(shè)為false,當(dāng)然,前提是你的窗體要設(shè)計(jì)為不必卷動(dòng)的窗體風(fēng)格。因?yàn)樽哟翱诘拇笮『臀恢迷贑reateParams()中設(shè)定,所以Position屬性可置為poDefault。Caption和BorderIcon屬性將被忽略,故而無(wú)需指定。確保BorderStyle置為bsSizeable,還有BorderWidth置為0,如果此二屬性設(shè)成其它值,子窗體與主窗體會(huì)不協(xié)調(diào)。
窗體的其它元件
很多時(shí)候,除了從屬窗體外,主窗體還包含別的元件,比如工具條和狀態(tài)條,此時(shí)設(shè)置TCreateParams結(jié)構(gòu)體的X、Y、Width和Height成員時(shí)得考慮到工具條和狀態(tài)條,子窗體應(yīng)與頂部的工具條和底部隊(duì)狀態(tài)條協(xié)調(diào)。因此設(shè)置TCreateParams結(jié)構(gòu)體的成員的代碼應(yīng)該是:
Params.X=0;
Params.Y=MainForm->ToolBar->Height+1;
Params.Width=MainForm->ClientRect.Right;
Params.Height=(MainForm->StatusBar->Top-1)-Params.Y;
注意Y設(shè)為工具條底部加1,子窗體寬度置為主窗體客戶區(qū)寬度,高度根據(jù)子窗體和狀態(tài)條的頂部計(jì)算,基本上為界于工具條底部和狀態(tài)條底部之間的主窗體客戶區(qū)。這些就是使從屬窗體“寄居”于主窗體的所有要求,你可能還有其它想在子窗體中實(shí)現(xiàn)的特征,我將把這些特征的討論留到后面。
設(shè)置主窗體
主窗體也得進(jìn)行設(shè)置以控制其“收留”的子窗體。首先需要將子窗體從自動(dòng)創(chuàng)建窗體列表中去掉,需要時(shí)再創(chuàng)建它們。如果不從列表中去掉,則當(dāng)你的應(yīng)用程序啟動(dòng)時(shí)它們會(huì)自動(dòng)顯示。你還需要一個(gè)變量來(lái)跟蹤當(dāng)前的活動(dòng)子窗體,在主窗體的Public區(qū)聲明如下變量,TForm * ActiveChild; ActiveChild是公有的,因?yàn)樽哟绑w要訪問(wèn)這個(gè)變量。我馬上會(huì)演示如何使用這一變量。現(xiàn)在來(lái)書(shū)寫(xiě)顯示子窗體的代碼,先看下面程序行,然后我來(lái)解釋。
void __fastcall TMainForm::Chart1Click(TObject *Sender)
{
if(ActiveChild)
delete ActiveChild;
TChartForm * form=new TChartForm(this);
ActiveChild=form;
form->Show();
Chart1->Checked=true;
Table1->Checked=false;
}
此方法是主窗體的菜單項(xiàng)的OnClick處理句柄,猜的沒(méi)錯(cuò),這是在顯示TChartForm子窗體。首先檢查ActiveChild變量是否非0,如果有激活動(dòng)子窗體ActiveChild將非0。如果ActiveChild不為0,刪除此變量關(guān)聯(lián)的指針以摧毀活動(dòng)子窗體,否則程序會(huì)將子窗體一個(gè)接一個(gè)地堆疊在一起。接著創(chuàng)建一個(gè)TChartForm類的實(shí)例,將new操作返回到指針賦予ActiveChild變量,這樣ActiveChild總是包含一個(gè)指向當(dāng)前子窗體的指針。最后調(diào)用Sow()方法顯示子窗體。最后兩行代碼確保代表表單或圖形顯示的菜單項(xiàng)顯示一個(gè)選中標(biāo)記。為完成ActiveChild變量的討論,我得帶你回到子窗體單元一會(huì)兒。每個(gè)子窗體都含有一個(gè)如下的OnClose事件的事件句柄:
void __fastcall TChartForm::FormClose(TObject *Sender,
TCloseAction &Action)
{
MainForm->ActiveChild=0;
MainForm->Chart1->Checked=false;
Action=caFree;
}
注意當(dāng)窗體被摧毀時(shí),主窗體的ActiveChild被置為0,并將與子窗體關(guān)聯(lián)的菜單項(xiàng)置為未選中。Action參數(shù)置為caFree,以通知VCL釋放與此窗體關(guān)聯(lián)的內(nèi)存。你或許會(huì)疑惑為何FormClose句柄包含上面最后兩行。答案是每個(gè)子窗體都有一個(gè)Close按鈕用來(lái)關(guān)閉窗體,如果用Close按鈕來(lái)關(guān)閉窗體,就需要釋放內(nèi)存和uncheck菜單項(xiàng)。
額外特征
例子程序至少還有一個(gè)尚未討論的特征,就是如果子窗體比主窗體大,要重新調(diào)整主窗體大小以容納子窗體。這些語(yǔ)句放在子窗體的CreateParams()方法中。之前我展示過(guò)一個(gè)簡(jiǎn)單的CreateParams()例子,但沒(méi)有放進(jìn)調(diào)整主窗體大小的語(yǔ)句。列表B中有完整的CreateParams()方法,和先前展示唯一不同的是包含以下語(yǔ)句:
if(Width>MainForm->ClientWidth)
MainForm->ClientWidth=Width;
if(Height>(MainForm->StatusBar->Top-MainForm->ToolBar->Height))
MainForm->ClientHeight=Height+
MainForm->ToolBar->Height+
MainForm->StatusBar->Height ;
這幾條語(yǔ)句檢查子窗體的寬度是否大于主窗體的ClientWidth屬性,如果是,主窗體的ClientWidth置為子窗體的寬度。余下的幾行作的是同樣的事情,只不過(guò)針對(duì)的是主窗體的客戶區(qū)高度而已。這些語(yǔ)句的結(jié)果是主窗體總是被調(diào)整到能完全容納被顯示的子窗體。范例程序還考慮了主窗體調(diào)整大小時(shí)的情況。如果主窗口大小有變化,子窗體大小也必須變化以進(jìn)行充滿主窗體的客戶區(qū)。以下語(yǔ)句演示了主窗體的OnResize事件句柄:
void __fastcall TMainForm::FormResize(TObject *Sender)
{
if(ActiveChild)
{
ActiveChild->Width=ClientRect.Right;
ActiveChild->Height=(MainForm->StatusBar->Top-1)-
ActiveChild->Top;
}
}
這些語(yǔ)句相當(dāng)直接,無(wú)需我逐條解釋。注意首先檢查ActiveChild變量確保非0(即指向某子窗體),很明顯如果當(dāng)前沒(méi)有激活任何子窗體,在OnResize中就什么都不用干。其余語(yǔ)句是CreateParams()中看到語(yǔ)句的變種,只是簡(jiǎn)單地計(jì)算子窗體的新尺寸并設(shè)置相應(yīng)的Width和Height屬性。
結(jié)語(yǔ)
列表A包含了例子程序主窗體的代碼。列表B示出了TChartForm單元的源碼。頭文件都沒(méi)有給出因?yàn)闆](méi)有什么有意義的語(yǔ)句,也未給出TTableForm單元的語(yǔ)句,因?yàn)榕cChatForm單元類似。你可以在www.reisdorph.com下載例子程序。將子窗體“寄居”于主窗體提供了一種清晰的替代MDI的方法,對(duì)那些只能以無(wú)模式窗體形式向用戶顯示數(shù)據(jù)的程序也是一種替代,使用子窗體允許你使用窗體設(shè)計(jì)器設(shè)計(jì)你的從窗口,并將操縱子窗體的代碼放在一個(gè)地方。
列表A:MAINU.CPP
#include <vcl.h>
#pragma hdrstop
#include "MainU.h"
#include "ChartU.h"
#include "TableU.h"
#pragma resource "*.dfm"
TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
//清0以防包含隨機(jī)數(shù)
ActiveChild=0;
//打開(kāi)數(shù)據(jù)表
Table->Active=true;
}
void __fastcall TMainForm::Table1Click(TObject *Sender)
{
if(Table1->Checked) return;
if(ActiveChild){
delete ActiveChild;
ActiveChild=0;
}
TTableForm *form=new TTableForm(this);
//將DBGrid的DBGrid::DataSource屬性賦給數(shù)據(jù)源
form->DBGrid->DataSource=DataSource;
//跟蹤活動(dòng)子窗體
Active=form;
form->Show();
Table1->Checked=true;
Chart1->Checked=false;
}
void __fastcall TMainForm::Chart1Click(TObject *Sender)
{
if(Chart1->Checked) return;
if(ActiveChild){
delete ActiveChild;
ActiveChild=0;
}
TChartForm *form=new TChartForm(this);
Active=form;
form->Show();
Chart1->Checked=true;
Table1->Checked=false;
}
void __fastcall TMainForm::FormResize(TObject *Sender)
{
if(ActiveChild){
ActiveChild->Width=ClientRect.Right;
ActiveChild->Height=(MainForm->StatusBar->Top-1)-
ActiveChild->Top;
}
}
列表B:CHARTU.CPP
#include <vcl.h>
#pragma hdrstop
#include "ChartU.h"
#include "MainU.h"
#pragma resource "*.dfm"
TChartForm *ChartForm;
__fastcall TChartForm::TChartForm(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TChartForm::CreateParams(TCreateParams& Params)
{
//調(diào)用基類CreateParams方法
TForm::CreateParams(Params);
//子窗口類型
Params.Style=WS_CHILD|WS_CLIPSIBLINGS;
//設(shè)置父為主窗體
Params.WndParent=MainForm->Handle;
Params.X=0;
if(Width>MainForm->ClientWidth)
MainForm->ClientWidth=Width;
if(Height>(MainForm->StatusBar->Top-MainForm->ToolBar->Height))
MainForm->ClientHeight=Height+
MainForm->ToolBar->Height+
MainForm->StatusBar->Height;
Params.Y=MainForm->ToolBar->Height+1;
Params.Width=MainForm->ClientRect.Right;
Params.Height=(MainForm->StatusBar->Top-1)-Params.Y;
}
void __fastcall TChartForm::FormClose(TObject *Sender,
TCloseAction &Action)
{
MainForm->ActiveChild=0;
MainForm->Chart1->Checked=false;
Action=caFree;
}
void __fastcall TChartForm::CloseBtnClick(TObject *Sender)
{
Close();
}
總結(jié)
以上是生活随笔為你收集整理的[CB]将窗体从属于主窗体的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: faiss(1):简介 安装 与 原理
- 下一篇: 基于bert模型的文本分类研究:“Pre