# 7.3 使用布局控件進行編程
現在我們開始使用布局控件進行窗口布局,首先,創建一個頂層的布局控件(任何類型的布局控件都可以),使用wxWindow::SetSizer函數將它和你的頂層窗口綁定.現在你可以在這個頂層布局控件中放置你的窗口或者其它控件元素了.如果你想讓你的頂層窗口的大小適合所有控件所需要的大小,你可以調用wxSizer::Fit函數,將那個頂層窗口的指針作為其參數.想要頂層窗口在以后的執行過程中尺寸永遠不小于初始尺寸,可以使用wxSizer:: SetSizeHints函數,將頂層窗口的指針作為參數,這將使得wxWindow::SetSizeHints函數以合適的參數被調用.
除了使用上面介紹的方法依次調用三個函數以外,你還可以直接通過調用wxWindow::SetSizerAndFit函數來達到同樣的效果,這會使得上面的三個函數依次被調用.
如果在你的frame窗口里使用了panel,你可能不知道到底該給frame還是panel指定布局控件.這個問題應該這樣看,如果你的frame窗口中只有一個panel,所有其它的窗口和控件都是panel的子窗口,那么wxWidgets已經知道怎樣將這個panel以合適的大小和位置放置在frame上了,因此你只需要給panel綁定一個布局控件,以便其可以對所有panel的子窗口進行布局.而如果你的frame窗口中有多個panel,那么首先你不得不為frame綁定一個布局控件以便對panel進行布局,然后針對每個panel還應該綁定一個布局控件,以便對 panel中的子窗口進行布局.
接下來的小節里,我們來依次描述一下每一種布局控件類型以及使用它們的方法:
使用wxBoxSizer進行編程
wxBoxSizer可以將它的容器子元素進行橫向或者縱向的排列(具體的排列方式在構造函數中指定).如果采用橫向排列的方法,則子元素在縱向上可以指定居中,頂部對齊,底部對齊,如果采用縱向排列的方法,子元素在橫向上可以指定居中,左對齊或者右對齊的方式.前一小節提到過的縮放因子用來指示在主要方向上的縮放,比如對于橫向排列來說,縮放因子指的就是在橫向上子元素的縮放比例. 下圖演示了上一小節最后一幅圖采用縱向排列的樣子.

你可以使用wxBoxSizer的Add方法增加一個子元素:
```
// 增加一個窗口
void Add(wxWindow* window, int stretch = 0, int flags = 0,
int border = 0);
// 增加一個布局控件
void Add(wxSizer* window, int stretch = 0, int flags = 0,
int border = 0);
```
第一個參數是要增加的窗口或者布局控件的指針
第二個參數是前面說過的縮放因子
第三個參數是一個比特位列表,用來指示新增的子元素的對齊和邊界的行為.對齊比特位用來指示當垂直排列的布局控件的寬度發生改變時子元素的水平對齊方式,或者是水平排列的布局控件的高度改變時子元素的垂直對齊方式,默認的值為 wxALIGN_LEFT | wxALIGN_TOP,可選的值列舉在下表中:
| 0 | 子元素保留原始大小. |
|:--- |:--- |
| wxGROW | 子元素隨這布局控件一起改變大小. 等同于wxEXPAND. |
| wxSHAPED | 子元素保持原有比例按縮放因子縮放. |
| wxALIGN_LEFT | 左對齊. |
| wxALIGN_RIGHT | 右對齊. |
| wxALIGN_TOP | 頂端對齊. |
| wxALIGN_BOTTOM | 底部對齊. |
| wxALIGN_CENTER_HORIZONTAL | 水平居中. |
| wxALIGN_CENTER_VERTICAL | 垂直居中. |
| wxALIGN_CENTER | 水平或者垂直居中. wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL. |
| wxLEFT | 邊界間隔位于子元素左面. |
| wxRIGHT | 邊界間隔位于子元素右面. |
| wxTOP | 邊界間隔位于子元素上面. |
| wxBOTTOM | 邊界間隔位于子元素下面. |
| wxALL | 邊界間隔位于子元素四周.wxLEFT | wxRIGHT | wxTOP | wxBOTTOM. |
第四個參數指定邊界間隔的大小
當然你也可以直接增加一段空白,下面演示了增加空白區域的幾種方法:
```
// 增加一段空白 (舊方法)
void Add(int width, int height, int stretch = 0, int flags = 0,
int border = 0);
// 增加一段固定大小的空白
void AddSpacer(int size);
// 增加一個可縮放的空白
void AddStretchSpacer(int stretch = 1);
```
上面第二種方法相當于調用Add(size, size, 0),第三種則相當于調用Add(0, 0, stretch).
我們來舉這樣一個例子,一個對話框包含一個多行文本框和兩個位于底端的按鈕.我們可以以這樣的角度去看待這些窗口,首先是一個頂層的垂直布局,包含一個多行文本框和一個底層的子布局控件,這個子布局控件是一個水平的布局控件,它包含一個OK按鈕,被放置在左面,和一個Cancel按鈕,被放置在右面.當對話框的大小發生變化的時候,我們希望多行文本框隨著對話框大小的變化而變化,而按鈕則保持它們原來的大小,并且在水平方向上居中排列,如下圖所示:

下面列出了實現上述對話框所使用的代碼:
```
MyDialog::MyDialog(wxWindow *parent, wxWindowID id,
const wxString &title )
: wxDialog(parent, id, title,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL );
// 創建一個最小大小為 100x60的多行文本框
topSizer->Add(
new wxTextCtrl( this, wxID_ANY, "My text.",
wxDefaultPosition, wxSize(100,60), wxTE_MULTILINE),
1, // 垂直方向可縮放,縮放因子為1
wxEXPAND| // 水平方向可縮放
wxALL, // 四周都由空白邊框
10 ); // 空白邊框大小為10
wxBoxSizer *buttonSizer = new wxBoxSizer( wxHORIZONTAL );
buttonSizer->Add(
new wxButton( this, wxID_OK, "OK" ),
0, // 水平方向不可縮放
wxALL, // 四周有空白邊框:(注意默認為頂部對齊)
10 ); // 空白邊框大小為10
buttonSizer->Add(
new wxButton( this, wxID_CANCEL, "Cancel" ),
0, // 水平方向不可縮放
wxALL, // 四周有空白邊框:(注意默認為頂部對齊)
10 ); // 空白邊框大小為10
topSizer->Add(
buttonSizer,
0, // 垂直方向不可縮放
wxALIGN_CENTER ); // 無邊框并且居中對齊
SetSizer( topSizer ); // 綁定對話框和布局控件
topSizer->Fit( this ); // 調用對話框大小
topSizer->SetSizeHints( this ); // 設置對話框最小大小
}
```
使用wxStaticBoxSizer編程
wxStaticBoxSizer是一個繼承自wxBoxSizer的布局控件,除了實現wxBoxSizer的功能,另外還在整個布局的范圍以外增加了一個靜態的邊框wxStaticBox,這個wxStaticBox需要手動創建并且在wxStaticBoxSizer的構造函數中作為參數傳入,Add函數和wxBoxSizer的Add函數用法相同.
下圖演示了使用wxStaticBoxSizer對一個復選框進行布局的樣子:

對應的代碼:
```
MyDialog::MyDialog(wxWindow *parent, wxWindowID id,
const wxString &title )
: wxDialog(parent, id, title,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
// 創建一個頂層布局控件
wxBoxSizer* topLevel = new wxBoxSizer(wxVERTICAL);
// 創建靜態文本框和靜態文本框布局控件
wxStaticBox* staticBox = new wxStaticBox(this,
wxID_ANY, wxT("General settings"));
wxStaticBoxSizer* staticSizer = new wxStaticBoxSizer(staticBox,
wxVERTICAL);
topLevel->Add(staticSizer, 0,
wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
// 在其中增加一個復選框
wxCheckBox* checkBox = new wxCheckBox( this, ID_CHECKBOX,
wxT("&Show splash screen"), wxDefaultPosition, wxDefaultSize);
staticSizer->Add(checkBox, 0, wxALIGN_LEFT |wxALL, 5);
SetSizer(topLevel);
topLevel->Fit(this);
topLevel->SetSizeHints(this);
}
```
使用wxGridSizer編程
wxGridSizer布局控件可以以二維表的方式排列它的子元素,這個二維表的每個表格的大小都是相同的,都等于最長的那個表格的長度和最高的那個表格的高度.創建一個wxGridSizer需要指定它的行數和列數,以及一個額外的行間距和列間距.Add方法和wxBoxSizer的用法相同.
下圖演示了一個兩行三列的網格布局控件,由于有一個很大的按鈕,導致這個格的大小很大,從而導致所有的表格的大小都跟著變大:

代碼如下:
```
MyDialog::MyDialog(wxWindow *parent, wxWindowID id,
const wxString &title )
: wxDialog(parent, id, title,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
// 創建一個頂層網格布局控件
wxGridSizer* gridSizer = new wxGridSizer(2, 3, 0, 0);
SetSizer(gridSizer);
wxButton* button1 = new wxButton(this, ID_BUTTON1, wxT("One"));
gridSizer->Add(button1, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button2 = new wxButton(this, ID_BUTTON2, wxT("Two (the second button)"));
gridSizer->Add(button2, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button3 = new wxButton(this, ID_BUTTON3, wxT("Three"));
gridSizer->Add(button3, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button4 = new wxButton(this, ID_BUTTON4, wxT("Four"));
gridSizer->Add(button4, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button5 = new wxButton(this, ID_BUTTON5, wxT("Five"));
gridSizer->Add(button5, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button6 = new wxButton(this, ID_BUTTON6, wxT("Six"));
gridSizer->Add(button6, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
gridSizer->Fit(this);
gridSizer->SetSizeHints(this);
}
```
使用wxFlexGridSizer編程
wxFlexGridSizer同樣采用二維表來對其子元素進行布局,和wxGridSizer不同的是,它不要求所有的表格的大小都是一樣的,只要求同一列上所有表格的寬度是相同的并且同一行上所有表格的高度是相同的,也就是說,行的高度或者列的寬度僅由這一行或者這一列上的子元素決定.另外還可以給行和列指定是否縮放,這意味著當整個布局控件的大小發生變化的時候,可以指定某些行或者列隨著整個布局控件的縮放而縮放.
創建一個wxFlexGridSizer可以指定行數,列數額外的垂直間距和水平間距.調用Add函數的方法和wxBoxSizer相同.
下圖演示了一個使用wxFlexGridSizer進行布局的對話框的樣子,正如你看到的那樣,和wxGridSizer相比整個布局顯的更緊湊了,因為中間很寬的那一列不再影響其它列的寬度了.

初始情況下,我們看不出來設置第一列可以改變大小的效果,不過如果我們如下圖所示的那樣改變這個對話框的水平方向的大小,,我們就可以看到第一列占用了額外增加的空間,并且第一列的子元素也為居中方式顯式.

代碼如下:
```
MyDialog::MyDialog(wxWindow *parent, wxWindowID id,
const wxString &title )
: wxDialog(parent, id, title,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
//創建一個復雜網格布局控件
wxFlexGridSizer* flexGridSizer = new wxFlexGridSizer(2, 3, 0, 0);
this->SetSizer(flexGridSizer);
//讓第一列可變大小
flexGridSizer->AddGrowableCol(0);
wxButton* button1 = new wxButton(this, ID_BUTTON1, wxT("One"));
flexGridSizer->Add(button1, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button2 = new wxButton(this, ID_BUTTON2, wxT("Two (the second button)"));
flexGridSizer->Add(button2, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button3 = new wxButton(this, ID_BUTTON3, wxT("Three"));
flexGridSizer->Add(button3, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button4 = new wxButton(this, ID_BUTTON4, wxT("Four"));
flexGridSizer->Add(button4, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button5 = new wxButton(this, ID_BUTTON5, wxT("Five"));
flexGridSizer->Add(button5, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxButton* button6 = new wxButton(this, ID_BUTTON6, wxT("Six"));
flexGridSizer->Add(button6, 0, wxALIGN_CENTER_HORIZONTAL|
wxALIGN_CENTER_VERTICAL|wxALL, 5);
flexGridSizer->Fit(this);
flexGridSizer->SetSizeHints(this);
}
```
使用wxGridBagSizer編程
這種布局控件用來模擬現實世界中的那種固定位置和大小的基于布局控件的布局.它將它的子元素按照一個虛擬的網格進行排列,不過子元素的位置是通過wxGBPosition對象指定的,對象的大小使用wxGBSpan指定,對象的大小不僅限于一個網格.
創建wxGridBagSizer的可選參數包括垂直和水平方向的間隔(默認為0),Add函數需要提供的參數包括子元素的位置和大小,另外的可選標記和邊框大小參數的意義和wxBoxSizer是一樣的.
下圖演示了一個使用wxGridBagSizer進行布局的例子,我們指定了其中一個按鈕的大小為兩個單元列,我們還指定了第二行和第三列的大小是可以變化的,這樣當我們改變對話框的大小的時候,就會出現如下面另外一幅圖的效果.


相關代碼如下:
```
MyDialog::MyDialog(wxWindow *parent, wxWindowID id,
const wxString &title )
: wxDialog(parent, id, title,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
wxGridBagSizer* gridBagSizer = new wxGridBagSizer();
SetTopSizer(gridBagSizer);
wxButton* b1 = new wxButton(this, wxID_ANY, wxT("One (0,0)"));
gridBagSizer->Add(b1, wxGBPosition(0, 0));
wxButton* b2 = new wxButton(this, wxID_ANY, wxT("Two (2,2)"));
gridBagSizer->Add(b2, wxGBPosition(2, 2), wxGBSpan(1, 2),
wxGROW);
wxButton* b3 = new wxButton(this, wxID_ANY, wxT("Three (3,2)"));
gridBagSizer->Add(b3, wxGBPosition(3, 2));
wxButton* b4 = new wxButton(this, wxID_ANY, wxT("Four (3,3)"));
gridBagSizer->Add(b4, wxGBPosition(3, 3));
gridBagSizer->AddGrowableRow(3);
gridBagSizer->AddGrowableCol(2);
gridBagSizer->Fit(this);
gridBagSizer->SetSizeHints(this);
}
```
- 第一章 介紹
- 1.1 為什么要使用wxWidgets?
- 1.2 wxWidgets的歷史
- 1.3 wxWidgets社區
- 1.4 wxWidgets和面向對象編程
- 1.5 wxWidgets的體系結構
- 1.6 許可協議
- 第一章小結
- 第二章 開始使用
- 2.1 一個小例子
- 2.2 應用程序類
- 2.3 Frame窗口類
- 2.4 事件處理函數
- 2.5 Frame窗口的構造函數
- 2.6 完整的例子
- 2.7 wxWidgets程序一般執行過程
- 2.8 編譯和運行程序
- 第二章小結
- 第三章 事件處理
- 3.1 事件驅動編程
- 3.2 事件表和事件處理過程
- 3.3 過濾某個事件
- 3.4 掛載事件表
- 3.5 動態事件處理方法
- 3.6 窗口標識符
- 3.7 自定義事件
- 第三章小結
- 第四章 窗口的基礎知識
- 4.1 窗口解析
- 4.2 窗口類概覽
- 4.3 基礎窗口類
- 4.4 頂層窗口
- 4.5 容器窗口
- 4.6 非靜態控件
- 4.7 靜態控件
- 4.8 菜單
- 4.9 控制條
- 第四章小結
- 第五章繪畫和打印
- 5.1 理解設備上下文
- 5.2 繪畫工具
- 5.3 設備上下文中的繪畫函數
- 5.4 使用打印框架
- 5.5 使用wxGLCanvas繪制三維圖形
- 第五章小節
- 第六章處理用戶輸入
- 6.1 鼠標輸入
- 6.2 處理鍵盤事件
- 6.3 處理游戲手柄事件
- 第六章小結
- 第七章使用布局控件進行窗口布局
- 7.1 窗口布局基礎
- 7.2 窗口布局控件
- 7.3 使用布局控件進行編程
- 7.4 更多關于布局的話題
- 第七章小結
- 第八章使用標準對話框
- 8.1信息對話框
- 8.2 文件和目錄對話框
- 8.3 選擇和選項對話框
- 8.4 輸入對話框
- 8.5 打印對話框
- 第八章小結
- 第九章創建定制的對話框
- 9.1 創建定制對話框的步驟
- 9.2 一個例子:PersonalRecordDialog
- 9.3 在小型設備上調整你的對話框
- 9.4 一些更深入的話題
- 9.5 使用wxWidgets資源文件
- 第九章小結
- 第十章使用圖像編程
- 10.1 wxWidgets中圖片相關的類
- 10.2 使用wxBitmap編程
- 10.3 使用wxIcon編程
- 10.4 使用wxCursor編程
- 10.5 使用wxImage編程
- 10.6 圖片列表和圖標集
- 10.7 自定義wxWidgets提供的小圖片
- 第十章小結
- 第十一章剪貼板和拖放操作
- 11.1 數據對象
- 11.2 使用剪貼板
- 11.3 實現拖放操作
- 第十一章小結
- 第十二章高級窗口控件
- 12.1 wxTreeCtrl
- 12.2 wxListCtrl
- 12.3 wxWizard
- 12.4 wxHtmlWindow
- 12.5 wxGrid
- 12.6 wxTaskBarIcon
- 12.7 編寫自定義的控件
- 第十二章小結
- 第十三章數據結構類
- 13.1 為什么沒有使用STL?
- 13.2 字符串類型
- 13.3 wxArray
- 13.4 wxList和wxNode
- 13.5 wxHashMap
- 13.6 存儲和使用日期和時間
- 13.7 其它常用的數據類型
- 第十三章小結
- 第十四章文件和流操作
- 14.1 文件類和函數
- 14.2 流操作相關類
- 第十四章小結
- 第十五章內存管理,調試和錯誤處理
- 15.1 內存管理基礎
- 15.2 檢測內存泄漏和其它錯誤
- 15.3 構建自防御的程序
- 15.4 錯誤報告
- 15.5 提供運行期類型信息
- 15.6 使用wxModule
- 15.7 加載動態鏈接庫
- 15.8 異常處理
- 15.9 調試提示
- 第十五章小結
- 第十六章編寫國際化程序
- 16.1 國際化介紹
- 16.2 從翻譯說起
- 16.3 字符編碼和Unicode
- 16.4 數字和日期
- 16.5 其它媒介
- 16.6 一個小例子
- 第十六章小結
- 第十七章編寫多線程程序
- 17.1 什么時候使用多線程,什么時候不要使用
- 17.2 使用wxThread
- 17.3 用于線程同步的對象
- 17.4 多線程的替代方案
- 第十七章小結
- 第十八章使用wxSocket編程
- 18.1 Socket類和功能概覽
- 18.2 Socket及其基本處理介紹
- 18.3 Socket標記
- 18.4 使用Socket流
- 18.5 替代wxSocket
- 第十八章小結
- 第十九章使用文檔/視圖框架
- 19.1 文檔/視圖基礎
- 19.2 文檔/視圖框架的其它能力
- 19.3 實現Undo/Redo的策略
- 第十九章小結
- 第二十章完善你的應用程序
- 20.1 單個實例和多個實例
- 20.2 更改事件處理機制
- 20.3 降低閃爍
- 20.4 實現聯機幫助
- 20.5 解析命令行參數
- 20.6 存儲應用程序資源
- 20.7 調用別的應用程序
- 20.8 管理應用程序設置
- 20.9 應用程序安裝
- 20.10 遵循用戶界面設計規范
- 20.11 全書小結