1.表頭控制 Header Control2.圖像列表控制 Image List3.列表控制和視 List Control&List View4.樹控制和視 Tree Control&Tree View5.標籤控制和視 Tab Control&Tab View6.工具條控制 Toolbar Control7.進度條控制 Progress Control8.滑動條控制 Slider Control9.旋轉按鈕控制 Spin Button Control10.狀態條控制 Status Bar Control其中,絕大部分通用控制在MFC類庫中都存在兩種封裝形式,即控制類和視類,控制類主要是供直接使用控制而提供的,而視類則是通過間接使用控制而提供的,視類可以更好地把控制類集成到MFC結構中,並且使用視類可以利用引用直接對嵌套在其中的控制進行各種操作。兩者在使用方法上沒有太大區別,就拿列表控制類和視類來說,當創建列表視後可通過CListCtrl& ctrlList = GetListCtrl()成員函數取得視類引用ctrlList之後,就可以利用列表視的視函數對視進行各項操作(ctrlList.Add等)。
第1章 演練CHeadCtrl
表頭控制(CHeaderCtrl)通常應用在窗口中的文本或數據的列表之上。一般爲數據列的標題,可以包括多個部分,用戶可以拖動每個部分並可以控制每列的寬度。表頭控制類提供了普通表頭控制的基本方法,只有在WINDOWS95以後版本系統中才提供,其方法包含在afxcmn.h文件中,一般與標籤控制(CTabCtrl)和列表控制(CListCtrl)組合使用。
1.1 表頭控制的對象結構
1.1.1 表頭控制對象的建立方法
CHeaderCtrl &cheaderCtrl 建立表頭控制對象
Create 建立表頭並綁定對象
CHeaderCtrl::Create的格式如下:BOOL Create( DWORD dwStyle, const RECT&
rect, CWnd* pParentWnd, UINT nID );
其返回值非零時初始化成功,否則失敗。
參數dwStyle用來確定表頭控制類型;rect用來確定表頭控制的大小和位置;ParentWnd用來確定表頭控制的父窗口;nID用來表示表頭控制的標誌。
表頭控制風格包括:
HDS_BUTTONS 表示表頭控制外觀類似按鈕;
HDS_HORZ 表示表頭控制爲水平排列;
HDS_VERT 表示表頭控制爲垂直排列;
HDS_HIDDEN 表示表頭控制爲隱藏模式。
它也可以使用普通類控制風格,包括:
CCS_BOTTOM 設置控制位置在父窗口的底部並與父窗口同樣寬度;
CCS_NODIVIDER 在控制頂部形成兩個像素的高亮區;
CCS_NOHILITE 在控制頂部形成一個像素的高亮區;
CCS_NOMOVEY 在響應WM_SIZE消息時重置大小並水平排列;
CCS_NOPARENTALIGN 使控制自動靠近父窗口的頂部或底部;
CCS_NORESIZE 設置初始大小或新值時使控制使用默認寬度和高度;
CCS_TOP 設置在父窗口客戶區域的頂部並與父窗口同樣寬度;
同樣表頭控制也可以使用窗口控制風格,包括:
WS_CHILD 建立一個子窗口,不能用於WS_POPUP窗口類型;
WS_VISIBLE 建立一個初始時不可見的窗口;
WS_DISABLED 建立一個初始時無效的窗口;
WS_GROUP 確定可用光標移動的控制羣組;
WS_TABSTOP 確定可用TAB控制移動站點;
表頭控制一般分爲兩個步驟,首先確定表頭控制的數據結構,然後建立表頭控制並綁定對象。
1.1.2 表頭控制的屬性
表頭控制的屬性包括取得表頭控制中項目的數量GetItemCount、取得表頭控制中某一項目的內容GetItem和設置表頭控制中某一項目的內容SetItem。
1.1.3 表頭控制的操作方法
表頭控制的操作方法包括向表頭控制中插入一個新項目InsertItem、從表頭控制中刪除一個項目DeleteItem和繪製表頭中給定的項目DrawItem等。
1.2 表頭控制的數據結構
在使用表頭控制時,首先必須建立一個數據結構HD_ITEM,其結構定義如下:
typedef struct _HD_ITEM
{ UINT mask; //結構成員有效控制位
int cxy; //表頭項目的寬度
LPSTR pszText; //表頭項目內容
HBITMAP hbm; //表頭項目的位置句柄
int cchTextMax; //表頭內容字符串長度
int fmt; //表頭項目的格式
LPARAM lParam; //應用程序定義的32位數據
} HD_ITEM;
屏蔽控制位說明了數據結構成員中包含的有效數據,可以是下面標誌的組合:
HDI_BITMAP hbm成員有效
HDI_FORMAT fmt 成員有效
HDI_LPARAM lParam成員有效
HDI_TEXT pszText 和cchTextMax 成員有效
HDI_WIDTH cxy 成員有效並確定項目寬度值
格式標誌位fmt可以是以下標誌的組合:
HDF_CENTER 表頭項目居中
HDF_LEFT 表頭項目左對齊
HDF_RIGHT 表頭項目右對齊
HDF_BITMAP 表頭顯示一個位圖
HDF_OWNERDRAW 由主窗口自繪表頭項目
HDF_STRING 表頭項目爲一個字符串
1.3 表頭控制的應用技巧
由於表頭控制無法單獨使用,其主要是配合列表控制和標籤控制,並多以文字表頭應用多見,InsertItem、SetItem和GetItem是常用的方法,如在列表控制時利用InsertColumn屬性就可以增加一個表列的文本標題,具體用法和技巧見列表控制和標籤控制。下面以在列表控制中的增加表列的方法來具體說明:
lvcol.pszText=品 名;//設置第一列名
lvcol.iSubItem=i; //表列寬
m_ListCtrl.InsertColumn(i++,&lvcol);//插入一列
lvcol.pszText=數 量;//第二列名
lvcol.iSubItem=i;
lvcol.cx=70;
m_ListCtrl.InsertColumn(i++,&lvcol);//插入一列
......
第2章 演練CImageList
圖像列表控制(CImageList)是相同大小圖像的一個集合,每個集合中均以0爲圖像的索引序號基數,圖像列表通常由大圖標或位圖構成,其中包含透明位圖模式。可以利用WINDOWS32位應用程序接口函數API來繪製、建立和刪除圖像,並能實現增加、刪除、替換和拖動圖像等操作。圖像列表控制提供了控制圖像列表的基本方法,這些方法在WINDOWS95及以後版本才能實現。
2.1 圖像控制的對象結構
2.1.1 圖像控制的數據成員
m_hImageList 連接圖像對象的控制句柄
2.1.2 圖像控制的建立方法
CimageList&imageList建立圖像控制對象結構
Create 初始化圖像列表並綁定對象
圖像控制的建立方法如下:
BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );
BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2
,int nImage2,int dx, int dy );
其中各項參數的含義爲:cx定義圖像的寬度,單位爲象素;cy定義圖象的高度,單位爲象素;nFlags確定建立圖像列表的類型,可以是以下值的組合: ILC_COLOR、ILC_COLOR4、ILC_COLOR8、ILC_COLOR16、ILC_COLOR24、ILC_COLOR32、 ILC_COLORDDB和ILC_MASK;nInitial用來確定圖像列表包含的圖像數量;nGrow用來確定圖像列表可控制的圖像數量。
NbitmapID 用來確定圖像列表聯繫的位圖標誌值;crMask表示顏色屏蔽位;
LpszBitmapID 用來確定包含位圖資源的標識串;
imagelist1 指向圖像列表控制對象的一個指針;nImage1圖像列表1中包含的圖像數 量;imagelist2指向圖像列表控制對象的一個指針;nImage2圖像列表2中包含的圖像數量;dx表示以象素爲單位的圖像寬度;dy表示以象素爲單位的圖像高度。
同樣,圖像控制的建立也包括兩個步驟,首先建立圖像列表結構,然後建立圖像列表控制。
2.1.3 圖像控制的屬性類
圖像控制的屬性類包括返回m_hImageList.控制句柄GetSafeHandle、取得圖像列表中的圖像數量GetImageCount、設置圖像列表的背景顏色SetBkColor、取得圖像列表的背景顏色SetBkColor和取得圖像的有關信息SetBkColor。
2.1.4 圖像控制的操作方法
圖像控制的操作方法包括將一個圖像列表綁定到一個對象上Attach、將對象上的圖像列表解除綁定並返回句柄Detach、刪除一個圖像列表DeleteImageList、將一個圖像增加到圖像列表中Add和將一個圖像從圖像列表中刪除Remove等。
2.2 圖像控制的應用技巧
對於圖像控制,同樣不能單獨使用,必須與列表控制、樹控制和標籤控制相互結合應用,下面分別介紹其具體應用技巧。
2.2.1 圖像控制在列表控制中的應用技巧
2.2.1.1 設置圖像控制CListCtrl::SetImageList的調用格式如下:
CImageList* SetImageList( CImageList* pImageList, int nImageList );
其返回值是指向前一個圖像列表控制的一個指針,如果不存在前一個圖像列表則爲NULL;其中參數pImageList是指向圖像列表的標識,nImageList是圖像列表的類型,可以是如下值:
LVSIL_NORMAL 用大圖標方式進行圖像列表;
LVSIL_SMALL 用小圖標方式進行圖像列表;
LVSIL_STATE 以圖像狀態進行圖像列表;
2.2.1.2 取得圖像控制CListCtrl::GetImageList的調用格式如下:
CImageList* GetImageList( int nImageList ) const;
其返回值爲指向圖像列表控制的指針,其中nImageList用來確定取得返回值的圖像列表的 值,其取值與設置圖像列表函數相同。
③圖像控制在列表控制中的應用示例
CImageList Cil1,Cil2; //定義大小圖標像列表
CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//取得列表控制程序
Cil1.Create(32,32,TRUE,2,2);//建立32位圖像控制
Cil1.Add(pApp->LoadIcon(IDI_GJ));//增加選中狀態圖像
Cil1.Add(pApp->LoadIcon(IDI_XS));//增加非選中狀態圖像
Cil2.Create(16,16,TRUE,2,2); //建立16位圖像控制
Cil2.Add(pApp->LoadIcon(IDI_GJ));//增加選中狀態圖像
Cil2.Add(pApp->LoadIcon(IDI_XS));//增加非選中狀態圖像
m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL);//設置大圖標控制
m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);//設置小圖標控制
2.2.2 圖像控制在樹控制中的應用技巧
2.2.2.1 設置圖像控制CTreeCtrl::SetImageList的調用格式如下:
CImageList* SetImageList( CImageList * pImageList, int nImageListType );
其返回值爲指向前前一個圖像列表的指針,否則爲NULL;參數pImageList爲指向圖像列表的標識,如果pImageList爲NULL則所有的圖像都將從樹控制中被清除;nImageListType爲圖像列表設置的類型,可以是如下值之一:
TVSIL_NORMAL 設置正常圖像列表,其中包括選中和非選中兩種圖標;
TVSIL_STATE 設置圖像列表狀態,指用戶自定義狀態;
2.2.2.2 取得圖像控制CTreeCtrl::GetImageList的調用格式如下:
CImageList* GetImageList( UINT nImage );
如果調用成功則返回圖像列表控制指針,否則爲NULL;nImage爲取得返回值的圖像列表類型,其取值和取得圖像列表控制完全相同。
2.2.2.3 圖像控制在樹控制中的應用示例
CImageList Cil1,Cil2;//定義大小圖標像列表
CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//獲取應用程序指針
Cil1.Create(16,16,ILC_COLOR,2,2);//建立圖像控制
Cil1.Add(pApp->LoadIcon(IDI_PM));//增加選中狀態圖像
Cil1.Add(pApp->LoadIcon(IDI_CJ));//增加非選中狀態圖像
m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL);//設置圖像控制列表
然後在樹控制的結構定義中進行如下設置:
TCItem.item.iImage=0; //設置未選中圖像索引號
TCItem.item.iSelectedImage=1;//設置選中時圖像引號
2.2.3 圖像控制在標2.2.4 籤控制中的應用技巧
2.2.4.1 設置圖像控制CTabCtrl::SetImageList的調用格式
CImageList * SetImageList( CImageList * pImageList );
其返回值爲指向前一個圖像列表的指針,如果不存在前一個圖像列表則爲NULL;pImageList爲標識TAB控制的圖像列表指針。
2.2.4.2 取得圖像控制CTabCtrl::GetImageList的調用格式
HIMAGELIST GetImageList() const;
其返回值爲指向TAB控制的圖像列表指針,如果調用不成功則爲NULL。
其應用技巧較前兩種更加簡單,這裏不再贅述。
第3章 演練CList
3.1 列表控制的主要功能
列表控制和視(List Control&View)主要用來以各種方式顯示一組數據記錄供用戶進行各種操作,Windows98/95中資源管理器中的“查看”標籤下的 “大圖標|小圖標|列表|詳細資源”就是一個非常好的典型應用。列表中的記錄可以包括多個數據項,也可以包括表示數據內容的大小圖標,用來表示數據記錄的各種屬性。
列表控制提供了對Windows列表功能操作的基本方法,而使用列表視的視函數可以對列表視進行各種操作,通過調用視成員 GetListCtrl獲取嵌在列表視內列表控制的引用(GetListCtrl& ctrlList = GetListCtrl()),就可以和列表控制一樣進行各種操作。操作一個列表控制和視的基本方法爲:創建列表控制;創建列表控制所需要的圖像列表;向列表控制添加表列和表項;對列表進行各種控制,主要包括查找、排序、刪除、顯示方式、排列方式以及各種消息處理功能等;最後撤消列表控制。
對於一個列表控制,其最典型最常用的顯示控制方式爲:大圖標方式(LVS_ICON)、小圖標方式(LVS_SMALLICON)、列表顯示方式(LVS_LIST)和詳細資料(即報告LVS_REPORT)顯示方式。這可以通過設置其顯示方式屬性來實現。要控制列表所在窗口的風格,可通過功能函數GetWindowLong和SetWindowLong來實現,要控制列表圖標的對齊方式,可通過設置列表窗口的風格LVS_ALIGNTOP或 LVS_ALIGNLEFT來實現,
3.2 列表控制的對象結構
3.2.1 列表控制的建立方法
CListCtrl&listCtrl 定義列表對象的結構
Create 建立列表控制並綁定對象
列表控制CListCtrl::Create的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle用來確定列表控制的風格;rect用來確定列表控制的大小和位置;pParentWnd用來確定列表控制的父窗口,通常是一個對話框;nID用來確定列表控制的標識。其中列表控制的風格可以是下列值的組合:
LVS_ALIGNLEFT 用來確定表項的大小圖標以左對齊方式顯示;
LVS_ALIGNTOP 用來確定表項的大小圖標以頂對齊方式顯示;
LVS_AUTOARRANGE 用來確定表項的大小圖標以自動排列方式顯示;
LVS_EDITLABELS 設置表項文本可以編輯,父窗口必須設有LVN_ENDLABELEDIT風格;
LVS_ICON 用來確定大圖標的顯示方式;
LVS_LIST 用來確定列表方式顯示;
LVS_NOCOLUMNHEADER 用來確定在詳細資料方式時不顯示列表頭;
LVS_NOLABELWRAP 用來確定以單行方式顯示圖標的文本項;
LVS_NOSCROLL 用來屏蔽滾動條;
LVS_NOSORTHEADER 用來確定列表頭不能用作按鈕功能;
LVS_OWNERDRAWFIXED 在詳細列表方式時允許自繪窗口;
LVS_REPORT 用來確定以詳細資料即報告方式顯示;
LVS_SHAREIMAGELISTS用來確定共享圖像列表方式;
LVS_SHOWSELALWAYS 用來確定一直顯示被選中表項方式;
LVS_SINGLESEL 用來確定在某一時刻只能有一項被選中;
LVS_SMALLICON 用來確定小圖標顯示方式;
LVS_SORTASCENDING 用來確定表項排序時是基於表項文本的升序方式;
LVS_SORTDESCENDING 用來確定表項排序時是基於表項文本的降序方式;
3.2.2 列表控制的屬性類
列表控制的屬性類包括取得列表控制的背景色GetBkColor、設置列表控制的背景色SetBkColor、取得列表控制的圖像列表 GetImageList、設置列表控制的圖像列表SetImageList、取得列表項數目GetItemCount、取得列表控制的屬性 GetItem、取得與表項相關的數據GetItemData、設置表項的屬性SetItem、設置與表項相關的數值SetItemData、取得相關聯的下一個表項GetNextItem、設置列表控制的文本顏色SetTextColor、取得列表控制的文本背景顏色GetTextBkColor、設置表項的最大數目SetItemCount和取得被選中表項的數目GetSelectedCount等。
3.2.3 列表控制的操作方法
列表控制的操作方法包括插入一個新的表項InsertItem、刪除一個表項DeleteItem、排序表項SortItems、測試列表的位置 HitTest、重繪表項RedrawItems、插入一個表列InsertColumn、刪除一個表列DeleteColumn、編輯一個表項文本 EditLabel和重繪一個表項DrawItem等。
3.3 列表控制的數據結構
列表控制中包含兩個非常重要的數據結構LV_ITEM和LV_COLUMN。LV_ITEM用於定義列表控制的一個表項,LV_COLUMN用於定義列表控制的一個表列,其定義格式分別爲:
typedef struct _LV_ITEM {
UINT mask; //結構成員屏蔽位
int iItem; //表項索引號
int iSubItem; //子表項索引號
UINT state; //表項狀態
UINT stateMask; //狀態有效性屏蔽位
LPTSTR pszText; //表項名文本
int cchTextMax; //表項名最大長度
int iImage; // 表項圖標的索引號
LPARAM lParam; // 與表項相關的32位數
} LV_ITEM;
typedef struct _LV_COLUMN {
UINT mask; //結構成員有效性屏蔽位
int fmt; //表列對齊方式
int cx; //表列的象素寬度
LPTSTR pszText; //表列的表頭名
int cchTextMax; //表列名的文本長度
int iSubItem; //與表列關聯的子表項索引號
} LV_COLUMN;
其中fmt可以取如下值:
LVCFMT_CENTER 表列居中對齊
LVCFMT_LEFT 表列左對齊
3.4 列表控制的應用技巧示例
本文給出具體實例演示列表控制及前面的表頭控制和圖像列表的應用技巧。步驟如下:
1、通過“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名爲VCLIST的工程,在建立過程中選擇基於對話框(Dialog based)的應用;將對話框中的默認控件刪除,並將所有對話框屬性中的Language域設置爲Chinese(P.R.C.),以使應用程序支持中文;
建立兩個圖標IDI_GJ和IDI_XS,用來表示圖標的選中和非選中狀態,對於每個圖標都應建立32X32和16X16兩種大小,以保證程序的需要;
3、在對話框窗口中設計組合框(Group Box),組合框中設置四個無線按鈕(Radio)“大圖標|小圖標|列表|資料”,同時設置排序、刪除和關閉三個控制按鈕(Button),並在對話框中設置大小合適的列表控制(List Ctrl),其對應標識分別如下:
控制名稱 標題名稱 標識符號
列表控制 IDC_LISTCTRL
組合框 方式 IDC_STATIC
無線按鈕 大圖標 IDC_STDICON
小圖標 IDC_SMLICON
列 表 IDC_LIST
資 料 IDC_REPORT
按鈕 排 序 IDC_SORT
刪 除 IDC_DEL
關 閉 IDOK
4、在設置無線按鈕時,需要注意的是隻有大圖標的Group屬性爲選中狀態,而其它無線按鈕的狀態均爲默認值。
5、選中列表控制控件,選擇“VIEW->ClassWizard->Memory Variables”,並利用IDC_ LISTCTRL引入成員變量,其變量類型爲:
變量名 種類 變量類型
m_ListCtrl Control ClistCtrl
同時利用“MESSAGES MAP”爲各無線按鈕和命令按鈕增加控制功能。
6、然後在包含文件和代碼文件中分別加入如下代碼:
(1)在VCLISTDlg.h中增加數據結構和定義
typedef struct tagSPS { //定義結構
char szPm[10]; //品名
int Lx; //0-GJ1-XS
char szSl[10]; //數量
char szDj[10]; //單價
char szJe[10]; //金額
} SPS;
int CALLBACK CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort);
(2)在VCLISTDlg.CPP中的起始處增加初始化數據和程序定義
//在文件開始處增加數據結構初始化
SPS Sps[]={//信息
{紅梅,0,1000,30,30000},
{黃梅,0,1000,29,29000},
{綠梅,0,1000,28,28000},
{青梅,0,1000,27,27000},
{白梅,0,1000,31,31000},
{紅梅,1,1000,30,30000},
{黃梅,1,1000,29,29000},
{綠梅,1,1000,28,28000},
{青梅,1,1000,27,27000},
{白梅,1,1000,31,31000}};
CImageList Cil1,Cil2;//大小圖像列表
(3)在程序初始化處增加表頭、圖像和列表控制建立代碼
BOOL CVCLISTDlg::OnInitDialog()
{CDialog::OnInitDialog();
//其它代碼
// TODO: Add extra initialization here此處增加代碼
LV_ITEM lvitem;
LV_COLUMN lvcol;
int i,iPos,iItemNum;
CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();//創建圖象列表
Cil1.Create(32,32,TRUE,2,2);
Cil1.Add(pApp->LoadIcon(IDI_GJ));
Cil1.Add(pApp->LoadIcon(IDI_XS));
Cil2.Create(16,16,TRUE,2,2);
Cil2.Add(pApp->LoadIcon(IDI_GJ));
Cil2.Add(pApp->LoadIcon(IDI_XS));//設置圖象列表
m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL);
m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);//向列表控制中添加表列
lvcol.mask=LVCF_FMT|LVCF_SUBITEM|LVCF_TEXT|LVCF_WIDTH;
lvcol.fmt=LVCFMT_CENTER;//居中
i=0;
lvcol.pszText=品 名;
lvcol.iSubItem=i;
lvcol.cx=70;
m_ListCtrl.InsertColumn(i++,&lvcol);
lvcol.pszText=數 量;
lvcol.iSubItem=i;
lvcol.cx=70;
m_ListCtrl.InsertColumn(i++,&lvcol);
lvcol.pszText=單 價;
lvcol.iSubItem=i;
lvcol.cx=70;
m_ListCtrl.InsertColumn(i++,&lvcol);
lvcol.pszText=金 額;
lvcol.iSubItem=i;
lvcol.cx=70;
m_ListCtrl.InsertColumn(i++,&lvcol);
//向列表控制中添加表項
iItemNum=sizeof(Sps)/sizeof(SPS);
for(i=0;i
lvitem.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
lvitem.iItem=i;
lvitem.iSubItem=0;
lvitem.pszText=Sps[i].szPm;
lvitem.iImage=Sps[i].Lx;
lvitem.lParam=i;
iPos=m_ListCtrl.InsertItem(&lvitem);//返回表項插入後的索引號
lvitem.mask=LVIF_TEXT;
lvitem.iItem=iPos;
lvitem.iSubItem=1;
lvitem.pszText=Sps[i].szSl;
m_ListCtrl.SetItem(&lvitem);
lvitem.iSubItem=2;
lvitem.pszText=Sps[i].szDj;
m_ListCtrl.SetItem(&lvitem);
lvitem.iSubItem=3;
lvitem.pszText=Sps[i].szJe;
m_ListCtrl.SetItem(&lvitem);
}
CheckRadioButton(IDC_STDICON,IDC_REPORT,IDC_STDICON);
return TRUE; // return TRUE unless you set the focus to a control
}
(4)完善列表顯示方式代碼
在利用Classwizard類嚮導創建各功能按鈕顯示功能函數之後,必須依次完善這些功能函數的代碼,這些功能函數如下:
void CVCLISTDlg::OnStdicon()//設置大圖標顯示方式
{ // TODO: Add your control notification handler code here
LONG lStyle;
lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當前窗口類型
lStyle&=~LVS_TYPEMASK; //清除顯示方式位
lStyle|=LVS_ICON; //設置顯示方式
SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設置窗口類型
}
void CVCLISTDlg::OnSmlicon() { // TODO: Add your control notification handler code here
LONG lStyle;
lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當前窗口類型
lStyle&=~LVS_TYPEMASK; //清除顯示方式位
lStyle|=LVS_SMALLICON; //設置顯示方式
SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設置窗口類型
}
void CVCLISTDlg::OnList() //設置列表顯示方式
{ // TODO: Add your control notification handler code here
LONG lStyle;
lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當前窗口類型
lStyle&=~LVS_TYPEMASK; //清除顯示方式位
lStyle|=LVS_LIST; //設置顯示方式
SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設置窗口類型
}
void CVCLISTDlg::OnReport() //詳細資料顯示方式
{ // TODO: Add your control notification handler code here
LONG lStyle;
lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當前窗口類型
lStyle&=~LVS_TYPEMASK; //清除顯示方式位
lStyle|=LVS_REPORT; //設置顯示方式
SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設置窗口類型
}
(5)刪除功能的實現
要實現刪除功能,必須取得選中表項的數和表項總數,並且需要從後向前進行依次刪除,其原因是每個表項被刪除後,其後各表項的索引號均會發生遞減變化,如果採取從前向後刪除的方法,就會造成無法正常刪除選中的表項,其功能代碼如下:
void CVCLISTDlg::OnDel() //刪除按鈕功能
{ // TODO: Add your control notification handler code here
int i,iState;
int nItemSelected=m_ListCtrl.GetSelectedCount();//所選表項數
int nItemCount=m_ListCtrl.GetItemCount();//表項總數
if(nItemSelected<1) return;
for(i=nItemCount-1;i>=0;i--){
iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED);
if(iState!=0) m_ListCtrl.DeleteItem(i);
}
}
(6)排序功能的實現
列表控制有一個特殊的功能,當以詳細資料方式顯示時,列表頂部的表頭可以當作按鈕來使用,這可以通過列表控制創建時的風格來控制。當鼠標點擊列表頭名稱時,列表控制就會向其父窗口發送一個LNV_COLUMNCLICK消息,利用類導向中列表控制IDC_LISTCTRL對應的 LNV_COLUMNCLICK消息加入相應處理函數,就可將表列按照特定順序進行排列。其函數使用方法見程序,其中iSort爲排序的表列索引號, (PFNLVCOMPARE)CompareFunc爲進行具體排序的回調函數,也就是說,通過鼠標點擊表頭實現的排序過程是由第三方開發的專用排序函數來實現的,排序函數只是實現表項的具體比較操作,而整個排序過程是由SortItemS屬性通過不斷調用這個函數來實現的。正常的排序過程是升序方式,通過調換排序函數中的參數值,就可實現降序排列,即將PARAM1與PARAM2調換位置。這個回調函數的前兩個參數爲表列中表項的索引號,第三個參數爲排序的表列索引號。
void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult)
{ //鼠標左鍵單擊表頭處理函數
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
static int iSorted=-1;//排列序號
if (pNMListView->iSubItem==iSorted) return;
iSorted=pNMListView->iSubItem;
m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,iSorted);
*pResult = 0;
}
//排序時比較表項的回調函數
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,LPARAM lParamSort)
{ char *text1,*text2;
switch (lParamSort){
case 0L:text1=Sps[lParam1].szPm;
text2=Sps[lParam2].szPm;break;
case 1L:text1=Sps[lParam1].szSl;
text2=Sps[lParam2].szSl;break;
case 2L:text1=Sps[lParam1].szDj;
text2=Sps[lParam2].szDj;break;
case 3L:text1=Sps[lParam1].szJe;
text2=Sps[lParam2].szJe;break;
}
return (strcmp(text1,text2));//結果爲>0 =0 <0
}
同樣,也可以通過專用按鈕來實現排序功能,如本文的排序按鈕對應的功能代碼如下:
void CVCLISTDlg::OnSort()
{ // TODO: Add your control notification handler code here
m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);}
7、列表視的演練技巧
在使用列表視時,其方法與列表控制基本相同,只不過列表視是在窗口中來實現的而列表控制是在對話框中實現,列表視的各種功能是通過菜單來實現的而列表控制是通過按鈕等方式來實現的,列表控制需要在對話框中創建列表控制控件而列表視直接佔據整個窗口,在設計過程中只要將按鈕和列表控制設計過程變爲菜單設計,並注意在功能增加是在類嚮導中是通過菜單命令來操作,同時在每個功能函數前面增加取得列表視引用的命令( CListCtrl& ListCtrl = GetListCtrl()),而其餘數據結構和代碼均不需要修改,實現起來比較容易。
筆者實現的列表控制和視程序的運行結果如下:
列表控制演練示例結果
列表視演練示例結果
第4章 演練CTree
4.1 樹控制的主要功能
樹控制和視(Tree Control&View)主要用來顯示具有一定層次結構的數據項,如資源管理器中的磁盤目錄等,以供用戶在其中進行各種選擇。樹控制中的每個數據項包括數據項名稱的文本字符串和用於表示該數據項的圖像,每個數據項下面均可包含各種子項,整個結構就象目錄樹一樣。對於包含各種子項的數據項,可通過鼠標雙擊來展開或合攏,這可以通過控制樹的不同風格來實現樹控制的不同顯示形態。這些風格主要包括:
TVS_HASLINES表示用連線來連接父項和它下面的各個子項,這可以使樹的顯示層次結構更加清晰,但在無父項的各子項之間並沒有連線;
TVS_LINESATROOT表示在無父項的各子項即根下面的各子項之間存在連線;
TVS_HASBUTTONS 表示在帶有子項的父項前面增加一個帶“+”或“-”的按鈕,這使得用戶也可以通過單擊這個小按鈕來實現子項的展開和合攏,當存在子項時,按鈕的初始狀態爲 “+”,當子項被展開時,按小按鈕由“+”變爲“-”號,當子項合攏時,小按鈕由“-”變爲“+”號,這一風格同樣對於根項無效,如果需要可通過組合 TVS_LINESATROOT風格來實現;
TVS_EDITLABELS表示允許讓用戶單擊具有輸入焦點的數據項來修改其名稱。
對於樹控制,MFC中也以兩種形式來封裝,即樹控制(CTREECTRL)和樹視(CTREEVIEW),來滿足用戶的不同需求,對於一般要求的用戶如在對話框中應用,使用樹控制比較方便,而對於具有較高要求的用戶,在使用樹視時還具有視窗口的各種方便特性,可以更好地滿足文檔/視結構的要求。當在窗口中使用樹視時,樹視會佔滿兩個窗口的客戶區域並自動隨窗口的框架結構的調整而調整,並能夠很好地處理諸如菜單、加速鍵和工具條中的各種命令消息。在使用樹視時只要利用其成員函數CtreeView取得其一個引用,就可以象樹控制一樣方便地應用:CtreeCtrl &treeCtrl = GetTreeCtrl()。
4.2 樹控制的對象結構
4.2.1 樹控制的建立方法
CtreeCtrl&treeCtrl 建立樹控制對象結構
Create 建立樹控制並綁定對象
樹控制CTreeCtrl::Create的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle用來確定樹控制的類型;rect用來確定樹控制的大小和位置;pParentWnd用來確定樹控制的父窗口,通用是一個對話框並且不能爲NULL;nID用來確定樹控制的標識。樹控制的風格可以是下列值的組合:
TVS_HASLINES 表示樹控制在各子項之間存在連線;
TVS_LINESATROOT 表示樹控制在根項之間存在連線;
TVS_HASBUTTONS 表示樹控制視在父項左側存在展開合攏控制按鈕;
TVS_EDITLABELS 表示可以控制鼠標單擊修改樹項的名稱;
TVS_SHOWSELALWAYS 表示選中項即使在窗口失去輸入焦點時仍然保持選中狀態;
TVS_DISABLEDRAGDROP表示禁止樹控制發送TVN_BEGINDRAG消息
4.2.2 樹控制的屬性類
樹控制屬性類包括取得樹控制中項數GetCount、取得樹控制中項相對於父項的偏移值GetIndent、取得樹控制圖像列表控制句柄 GetImageList、設置樹控制圖像列表控制句柄SetImageList、取得匹配下一個樹項GetNextItem、判斷給定樹項是否包含子項 ItemHasChildren、取得樹項子項GetChildItem、取得下一個同屬樹項GetNextSiblingItem、取得前一個同屬樹項 GetPrevSiblingItem、取得父樹項GetParentItem、取得第一個可視樹項GetFirstVisibleItem、取得下一個可視樹項GetNextVisible Item、取得前一個可視的樹項GetPrevVisibleItem、取得被選中的樹項GetSelectedItem、取得根樹項 GetRootItem、取得樹項的屬性GetItem、設置樹項的屬性SetItem、取得樹項的狀態GetItemState、設置樹項的狀態 SetItemState、取得與樹項關聯圖像GetItemImage、設置與樹項關聯圖像SetItemImage、取得樹項文本 GetItemText、設置樹項文本SetItemText和取得樹項編輯控制句柄GetEditControl等。
4.2.3 樹控制的操作方法
樹控制的操作方法包括插入一個樹項InsertItem、刪除一個樹項DeleteItem、刪除所有樹項DeleteAllItems、展開或合攏樹項的子項Expand、選中特定樹項SelectItem、選擇一個樹項作爲第一個可視樹項SelectSetFirstVisible、編輯一個可視的樹項 EditLabel和排序給定父樹項的子樹項SortChildren等。
4.3 樹控制的數據結構
在使用樹控制時需要了解兩個個非常重要的數據結構TV_ITEM和TV_INSERTSTRUCT,前一個數據結構是用來表示樹控制的樹項信息,後一個數據結構是用來定義將樹項增加到數據控制中所需要的數據內容。另外,還需要NM_TREEVIEW、TV_DISPINFO和TV_HITTESTINFO三個數據結構,這幾個數據結構的定義方法如下:
4.3.1 基本數據項結構
typedef struct _TV_ITEM {
UINT mask; //結構成員有效性屏蔽位
HTREEITEM hItem; //數據項控制句柄
UINT state; //數據項狀態
UINT stateMask; //狀態有效性屏蔽位
LPSTR pszText; //數據項名稱字符串
int cchTextMax; //數據項名稱的最大長度
int iImage; //數據項圖標索引號
int iSelectedImage;//選中數據項圖標索引號
int cChildren; //子項標識
LPARAM lParam; //程序定義的32位數據
} TV_ITEM, FAR *LPTV_ITEM;
4.3.2 插入樹項結構
typedef struct _TV_INSER TSTRUCT {
HTREEITEM hParent; //父項控制句柄
HTREEITEM hInsertAfter; //插入樹項的位置
TV_ITEM item; //數據項的結構
} TV_INSERTSTRUCT, FAR *LPTV_INSERTSTRUCT;
其中插入的位置如果是TVI_FIRST 或TVI_LAST ,則分別插入到樹控制的最前面或最後面,如果是TVI_SORT ,則插入的樹項自動插入到合適的位置。
4.3.3 樹控制通知消息結構
typedef struct _NM_TREEVIEW {
NMHDR hdr; //通知消息句柄
UINT action; //通知消息標誌
TV_ITEM itemOld; //原來的數據結構
TV_ITEM itemNew; //新的數據結構
POINT ptDrag; //拖動指針
} NM_TREEVIEW;
4.3.4 取得或設置數據結構
typedef struct _TV_DISPINFO { tvdi
NMHDR hdr; //通知消息控制句柄
TV_ITEM item; //數據項結構
} TV_DISPINFO;
4.3.5 指4.3.6 針測試數據結構
typedef struct _TVHITTESTINFO { tvhtst
POINT pt; //客戶區域屏幕座標指針
UINT flags; //存放測試結果的變量
HTREEITEM hItem; //測試的數據項結構
} TV_HITTESTINFO, FAR *LPTV_HITTESTINFO;
其中flags測試結果可以是如下值:
TVHT_ABOVE 在客戶區域上面
TVHT_BELOW 在客戶區域下面
TVHT_NOWHERE 在客戶區域中並在最後一項下面
TVHT_ONITEM 在與樹項關聯的位圖或標籤內
TVHT_ONITEMBUTTON 在與樹項關聯的按鈕上
TVHT_ONITEMICON 在與樹項關聯的位圖上
TVHT_ONITEMINDENT 在與樹項關聯的聯線上
TVHT_ONITEMLABEL 在與樹項關聯的標籤上
TVHT_ONITEMRIGHT 在樹項的右側區域中
TVHT_ONITEMSTATEICON 在用戶定義的狀態圖標上
TVHT_TOLEFT 在客戶區域的左側
TVHT_TORIGHT 在客戶區域的右側
4.4 樹控制的應用技巧示例
這裏仍以基於對話框演示實例來具體介紹樹控制及其和圖像列表相結構的應用技巧:
通過“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名爲VCTREE的工程,在建立過程中選擇基於對話框(Dialog based)的應用;將對話框中的默認控件刪除,並將所有對話框屬性中的Language域設置爲Chinese(P.R.C.),以使應用程序支持中文;建立兩個圖標IDI_PM和IDI_CJ,用來表示圖標的選中和非選中狀態,對於每個圖標都應建立32X32和16X16兩種大小,以保證程序的需要;在對話框窗口中添加樹控制對象(TREE CONTROL),並設置五個按鈕“增加|刪除|查看|排序|關閉”,其對應標識分別如下:
控制名稱 標題名稱 標識符號
樹控制 IDC_TREECTRL
按鈕 增 加 IDC_ADD
刪 除 IDC_DEL
查 看 IDC_VIEW
排 序 IDC_SORT
關 閉 IDOK
5、選中樹控制控件,選擇“VIEW->ClassWizard->Memory Variables。骺刂艻DC_TREECTRL 引入成員變量,其變量類型爲:
變量名 種類 變量類型
m_TreeCtrl Control CTreeCtrl
同時利用“MESSAGES MAP”爲各命令按鈕增加控制功能函數。
6、然後在代碼文件VCTREEDlg.CPP中分別加入如下控制代碼:
(1)在文件開始處增加圖像列表定義
CImageList Cil1,Cil2;//大小圖標像列表
(2)在初始化文件開始處增加代碼
BOOL CVCTREEDlg::OnInitDialog()
{ CDialog::OnInitDialog();
......//原來其它代碼
// TODO: Add extra initialization here
// 此處開始增加代碼
CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//創建圖象列表
Cil1.Create(16,16,ILC_COLOR,2,2);
Cil1.Add(pApp->LoadIcon(IDI_PM));
Cil1.Add(pApp->LoadIcon(IDI_CJ));
m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL); //設置圖象列表
DWORD dwStyles=GetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE);//獲取樹控制原風格
dwStyles|=TVS_EDITLABELS|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;
SetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE,dwStyles);//設置風格
char * CJ[4]={玉溪捲菸廠,雲南捲菸廠,瀋陽捲菸廠,成都捲菸廠};//根數據名稱
char * PM[4][5]={
{紅梅一,紅梅二,紅梅三,紅梅四,紅梅五},//產品數據項
{白梅一,白梅二,白梅三,白梅四,白梅五},
{綠梅一,綠梅二,綠梅三,綠梅四,綠梅五},
{青梅一,青梅二,青梅三,青梅四,青梅五}};
int i,j;
HTREEITEM hRoot,hCur;//樹控制項目句柄
TV_INSERTSTRUCT TCItem;//插入數據項數據結構
TCItem.hParent=TVI_ROOT;//增加根項
TCItem.hInsertAfter=TVI_LAST;//在最後項之後
TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//設屏蔽
TCItem.item.pszText=數據選擇;
TCItem.item.lParam=0;//序號
TCItem.item.iImage=0;//正常圖標
TCItem.item.iSelectedImage=1;//選中時圖標
hRoot=m_TreeCtrl.InsertItem(&TCItem);//返回根項句柄
for(i=0;i<4;i++){//增加各廠家
TCItem.hParent=hRoot;
TCItem.item.pszText=CJ[i];
TCItem.item.lParam=(i+1)*10;//子項序號
hCur=m_TreeCtrl.InsertItem(&TCItem);
for(j=0;j<5;j++){//增加各產品
TCItem.hParent=hCur;
TCItem.item.pszText=PM[i][j];
TCItem.item.lParam=(i+1)*10+(j+1);//子項序號
m_TreeCtrl.InsertItem(&TCItem);
}
m_TreeCtrl.Expand(hCur,TVE_EXPAND);//展開樹
}
m_TreeCtrl.Expand(hRoot,TVE_EXPAND);//展開上一級樹
return TRUE; // return TRUE unless you set the focus to a control
}
(3)增加樹項功能的實現
在增加樹項功能時,除了需要定義和設置插入樹項的數據結構之外,還需要注意的是新增樹項的名稱初始時均爲“新增數據”,增加後允許用戶給數據項設置自定義名稱。在編程時應特別注意m_TreeCtrl.EditLabel(hInsert);後面不能跟任何其它程序命令,否則這條編輯指令無效。
void CVCTREEDlg::OnAdd()
{ //增加子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選擇項句柄
if(hSel==NULL) return;//無任何選項則返回
static int nAddNo=100;//編號大於100爲新增數據
TV_INSERTSTRUCT TCItem;//定義插入項數據結構
TCItem.hParent=hSel; //設置父項句柄
TCItem.hInsertAfter=TVI_LAST;//在最後增加
TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//設屏蔽
TCItem.item.pszText=新增數據;
TCItem.item.lParam=nAddNo++;//索引號增加
TCItem.item.iImage=0;//正常圖標
TCItem.item.iSelectedImage=1;//選中時圖標
HTREEITEM hInsert=m_TreeCtrl.InsertItem(&TCItem);//增加
m_TreeCtrl.Expand(hSel,TVE_EXPAND);
m_TreeCtrl.EditLabel(hInsert);//修改增加的數據
}
(4)刪除樹項功能的實現
在實現刪除功能時,應對存在子項的樹項進行提示,以警告用戶是否連同其子項一起刪除。
void CVCTREEDlg::OnDel()
{ //刪除子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
if(m_TreeCtrl.ItemHasChildren(hSel))//判斷是否有子項
if(MessageBox(廠家下存在品名,一同刪除?,警告,MB_YESNO)==IDNO) return;
m_TreeCtrl.DeleteItem(hSel);
}
(5)排序功能的實現
排序功能是對所選中的樹項的所有子項按字符中順序進行排序,如果想要按照其它規則進行排序,應利用SortChildrenItemBC()函數進行自行開發排序程序,這個自行開發的函數與列表控制中實現的函數基本相同,可興趣的讀可以試驗。
void CVCTREEDlg::OnSort()
{ //排序子項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
m_TreeCtrl.SortChildren(hSel);
}
(6)查看功能的實現
查看功能用來查看選中樹項的有關信息,函數中中顯示了樹項的文本名稱和標識號,可以將這兩個信息作爲查找關鍵字,來查看其它更詳細的信息。
void CVCTREEDlg::OnView()
{ //查看選中項功能函數
HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得選項句柄;
if(hSel==NULL) return;//無任何選項則返回
CString cText=m_TreeCtrl.GetItemText(hSel);//取得數據項名
LONG IDs=m_TreeCtrl.GetItemData(hSel);//取得數據項序號
char temp[100];
wsprintf(temp,廠家:%s 編號:%05d,cText,IDs);
MessageBox(temp,選擇信息);
}
(7)修改功能的實現
如果不進行其它處理,當修改樹項的文本名稱後,就會發現其未被修改,這是因爲程序中沒有對修改結果進行保存處理,這就要利用TV_DISPINFO結構和SetItemText函數對TVN_ENDLABELEDIT進行處理,這樣就可以正確地實現修改功能。
void CVCTREEDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
{ TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
// TODO: Add your control notification handler code here
if(pTVDispInfo->item.pszText==0) return;//用戶取消修改操作
m_TreeCtrl.SetItemText(pTVDispInfo->item.hItem,
pTVDispInfo->item.pszText);//設置新數據
*pResult = 0;
}
7、樹視的演練技巧
樹視的應用技巧在使用樹視時,其方法與樹控制基本相同,只不過樹視是在窗口中來實現的而樹控制是在對話框中實現,樹視的各種功能是通過菜單來實現的而樹控制是通過按鈕等方式來實現的,樹控制需要在對話框中創建樹控制控件而樹視直接佔據整個窗口,在設計過程中只要將按鈕和樹控制設計過程變爲菜單設計,並注意在功能函數是在類嚮導中是通過菜單命令來操作,同時在每個功能函數前面增加取得列表視引用的命令(CTreeCtrl& TreeCtrl = GetTreeCtrl()),而其餘數據結構和代碼均不需要修改,實現起來比較容易。筆者實現的樹控制和視程序的運行結果如下:
樹控制的演練示例結果
樹視演練結果示例
第5章 演練CTab
5.1 標5.2 籤控制的主要功能
標籤控制(Tab Control)是用來在一個窗口如對話框等中的同一用戶區域控制多組顯示信息或控制信息,由頂部的一組標籤來控制不同的信息提示,標籤即可以是文本說明也可以是一個代表文本含義的圖標,或是兩者的組合。針對不同的選擇標籤,都會有一組提示信息或控制信息與之相對應,供用戶進行交互操作,這在 WINDOWS98的屬性表中最常見。另外還存在一種特殊風格的標籤,即TBS_BUTTONS風格的標籤,這種標籤外觀類似按鈕,通過鼠標點擊改變狀態,一般用來執行一些功能而不是用來顯示或控制信息。
提到標籤,最快想到的應該是屬性表對話(Property Sheet),這兩者的配合應用更是隨處可見。屬性表對話框有時也稱爲多頁對話框(Multiple-Page Dialog)或是標籤對話框(Table Dialog),最多可設置24個屬性頁(Property Page),通過頂部的標籤來選擇不同的屬性頁。另外還有一種特殊的屬性表對話框,就象VC++5.0中的類嚮導AppWizard一樣,其不存在供用戶選擇的標籤,而是按照順序依次控制屬性頁的顯示,並且還有一般屬性頁中不存在的“確認”、“上一步”、“下一步”、“完成”和“幫助”等按鈕。
標籤控制在MFC中只存在一種封裝形式,即控制類CtabCtrl。在使用標籤時即可以在對話框中直接添加,也可以在窗口中作爲子窗口來使用,只不過這樣應用時需要選創建標籤。
5.3 標5.4 籤控制的對象結構
5.4.1 標5.4.2 籤控制的建立方法
CTabCtrl&tabCtrl 建立標籤控制對象結構
Create 建立標籤控制並綁定對象
標籤控制CTabCtrl::Create的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle用來確定標籤控制的風格;rect用來控制標籤的大小和位置;pParentWnd用來確定標籤控制的父窗口句柄;nID用來確定標籤控制的標識符。
標籤控制的風格可以是如下值的組合:
TCS_BUTTONS 表示將標籤外觀定義成類似按鈕
TCS_FIXEDWIDTH 使所有標籤具有相同的寬度
TCS_FOCUSNEVER 使特定標籤永遠不接收輸入焦點
TCS_FOCUSONBUTTONDOWN 當標籤被鼠標點擊時接收輸入焦點,其僅與TCS_BUTTONS合用
TCS_FORCEICONLEFT 強制圖標在左面,剩餘部分使標籤居中
TCS_FORCELABELLEFT 使圖標和標籤均左對齊
TCS_MULTILINE 允許標籤控制顯示多行標籤
TCS_OWNERDRAWFIXED 允許父窗口自繪標籤
TCS_RIGHTJUSTIFY 使標籤右對齊
TCS_SHAREIMAGELISTS 當控制被撤消時標籤控制的圖像不被撤消
TCS_TOOLTIPS 允許標籤控制存在工具提示控制
TCS_TABS 標籤正常顯示,爲默認狀態
TCS_SINGLELINE 將標籤只顯示在一行上,默認狀態
TCS_RAGGEDRIGHT 不使標籤自動填滿控制區域,默認狀態
同樣,標籤控制還可以使用窗口的一些控制風格:
WS_CHILD 爲標籤控制創建子窗口,只能與WS_POPUP風格一起使用
WS_VISIBLE 建立一個初始可視的標籤控制
WS_DISABLED 建立一個初始不可視的標籤控制
WS_GROUP 建立標籤控制羣組的第一個控制
WS_TABSTOP 建立可用TAB鍵移動的標籤控制
5.4.3 標5.4.4 籤控制的屬性類
標籤控制的屬性類包括取得與標籤控制相關聯的圖像列表GetImageList、設置標籤控制的圖像列表SetImageList、取得標籤控制中標籤的總數GetItemCount、取得標籤控制中特定標答的相關信息GetItem、設置標籤的部分或全部屬性SetItem、檢測當前被選中的標籤 GetCurSel、將一個標籤設置爲選中狀態SetCurSel和取得具有當前輸入焦點的標籤SetCurSel等。
5.4.5 標5.4.6 籤控制的操作方法
標籤控制的操作方法包括在標籤控制中插入一個標籤InsertItem、刪除一個標籤 DeleteItem、從標籤控制中刪除所有項目DeleteAllItems、從標籤控制中刪除一個圖像列表RemoveImage和繪製標籤控制中的特定一項DrawItem等。
5.5 標5.6 籤控制的數據結構
在使用標籤控制時,必須使用的函數就是在標籤控制中插入標籤。函數InsertItem的原形如下:
BOOL InsertItem(int nItem,TC_ITEM * pTabCtrlItem);
該函數中的TC_ITEM爲添加標籤時所使用信息的數據結構,其數據成員的定義方法及含義如下:
typedef struct _TC_ITEM {
UINT mask; // 確定結構成員的屏蔽或設置位
UINT lpReserved1; // 保留未用
UINT lpReserved2; // 保留未用
LPSTR pszText; // 標籤名稱字符串
int cchTextMax; // 標籤名稱字符串緩衝區大小
int iImage; // 標籤控制的圖像索引號
LPARAM lParam; // 應用程序定義的相關32位數據
} TC_ITEM;
當鼠標點擊標籤控制中的標籤時,標籤控制就會向其父窗口發送相關的通知消息,通過處理這些通知消息,程序可以實現各種功能。
5.7 屬性表和屬性頁的基本用法
在標籤控制過程中,屬性表對話框和屬性頁是必不可少的。在MFC類庫中,屬性表對話框類CpropertySheet是由CWnd類派生而來的,而屬性頁類CpropertyPage是由Cdialog類派生而來的,它們的用法基本相同:
1、創建所有的屬性頁。創建屬性頁的方法與創建一般對話框資源的方法一樣,利用對話框編輯器可以爲每個屬性頁創建一個對話框模板,其區別在於,當利用類嚮導 ClassWizard爲屬性頁生成類時應選擇屬性頁類CpropertyPage作爲基類,而不是將一般的對話框類Cdialog作爲基類;
2、創建屬性表對話框,並將事先創建好的各屬性頁添加進去,兩者的創建順序可以互換,但在創建完之後將屬性頁添加到屬性表對話框中去這一步是必須要做的;
3、顯示屬性表對話框。雖然屬性表對話框類CpropertySheet不是由對話框類Cdialog派生而來的,但兩者的操作非常類似,調用DoModal()函數就會顯示一個模態屬性表對話框,而調用Create()操作就會顯示一個非模態的屬性表對話框;
4、對數據交換的處理。和對話框類似,屬性表對話框與對象之間的數據交換也是通過數據成員2來實現的,只是屬性表本身不帶數據成員,而實際進行數據交換的是屬性頁中的數據成員;
5、對嚮導對話框的處理。如果要顯示一個嚮導對話框,在顯示之前應首先調用SetWizardMode()函數對嚮導對話框進行特殊處理,對於存在按鈕的嚮導對話框,還應調用SetWizardButtons()來對嚮導對話框的按鈕功能進行定製,在用戶操作結束時還應調用SetFinishText()函數將“完成”按鈕設置爲有效狀態。
5.8 標5.9 籤控制的應用技巧示例程序
本文給出一個基於文檔的標籤應用實例。實例程序中通過簡單設置菜單、標籤和屬性表來演示標籤控制的實際應用技巧,程序通過選擇菜單選項彈出設置正文顏色、字體和修飾等屬性表對話框來和用戶進行簡單交互。其實現步驟如下:
1、利用應用程序嚮導AppWizard創建一個基於文檔的工程TAB,在選擇工程類型時應選擇單文檔;
2、利用資源中的菜單生成器,刪除無用菜單,並增加如下菜單結構
菜單名 標識符
設置(S) (彈出菜單名)
背景設置(B) IDM_BKGRND
前景設置(F) IDM_FRGRND
3、利用對話框設計器設置屬性表對話框所需要的四個屬性頁,注意在選擇基類時應將屬性頁類CpropertyPage作爲基類,並將對話框及菜單等控件的所有屬均改爲中文。四個屬性頁及其包括的控件內容分別爲:(1)文字屬性對話框包括一個輸入文字的文本輸入框,用於輸入和修改在窗口上顯示的文字;(2)字體屬性對話框包括三個選中框,用來確定顯示的字體修飾;(3)字間距屬性對話框包括一個用於顯示提示信息的標籤和用於輸入字間距大小的文本輸入框;(4)顏色屬性對話框包括一個成組框和三個單選圓鈕;(5)窗口中設置一個用於顯示輸入文字的標籤。
以上控制的設置參數如下:
控制名稱 標題名稱 標識符串
標籤控制 IDC_TABCTRL
表態文本 字間距(10-100) IDC_STATIC1
編輯框 IDC_LIST
成組框 顏色 IDC_STATIC2
單選按鈕 黑色 IDC_BLACK
紅色 IDC_RED
藍色 IDC_BLUE
文本框(編輯框) IDC_TEXT
設置字體(複選按鈕)粗體 IDC_BOLD
斜體 IDC_ITALIC
下劃線 IDC_UNDERLINE
按 鈕 確認 IDOK
取消 IDCANCEL
利用類嚮導ClassWizard在屬性表對話框CtabDlg、屬性頁對話框CtextPage和CstylePage中分別加入如下數據成員:
標識符串 類型 數據成員
IDC_TABCTRL CtabCtrl m_tabCtrl
IDC_DIST int m_nDist
IDC_BLACK int m_nColor
IDC_TEXT Cstring m_cText
IDC_BOLD BOOL m_bBold
IDC_ITALIC BOOL m_bItalic
IDC_UNDERLINE BOOL m_bUnderline
以上數據成員也可以在TABDlg.h、StylePage.h和TextPage.h中利用手工方法增加。
4、將要顯示的數據成員加入到視類中去,來和對話框之間進行數據交換,並且將其在初始化函數中進行數據初始化。
(1)在TabView.h中增加如下代碼:
#include TabDlg.h
#include TextPage.h
#include StylePage.h
class CTabView : public CView
{public:
int nDist;//數值
int nColor;//顏色
CString cText;//中文字符串
BOOL bBold,bItalic,bUnderline;//字體屬性
}
(2)在TabView.cpp中對數據成員進行如下初始化。
CTabView::CTabView()
{ nDist=20;
nColor=1;
cText=CString(標籤控制演示實例);
bBold=bItalic=bUnderline=FALSE;
}
(3)在TabDlg.cpp中向控制中增加標籤,來實現背景設置功能。
BOOL CTabDlg::OnInitDialog()
{ CDialog::OnInitDialog();
TC_ITEM tcItem;//添加標籤
tcItem.mask=TCIF_TEXT;
tcItem.pszText=字 間 距;
m_tabCtrl.InsertItem(0,&tcItem);
tcItem.pszText=顏色設置;
m_tabCtrl.InsertItem(1,&tcItem);
m_tabCtrl.SetCurSel(1);
return TRUE;
}
當標籤切換時,標籤控制會自動向對話框窗口發送TCN_SELCHANGE通知消息,這時需要根據所選擇的標籤索引號對屬性頁的顯示和隱藏進行切換控制,應完善OnSelchangeTabctrl()函數:
void CTabDlg::OnSelchangeTabctrl(NMHDR* pNMHDR, LRESULT* pResult)
{ int iPage=m_tabCtrl.GetCurSel();//所選標籤號
switch(iPage){
case 0://字間距
GetDlgItem(IDC_STATIC2)->ShowWindow(SW_HIDE);//隱藏選擇按鈕
GetDlgItem(IDC_BLACK)->ShowWindow(SW_HIDE);//隱藏選擇按鈕
GetDlgItem(IDC_RED)->ShowWindow(SW_HIDE);//隱藏選擇按鈕
GetDlgItem(IDC_BLUE)->ShowWindow(SW_HIDE);//隱藏選擇按鈕
GetDlgItem(IDC_STATIC1)->ShowWindow(SW_SHOW);//顯示輸入項數
GetDlgItem(IDC_DIST)->ShowWindow(SW_SHOW);//顯示輸入項數
break;
case 1://顏色設置
GetDlgItem(IDC_STATIC1)->ShowWindow(SW_HIDE);//隱藏項數輸入
GetDlgItem(IDC_DIST)->ShowWindow(SW_HIDE);//隱藏項數輸入
GetDlgItem(IDC_STATIC2)->ShowWindow(SW_SHOW);//顯示選項選擇
GetDlgItem(IDC_BLACK)->ShowWindow(SW_SHOW);//顯示選項選擇
GetDlgItem(IDC_RED)->ShowWindow(SW_SHOW);//顯示選項選擇
GetDlgItem(IDC_BLUE)->ShowWindow(SW_SHOW);//顯示選項選擇
break;
}
*pResult = 0;
}
(4)菜單功能的完善。在執行相應的菜單功能時,必須對類嚮導增加的相應功能函數進行代碼完善,這就要處理TabView.cpp文件,背景設置功能函數如下:
void CTabView::OnBkgrnd()
{ CTabDlg ctd;
ctd.m_nDist=nDist;
ctd.m_nColor=nColor;
if(ctd.DoModal()==IDCANCEL) return;
nDist=ctd.m_nDist;
nColor=ctd.m_nColor;
Invalidate();//重新繪製窗口
}
同樣,也要對前景設置功能函數進行完善:
void CTabView::OnFrgrnd()
{ CPropertySheet cps(前景設置);//創建屬性表對象
CTextPage ctp; //顯示文字屬性頁
CStylePage csp;//顯示字體屬性頁
ctp.m_cText=cText;
csp.m_bBold=bBold;
csp.m_bItalic=bItalic;
csp.m_bUnderline=bUnderline;
cps.AddPage(&ctp);//添加屬性頁
cps.AddPage(&csp);
if(cps.DoModal()==IDCANCEL) return;
cText=ctp.m_cText;
bBold=csp.m_bBold;
bItalic=csp.m_bItalic;
bUnderline=csp.m_bUnderline;
Invalidate();//重新繪製窗口
}
(5)爲了充分演示標籤控制與各屬性頁之間的數據交換功能,應該實現標籤控制各屬性頁與用戶之間數據交換結束後的窗口顯示功能,筆者實現的功能函數顯示了由屬性頁中輸入的字體及背景網格功能,TabView.cpp中的對應函數代碼如下:
void CTabView::OnDraw(CDC* pDC)
{ CTabDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
RECT rc;
GetClientRect(&rc);
int i,j,k;
CPen pen,*pOldPen;
COLORREF color;
switch (nColor){
case 0:color=RGB(0,0,0); //設置黑色
break;
case 1:color=RGB(0xff,0,0);//設置紅色
break;
case 2:color=RGB(0,0,0xff);//設置藍色
break;
}
pen.CreatePen(PS_SOLID,1,color);
pOldPen=pDC->SelectObject(&pen);//繪製背景網格
j=rc.right/nDist+1;
k=rc.bottom/nDist+1;
for(i=0;i
pDC->MoveTo(i*nDist,0);
pDC->LineTo(0,i*nDist);
if(i
pDC->MoveTo(i*nDist,0);
pDC->LineTo(rc.right,(j-i)*nDist);
} else {
pDC->MoveTo(0,(i-j)*nDist);
pDC->LineTo(rc.right,i*nDist);
}
}
pDC->SelectObject(&pOldPen);
CFont font,*pOldFont;
font.CreateFont(50,0,0,0,bBold?1000:200,
bItalic,bUnderline,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,DEFAULT_PITCH,NULL);
pOldFont=pDC->SelectObject(&font);
pDC->TextOut(20,20,cText);
pDC->SelectObject(pOldFont);
}
標籤控制的整個實現過程雖然比較繁鎖,但只要掌握其實現的本質,設計一個優秀的標籤控制界面也並非很困難的事情。
筆者實現的標籤控制的演練示例結果如下:
標籤控制演練示例結果
第6章 演練CToolBar
6.1 工具條控制的主要功能
所謂工具條就是具有位圖和分隔符組成的一組命令按鈕,位圖按鈕部分可以是下推按鈕、檢查盒按鈕、無線按鈕等。工具條對象類派生於主窗口架框類 CframeWnd或CMDIFrameWnd,其類控制CToolBar::GetToolBarCtrl是MFC類庫中封裝的一個成員函數,允許使用類庫中提供的一般控制和附加功能,CtoolBar類控制成員控制提供了Windows一般控制的所有功能,然而,通過調用 GetToolBarCtrl成員函數取得引用後,可以使工具條具有更強的特性。
工具條的創建具有四個步聚:首先是建立工具條資源;然後建立工具條對象結構;其次通過調用建立函數建立工具條對象並綁定;最後調用LoadToolBar調入工具條資源。
另外,還可以通過直接加載位圖的方法來建立,步驟如下:首先建立工具條對象;然後通過調用建立函數建立工具條並綁定對象;其次調入包含按鈕的位圖;最後利用SetButtons 函數設置按鈕的風格並與位圖建立聯繫。
其中,所有按鈕位圖均存放在一個位圖文件中,按鈕位圖的大小相同,默認爲16點寬、15點高,位圖必須從左至右存放。設置按鈕函數具有指向一組控制標識符 ID的指針和索引值,用來確定每個按鈕的位置,如果存在分隔符ID_SEPARATOR, 那麼該圖像就不存在索引值。正常情況下工具條中的按鈕都是單排從左至右排列的,可以通過SetButtonInfo函數改變排序規則。工具條中最終形成的按鈕大小相同,均爲24 x 22 象素,每個按鈕只對象一幅圖像。工具條中的按鈕默認爲下推按鈕,通過設置TBBS_CHECKBOX風格可以實現檢查盒按鈕,通過調用SetRadio成員函數可以實現無線按鈕。
6.2 工具條控制的對象結構
6.2.1 工具條的對象結構
6.2.1.1 工具條的建立方法
CToolBar &ToolBar 建立工具條對象結構
Create 建立工具條對象並綁定
工具條類CToolBar::Create 的調用格式如下:
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,
UINT nID = AFX_IDW_TOOLBAR );
其中參數pParentWnd用來確定指向工具條父窗口的指針;參數dwStyle用來確定工具條的風格,其取值如下;參數nID用來確定工具條子窗口的標識符。
CBRS_TOP 表示工具條在框架窗口的頂部
CBRS_BOTTOM 表示工具條在框架窗口的底部
CBRS_NOALIGN 表示工具條在父窗口改變大小時不響應
CBRS_TOOLTIPS 表示工具條具有動態提示功能
CBRS_SIZE_DYNAMIC 表示工具條是靜態的不能改變
CBRS_SIZE_FIXED 表示工具條是動態的可以改變
CBRS_FLOATING 表示工具條是浮動的
CBRS_FLYBY 表示狀態條上顯示工具條中按鈕的信息
CBRS_HIDE_INPLACE 表示工具條隱藏
除以上函數外,還包括設置按鈕和位圖的大小SetSizes、設置工具條的高度SetHeight、調入工具條資源LoadToolBar、調入工具條按鈕位圖LoadBitmap、設置工具條按鈕位圖SetBitmap、設置工具條中位圖按鈕的風格和索引值SetButtons等控制函數。
6.2.1.2 工具條的類屬性
工具條控制類的屬性包括取得標識符ID對象按鈕索引CommandToIndex、取得索引對應的命令標識符ID或分隔符GetItemID、取得索引對應的矩形區域GetItemRect、取得按鈕風格 GetButtonStyle、設置按鈕風格SetButtonStyle、取得按鈕的ID標識-風格-圖象數GetButtonInfo、設置按鈕ID 標識-風格-圖象數SetButtonInfo、取得按鈕提示文本GetButtonText、設置按鈕提示文本SetButtonText和取得工具條直接存取控制GetToolBarCtrl等。
6.2.2 工具條控制的對象結構
6.2.2.1 工具條控制的建立方法
CToolBarCtrl &ToolBarCtrl 建立工具條控制對象結構
Create 建立工具條控制對象並綁定
工具條控制類CToolBarCtrl::Create的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle用來確定工具條控制的風格,必須存在WS_CHILD風格;參數rect用來確定工具條控制的大小和位置;參數pParentWnd用來確定工具條控制的父窗口指針,不能爲NULL;參數nID用來確定工具條控制的標識符。
可以利用WS_CHILD、WS_VISIBLE和WS_DISABLED來設置工具條窗口的風格,但必須合理設置如下控制風格:
CCS_ADJUSTABLE 允許用戶處理工具條窗口大小,如果存在工具條窗口必須處理相應信
CCS_BOTTOM 使控制處於父窗口客戶區域底部並與窗口同樣寬
CCS_NODIVIDER 禁止在控制的頂部繪製2個象素的高亮條
CCS_NOHILITE 禁止在控制的頂部繪製1個象素的高亮條
CCS_NOMOVEY 使控制改變大小和移動時自動水平對齊,垂直對齊必須處理WM_SIZE消息
如果CCS_NORESIZE風格有效,則該風格無效
CCS_NOPARENTALIGN禁止控制自動移到父窗口頂部或底部,如果CCS_TOP或 CCS_BOTTOM風格
有效,則高度調整爲默認而寬度可以改變
CCS_NORESIZE 禁止設置新的大小或無效值時使用默認寬度和高度值,而使用建立值
CCS_TOP 使控制自動停靠在父窗口客戶區域頂部並與父窗口同樣寬度
最後,還必須利用下面的風格來控制工具條
TBSTYLE_TOOLTIPS 使工具條建立並管理動態提示控制
TBSTYLE_WRAPABLE 使工具條控制按鈕具有多行排列格式
6.2.2.2 工具條控制中的數據結構
工具條控制中最常用的數據結構爲TBBUTTON,其具體結構如下:
typedef struct _TBBUTTON {
int iBitmap; // 基於0的位圖索引值
int idCommand; // 按鈕按下時發送的命令值
BYTE fsState; // 按鈕的狀態
BYTE fsStyle; // 按鈕的風格
DWORD dwData; // 應用程序定義的數據
int iString; // 基於0的按鈕標籤字符串索引值
} TBBUTTON;
其中按鈕狀態fsState的值如下:
TBSTATE_CHECKED 表示按鈕具有TBSTYLE_CHECKED風格並且被按下
TBSTATE_ENABLED 表示按鈕允許接受輸入,否則變灰不接受任何輸入
TBSTATE_HIDDEN 表示按鈕不可見並且不接受任何輸入
TBSTATE_INDETERMINATE 表示按鈕是變灰的
TBSTATE_PRESSED 表示按鈕正被按下
TBSTATE_WRAP 表示按鈕具有換行特性,該按鈕必須具有TBSTATE_ENABLED狀態
按鈕風格style可以是下列值的組合:
TBSTYLE_BUTTON 表示建立標準下推按鈕
TBSTYLE_CHECK 表示建立檢查狀態按鈕
TBSTYLE_CHECKGROUP表示建立檢查按鈕羣
TBSTYLE_GROUP 表示建立按下狀態按鈕羣
TBSTYLE_SEP 表示建立按鈕分隔符
6.2.2.3 工具條控制的類屬性
工具條控制的類屬性必然的聯繫判斷按鈕使能狀態IsButtonEnabled、判斷按鈕檢查狀態 IsButtonChecked、判斷按鈕按下狀態IsButtonPressed、判斷按鈕是否隱藏IsButtonHidden、判斷按鈕變灰狀態 IsButtonIndeterminate、設置按鈕狀態SetState、取得按鈕狀態GetState、取得按鈕有關信息GetButton、取得按鈕總數GetButtonCount、取得按鈕矩形區域GetItemRect、設置按鈕結構大小SetButtonStructSize、設置按鈕大小SetButtonSize、設置按鈕位圖大小SetBitmapSize、取得按鈕提示控制GetToolTips、設置按鈕提示控制 SetToolTips等。
6.2.2.4 工具條控制類的操作方法
工具條控制類的操作方法包括使能按鈕EnableButton、檢查按鈕CheckButton、按下按鈕PressButton、隱藏按鈕HideButton、變灰按鈕Indeterminate、增加按鈕 AddButtons、插入按鈕InsertButton、刪除按鈕DeleteButton、取得控制符ID對應的索引CommandToIndex、恢復工具條狀態RestoreState、保存工具條狀態SaveState和重新確定工具條大小AutoSize等。
6.3 工具條控制的應用技巧
可以這樣說,工具條和上述常用控制是應用程序中不可缺少的功能元素,它的優劣會直接影響程序的基本功能和操作特性。所以這裏將對工具條的建立技巧、狀態保存與恢復、平面特性、停靠位置、排序方法、消息映射、狀態更新、控制使用和屬性控制等方面,全面闡述工具條的使用技巧。
6.3.1 工具條的建立技巧
6.3.1.1 普通工具條的建立方法
如果應用程序在建立時就具有工具條,則只需對工具條中的按鈕圖標進行簡單的增加、修改和刪除等操作就可滿足要求。如果未建立或者想增加其它工具條,則應按步驟追加建立。
首先打開已建立好的基於單文檔的框架工程文件CTool並選擇"Insert->Resource->ToolBar"選項,插入工具條資源並設置資源標識符;然後編輯工具欄中的按鈕圖標和相應的按鈕標識符,並利用類嚮導ClassWizard 爲按鈕消息增加COMMAND和UPDATE_COMMAND_UI兩種處理函數;在資源文件中增加和修改工具條圖標的動態提示等內容;打開 MainFrm.h包含文件在"CToolBar m_wndMainToolBar"後增加"CToolBar m_wndTestToolBar" 等來創建增加的工具條對象;在MainFrm.h 中設置建立函數所需的成員變量,如顏色變量爲m_bColor、動態提示功能變量爲m_bToolTips 等,注意成員變量名與其獲取的參數應完全對應以便使用;最後在MainFrm.cpp中的OnCreate()建立函數中按下述示例規則增加控制代碼,其實現具體步驟如下:
①在MainFrm.h中增加工具條對象控制和成員變量
#define TOOLLEFT 18
class CMainFrame:public CFrameWnd
......//其它代碼
public:
BOOL m_bToolTips;//工具條提示功能
......//其它代碼
protected://工具條控制成員變量
CStatusBar m_wndStatusBar; //框架程序的狀態條
CTestToolBar m_wndMainToolBar;//框架程序的工具條
CTestToolBar m_wndTestToolBar;//新增工具條
CTestToolBar m_wndDockToolBar;//浮動工具條
CTestToolBar m_wndDockNextBar;//浮動工具條
......//其它代碼
}
框架程序中工具條的控制類正常應爲CToolBar,可以是自己設計的派生類CtestToolBar(爲筆者擴充平面特性等功能後的新工具條控制類名)等,具體根據實際需要而定。利用CDialogBar類和CStyleBar 類還可以建立擴展類型的工具條,詳見後面工具條中控制應用技巧,但在該文件頭處必須
包含如下命令:
#ifndef __AFXEXT_H__
#include //直接存取CToolBar和CStatusBar
#endif
②在MainFrm.cpp中完善窗口建立函數
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
WINDOWPLACEMENT wp;//保存主窗口及工具欄窗口位置狀態
if (ReadWindowPlacement(&wp))//讀取位置狀態信息
SetWindowPlacement(&wp); //設置位置狀態信息
m_bToolTips=(AfxGetApp()->GetProfileInt(//讀提示功能
_T("General"),_T("ToolTips"),1)!=0); //默認值爲1
m_wndMainToolBar.SetState(TOOLLEFT,TRUE);//設置初始狀態
EnableDocking(CBRS_ALIGN_ANY);//停靠位置,必須提前位置
if (!m_wndMainToolBar.Create(this,WS_CHILD|WS_VISIBLE
|CBRS_SIZE_DYNAMIC|CBRS_TOP|((m_bToolTips)?
(CBRS_TOOLTIPS|CBRS_FLYBY):0),IDR_MAINFRAME)||
!m_wndMainToolBar.LoadToolBar(IDR_MAINFRAME))
{ //CBRS_SIZE_DYNAMIC爲鎖定位置風格
TRACE0("主工具條MAINFRAME建立失敗/n");
return -1;} // 建立失敗處理
......//建立其它工具條代碼,基本相同
if (!m_wndStatusBar.Create(this)||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{ //建立狀態條
TRACE0("Failed to create status bar/n");
return -1;} // fail to create
m_wndMainToolBar.SetWindowText(_T("主工具欄"));//設置標題
m_wndMainToolBar.EnableDocking(CBRS_ALIGN_ANY);//停靠位置
//m_wndMainToolBar.ModifyStyle(0,TBSTYLE_FLAT);//平面特性
......//設置其它工具條位置代碼,基本相同
DockControlBar(&m_wndMainToolBar,
AFX_IDW_DOCKBAR_TOP);//鎖定位置
DockControlBarLeftOf(&m_wndTestToolBar,
&m_wndMainToolBar);//連接工具條
DockControlBar(&m_wndDockToolBar,AFX_IDW_DOCKBAR_RIGHT);
m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));//恢復列格式,默認爲3
DockControlBarLeftOf(&m_wndDockNextBar,&m_wndDockToolBar);
m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));
LoadBarState(_T("General"));//恢復保存的狀態和位置
return 0;
}
以上建立過程除工具條建立和資源調用函數外,還涉及到了窗口和工具條的狀態保存和恢複函數、註冊表參數讀取函數、工具條停靠位置函數、工具條標題修改函數、工具條連接函數、工具條列格式控制函數和工具條風格修改函數,其中工具條建立函數中的風格設置很重要,如果建立的工具條需要重新設置多行多列的排序功能,除正確設置工具條停靠位置參數外,還必須設置CBRS_SIZE_FIXED 風格,即允許程序改變工具條窗口的尺寸,如果工具條不需要重新排序,則必須設置爲CBRS_SIZE_DYNAMIC 風格,否則工具欄不但不能進行重新排序和正確停靠到理想的位置,而且也無法正確保存和恢復工具條的位置和狀態,這一點應引起編程者高度重視。其餘函數以後分別介紹。
6.3.1.2 浮動工具條的建立方法
如果要建立浮動工具條,必須使用如下工具條的控制方法:
Cpoint pt(GetSystemMetrics(SM_CXSCREEN)-100,GetSystemMetrics(SM_CYSCREEN)/3);
FloatControlBar(&m_wndPaletteBar,pt);//浮動工具條
6.3.1.3 多位圖工具條的建立方法
如果工具條存在多幅按鈕位圖,如單色和彩色等,則必須將工具條按鈕存在在位圖資源文件中而不是工具條資源中,並如下建立:
if(!m_wndDockToolBar.Create(this,WS_CHILD|WS_VISIBLE|
CBRS_SIZE_FIXED|CBRS_TOP|CBRS_TOOLTIPS,ID_PALETTEBAR)||
!m_wndDockToolBar.LoadBitmap(IDR_DOCKTOOLBAR)||
!m_wndDockToolBar.SetButtons(DockTool,
sizeof(DockTool)/sizeof(UINT)))
其中DockTool爲按鈕IDs數據結構,其定義方法如下:
static UINT BASED_CODE DockTool[]=
{ ID_SEPARATOR,
ID_STYLE_LEFT,
ID_STYLE_CENTERED,
ID_STYLE_RIGHT,
ID_STYLE_JUSTIFIED,
};
上述建立過程中的EnableDocking 函數必須放在所有工具條建立函數之前,否則可能出現很難發現的錯誤,如特殊工具條初始位置控制等。工具條的所有特性均在上述建立函數中確定,所以其建立過程是實現理想工具條的關鍵環節。
6.3.2 工具條狀態保存和恢復6.3.3
很多應用程序中都具有保存和恢復應用程序及其工具條等狀態的功能,即下次啓動應用程序後進入上次的運行狀態,這種功能只需進行一次界面佈局便可永久保存,極大方便用戶。
要正確保存和恢復應用程序界面狀態,必須對應用程序窗口和工具條窗口等均進行保存和恢復,這需要完善應用程序的建立和關閉過程。具體步驟如下:
(1)首先利用類嚮導ClassWizard爲應用程序增加窗口關閉WM_CLOSE消息處理功能OnClose();
(2)在MainFrm.cpp中爲應用程序狀態設置成員變量
static TCHAR BASED_CODE szSection[]=_T("Settings");
static TCHAR BASED_CODE szWindowPos[]=_T("WindowPos");
static TCHAR szFormat[]=_T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d");
(3)編制窗口位置狀態讀取和寫入函數
static BOOL PASCAL NEAR ReadWindowPlacement(LPWINDOWPLACEMENT pwp)
{ //窗口位置狀態讀取函數,從INI文件中
CString strBuffer=AfxGetApp()->GetProfileString(szSection,szWindowPos);
if (strBuffer.IsEmpty()) return FALSE;
WINDOWPLACEMENT wp;//窗口位置數據結構
int nRead=_stscanf(strBuffer,szFormat,
&wp.flags,&wp.showCmd,//爲數據結構讀取數值
&wp.ptMinPosition.x,&wp.ptMinPosition.y,
&wp.ptMaxPosition.x,&wp.ptMaxPosition.y,
&wp.rcNormalPosition.left,&wp.rcNormalPosition.top,
&wp.rcNormalPosition.right,&wp.rcNormalPosition.bottom);
if (nRead!=10) return FALSE;
wp.length=sizeof wp;//結構大小
*pwp=wp; //結構指針
return TRUE;
}
static void PASCAL NEAR WriteWindowPlacement(
LPWINDOWPLACEMENT pwp)
{ //窗口位置狀態寫入函數,寫到INI文件
TCHAR szBuffer[sizeof("-32767")*8+sizeof("65535")*2];
wsprintf(szBuffer,szFormat,//將參數值轉換爲字符串
pwp->flags,pwp->showCmd,
pwp->ptMinPosition.x,pwp->ptMinPosition.y,
pwp->ptMaxPosition.x,pwp->ptMaxPosition.y,
pwp->rcNormalPosition.left,pwp->rcNormalPosition.top,
pwp->rcNormalPosition.right,pwp->rcNormalPosition.bottom);
AfxGetApp()->WriteProfileString(szSection,szWindowPos,szBuffer);
}
(4)在應用程序建立函數OnCreate()中增加狀態讀取和設置功能
WINDOWPLACEMENT wp;//保存主窗口及工具條窗口位置狀態
if (ReadWindowPlacement(&wp))//讀取位置狀態信息
SetWindowPlacement(&wp); //設置位置狀態信息
(5)在應用程序建立函數OnCreate()中增加工具條狀態恢復功能
m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));//恢復列格式,默認爲3
m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));
LoadBarState(_T("General"));//恢復保存的狀態和位置
(6)在應用程序關閉函數OnClose()中完善狀態保存功能
void CMainFrame::OnClose()
{ //保存工具條等的狀態
SaveBarState(_T("General"));//保存工具條狀態
AfxGetApp()->WriteProfileInt(_T("General"),//寫入列數
_T("Columns"),m_wndDockToolBar.GetColumns());
AfxGetApp()->WriteProfileInt(_T("General"),
_T("ToolTips"),(m_bToolTips!=0));//寫入提示功能
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
if (GetWindowPlacement(&wp)){
wp.flags=0;
if (IsZoomed()) wp.flags|=WPF_RESTORETOMAXIMIZED;
//如果窗口被放大,則保存爲最大化狀態
WriteWindowPlacement(&wp);
}
CFrameWnd::OnClose();
}
雖然SaveBarState()和LoadBarState()函數保存和恢復了工具條的所有默認位置狀態,但在實際自己實現的功能參數部分並不能被保存,所以應單獨編寫這些參數的保存代碼,如工具欄的排列格式列參數值、顏色狀態標誌和是否存在動態提示功能標誌等,在實際編程時一定要注意。
6.3.4 工具條的平面特性
工具條的平面特性給人耳目一新之感,很多大型應用程序中的工具條都採用這一特性,並取得了巨大成功。利用VC++5中的COMCTL32.DLL動態鏈接庫可以實現平面式工具條,其主要解決問題包括:由於MFC使用風格控制位來控制工具條的外觀,所以在建立工具條時不能直接設置這種風格,必須在建立後利用 SetFlatLookStyle()函數來修改;工具條控制本身也不在各級按鈕之間繪製分隔線,其另一個任務就是截取WM_PAINT消息,並在相應的位置處增加分隔線;工具條控制也不繪製左邊的把手(gripper) ,最後的任務就是調整客戶區域並繪製並繪製相應的gripper。
顯然,實際工作中需要動態鏈接庫COMCTL32.DLL支持的上述方法很不方便。儘管最簡便的方法是利用VC++ 5中的未公開工具欄風格TBSTYLE_FLAT,可以得到工具條的平面特性,只需在工具條建立後簡單地增加一條代碼 "m_WndMainToolBar.ModifyStyle(0,TBSTYLE_FLAT)",但筆者經試驗發現這種方法存在兩個嚴重錯誤:其一是所建立的平面工具條在移動時,不能自動清除移動前的按鈕圖標,使工具條畫面雜亂無章;其二是當建立的平面工具條具有浮動特性時,只要鼠標指針移動到浮動工具條上,整個應用程序窗口就會自動消失。所以第二種方法根本不可行。實現平面工具條的最好方法是在派生類中自己來完成,雖然這一過程比較複雜普通用戶很難做到,但如果存在一個完美的平面工具條控制類,在自己的應用程序中增加相應控制類就是一件很容易的事了。下面是筆者實現完美平面工具條派生類的步驟:
(1)首先利用類嚮導ClassWizard爲工具條控制類派生一個新類CTESTTOOLBAR ,並設置相應的派生類實現文件名。由於新類的基類無法直接選擇CTOOLBAR,所以在選擇新類的基類時先選擇CTOOLBARCTRL爲基類,當派生類生成後再將實現文件中的所有CTOOLBARCTRL類名修改爲CTOOLBAR控制類,並利用ClassWizard 爲新類增加消息WM_PAINT、WM_NCPAINT、WM_MOUSEMOVE、WM_LBUTTONDOWN和WM_LBUTTONUP消息處理功能函數,以便實現新類中平面工具條的各種特性。同時,要在MainFrm.cpp中增加包含文件TestToolBar.h。
(2)完善派生類實現文件TestToolBar.h內容
class CTestToolBar : public CToolBar
{......//其它代碼
public:
CTestToolBar(); //新類構造函數
UINT GetColumns() { return m_nColumns;};//取得列數
void SetState(UINT nLeft,BOOL nStated);//設置列數和狀態
void OnDrawBorder(int index,CDC &dc,int flag);//畫邊框
void OnEraseBorder(int index,CDC &dc);//刪除邊框
void OnDrawBorders();//畫平面特性
void OnDrawSep(int index,CDC &dc);//畫分隔線
void OnDrawGrapper();//畫把手
......//其它代碼
#ifdef _DEBUG //增加插入控制
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: //增加成員變量
UINT m_nColumns; //工具欄按鈕列數
UINT m_nFlags; //鼠標按鍵標誌
int m_nIndex; //按下的按鈕號
int m_nFlagl; //左鍵按下標誌
UINT m_nStated; //工具欄狀態
CRect rt; //關閉按鈕矩形區域
......//其它代碼
}
(3)完善派生類實現文件TestToolBar.cpp內容
......//其它代碼
#define TOOLLEFT 18
#define LBUTTONDOWN 1
#define LBUTTONUP 2
......//其它代碼
CTestToolBar::CTestToolBar()
{ //在構造函數中初始化變量
m_nColumns=0; //工具欄按鈕列數
m_cxLeftBorder=16; //左邊界
m_cxRightBorder=3; //右邊界
m_cyTopBorder=3; //頂邊界
m_cyBottomBorder=3;//底邊界
m_nFlags=0; //按鍵標誌成員變量
m_nIndex=0xffff; //按下的按鈕號
m_nFlagl=0; //左鍵按下標誌
m_nStated=TRUE; //工具欄狀態
}
......//其它代碼
#ifdef _DEBUG//插入代碼完善
void CTestToolBar::AssertValid() const
{ CToolBar::AssertValid(); }
void CTestToolBar::Dump(CDumpContext& dc) const
{ CToolBar::Dump(dc); }
#endif //_DEBUG
......//其它代碼
雖然需要實現的函數比較多,但總起來說不過是取得客戶區域或窗口所有區域的文本設備、建立畫筆和繪圖函數的集合,所以這裏只給出了畫按鈕凸凹邊線的函數,其它函數可仿造實現。
void CTestToolBar::OnDrawBorder(int index,CDC &dc,int flag)
{ //畫按鈕邊線flag=0凸=1凹
CRect rect;
GetItemRect(index,&rect);//取得客戶區域
rect.right--;rect.bottom--;
CPen *oldpen;
UINT color1,color2;
if (flag==0){//兩種狀態的顏色處理
color1=COLOR_BTNHILIGHT;//按鈕高度顏色
color2=COLOR_BTNSHADOW; //按鈕陰影顏色
} else {
color1=COLOR_BTNSHADOW;
color2=COLOR_BTNHILIGHT;
}
CPen pen1(PS_SOLID,1,::GetSysColor(color1));
CPen pen2(PS_SOLID,1,::GetSysColor(color2));
dc.SelectStockObject(NULL_BRUSH);
oldpen=dc.SelectObject(&pen1);
dc.MoveTo(rect.right,rect.top);//畫按鈕邊亮線
dc.LineTo(rect.left,rect.top);
dc.LineTo(rect.left,rect.bottom);
dc.SelectObject(&pen2); //畫按鈕邊暗線
dc.MoveTo(rect.right,rect.top);
dc.LineTo(rect.right,rect.bottom);
dc.LineTo(rect.left,rect.bottom);
dc.SelectStockObject(BLACK_PEN);//畫按鈕邊黑線
dc.MoveTo(rect.right+1,rect.top);
dc.LineTo(rect.right+1,rect.bottom+1);
dc.LineTo(rect.left,rect.bottom+1);
dc.SelectObject(oldpen);
DeleteObject(pen1);
DeleteObject(pen2);
}
void CTestToolBar::OnDrawBorders()
{ //實現平面工具條
CRect rect;
CPoint pt;
GetCursorPos(&pt); //取得鼠標指針
ScreenToClient(&pt);//變成窗口座標
int index;
int count=GetCount();//工具條按鈕總數
CClientDC dc(this); //窗口客戶區域
TBBUTTON button; //按鈕數據結構
CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
OnDrawGrapper(); //畫把手
for(index=0;index
GetItemRect(index,&rect);//取得按鈕矩形區域
rect.left++;rect.top++;
ToolBarCtrl.GetButton(index,&button);//取得按鈕信息
if(button.fsState&(TBSTATE_CHECKED|TBSTATE_HIDDEN))
continue;
if(button.fsStyle&TBSTYLE_SEP){//畫分隔線
if(m_nNew!=0) OnDrawSep(index,dc);
} else if ((m_nIndex==index)||
button.fsState&TBSTATE_PRESSED){//凹按鈕
OnEraseBorder(index,dc);//刪除按鈕邊界
if (rect.PtInRect(pt)) OnDrawBorder(index,dc,1);//繪下凹按鈕
else OnDrawBorder(index,dc,0);//繪凸出按鈕
} else if (!rect.PtInRect(pt)||m_nFlags==LBUTTONUP||
!(button.fsState&TBSTATE_ENABLED)){
OnEraseBorder(index,dc);//刪除按鈕邊界
} else if (m_nFlags!=LBUTTONDOWN){//凸按鈕
OnEraseBorder(index,dc);//刪除按鈕邊界
if(m_nFlagl==0)//鼠標按下防止再次重新出現凸起
OnDrawBorder(index,dc,0);//繪按鈕邊界
}
m_nFlags=0;//按下後移動後不正常凸起
}
ReleaseDC(&dc);
}
void CTestToolBar::OnPaint()
{ //完善重繪按鈕功能
CToolBar::OnPaint();
OnDrawBorders();//處理所有按鈕邊界
}
void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
{ //完善鼠標左鍵按下功能
m_nFlags=LBUTTONDOWN;//設置鼠標按鍵標誌
m_nFlagl=1;
CToolBar::OnLButtonDown(nFlags,point);//調原函數
int index;
int count=GetCount();//工具欄按鈕總數
TBBUTTON button;
CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
for(index=0;index
ToolBarCtrl.GetButton(index,&button);//取得按鈕信息
if (button.fsState&TBSTATE_PRESSED){ //記錄按下按鈕號
m_nIndex=index;
}
}
}
void CTestToolBar::OnLButtonUp(UINT nFlags, CPoint point)
{ //完善鼠標釋放功能
m_nFlags=LBUTTONUP;//設置鼠標按鍵標誌
m_nFlagl=0;
CToolBar::OnLButtonUp(nFlags, point);//調原函數
CRect rect;
CPoint pt;
GetCursorPos(&pt);//取得光標位置
ScreenToClient(&pt);//變成窗口座標
CClientDC dc(this);//窗口客戶區域
if (m_nIndex!=0xffff){//判斷按下按鈕執行功能時仍下凹
GetItemRect(m_nIndex,&rect);//取得矩形區域
rect.left++;rect.top++;
OnEraseBorder(m_nIndex,dc);//刪除按鈕邊界
if (rect.PtInRect(pt)) OnDrawBorder(m_nIndex,dc,1);//繪下凹按鈕
}
m_nIndex=0xffff;
}
void CTestToolBar::OnMouseMove(UINT nFlags, CPoint point)
{ //完善鼠標移動功能
CToolBar::OnMouseMove(nFlags, point);
int index;
int count=GetCount();//工具欄按鈕總數
CRect rect;
if (nFlags&MK_LBUTTON) m_nFlagl=1;//防止再次重新出現凸起
else m_nFlagl=0;
OnDrawBorders();//繪製所有按鈕
for(index=0;index GetItemRect(index,&rect);
rect.left++;rect.top++;
if (rect.PtInRect(point)&&//取得移動過程中輸入焦點
!(GetButtonStyle(index)&TBBS_SEPARATOR)){
SetCapture();//設置鼠標輸入焦點
return;
}
}
if (nFlags&MK_LBUTTON){//防止移出而失去輸入焦點
SetCapture();//設置鼠標輸入焦點
m_nFlagl=1;
return;
} else m_nFlagl=0;
ReleaseCapture();
return;
}
void CTestToolBar::OnNcPaint()
{ //背景重畫函數
CToolBar::OnNcPaint();
OnDrawGrapper();
}
void CTestToolBar::SetState(UINT nLeft,BOOL nStated)
{ //狀態設置函數
m_cxLeftBorder=nLeft;//左邊界
m_nStated=nStated; //工具欄狀態
}
(4)有關派生類函數幾點說明
①畫按鈕凹凸邊線函數OnDrawBorder()
正常工具條中的按鈕具有黑色的邊線,使按鈕凹凸感更強烈,但在平面工具條中的這種按鈕並不美觀,所以應省略黑色邊線部分,並且必須使用系統的API函數 GetSysColor函數來取得邊線顏色,以便系統改變顏色時按鈕邊線也隨之改變,同時由於凹凸按鈕邊線畫法完全相同,只是顏色相反,所以兩者完全可由這個函數來實現;
②畫分隔線函數OnDrawSep()
畫分隔線時應遍歷每個按鈕,來取得分隔線的位置,並且利用客戶區域文本描述表就可實現,只需畫亮暗兩條線就可實現;
③畫把手函數OnDrawGripper()
畫把手時應使用整個窗口的文本描述表,因爲客戶區域描述表不能在窗口的非客戶區域畫線,而且還必須判斷按鈕是否以多行多列方式排列,根據不同的排列方式畫水平或垂直把手,同時還要實現畫關閉按鈕功能,以和VC++5 等界面工具欄功能完全相同,另外還要判斷工具欄是否爲子窗口狀態,以確定是否畫把手和關閉按鈕;
④刪除按鈕邊線函數OnEraseBorder()
函數用於消除系統所繪按鈕凹凸邊線,使按鈕具有平面效果,也必須利用系統的API函數GetSysColor函數來取得系統顏色,以保證系統改變顏色時能夠正常消除按鈕邊線;
⑤實現平面工具欄所有功能函數OnDrawBorders()
在該函數中應特別注意對按鈕分隔符判斷、按鈕凹凸狀態判斷、鼠標左鍵按下後按鈕凹凸狀態判斷、刪除系統所畫按鈕邊線判斷判斷和按下鼠標左鍵並移動鼠標按鈕的凹凸狀態判斷等,並需要利用工具條控制取得對工具欄的引用;
⑥工具條更新功能函數OnPaint()
在這個函數中應注意對原系統更新功能函數的調用,以實現動態提示和按鈕圖標的顯示等功能;
⑦鼠標左鍵按下功能函數OnLButtonDown()
該函數中除需要調用原系統鼠標左鍵按下功能函數,以實現消息的發送等功能外,還需要設置鼠標左鍵按下標誌並記錄按下按鈕的位置,以便程序正確判斷按鈕的凹凸狀態;
⑧鼠標左鍵釋放功能函數OnLButtonDown()
該函數中除需要調用原系統鼠標左鍵釋放功能函數,以實現按鈕執行消息的發送等功能外,還需要設置鼠標左鍵釋放標誌,以便程序正確判斷按鈕的凹凸狀態,此外還應重繪按鈕凹下狀態以使按鈕功能執行時按鈕應處於凹下狀態,來保證工具欄按鈕與其它高級應用程序實現的功能完全相同;
⑨鼠標移動功能函數OnMouseMove()
該函數中應記錄鼠標左鍵按下狀態標誌,並在鼠標移動到按鈕上和鼠標左鍵按下時設置鼠標輸入焦點,來保證平面工具條在鼠標移動過程中的正常凸起狀態和鼠標點擊按鈕後對按鈕狀態的控制,如利用這一點可實現鼠標點擊按鈕後按鈕下凹,不釋放鼠標並移動到任何位置時按鈕凸起,重新移動到按鈕上按鈕仍下凹,這些全是控制鼠標焦點的功能,並及時釋放鼠標輸入焦點;
⑩背景重繪等函數OnNcPaint()
背景重繪函數是用來防止一個工具條被切換顯示狀態後,另一個工具欄中的把手和關閉按鈕等消失,這在鼠標反覆雙擊排序後工具條非客戶區域時,另一個排序後工具欄就會出現的現象;另外函數SetState()用來設置工具條左邊界和狀態,以便爲畫把手和關閉按鈕調整客戶區域並提供繪圖狀態。
此外,還有鼠標移動到把手上光標改變形狀和關閉按鈕功能,由於篇幅所限這裏從略,有興趣的讀者完全可以自己實現。
6.3.5 工具條的停靠位置
6.3.5.1 標6.3.5.2 準工具條的停靠位置
工具條類CToolBar是控制條類CControlBar 的派生類,其顯示的初始停靠位置是通過調用繼承的函數CControlBar::EnableDocking(DWORD dwStyle)來確定的,其參數dwStyle用來指定停靠具體位置,與本文有關的風格如下,其餘請參閱VC5的聯機幫助:
CBRS_ALIGN_TOP 工具條停靠在客戶區域頂部
CBRS_ALIGN_BOTTOM 工具條停靠在客戶區域底部
CBRS_ALIGN_LEFT 工具條停靠在客戶區域左邊
CBRS_ALIGN_RIGHT 工具條停靠在客戶區域右邊
CBRS_ALIGN_ANY 工具條停靠在客戶區域任何位置
利用應用程序嚮導AppWizard 生成的應用程序,其默認的停靠位置爲CBRS_ALIGN_ANY,即允許停靠在客戶區域的任何邊,正常顯示時爲靠近客戶區域的頂部: EnableDocking(CBRS_ALIGN_ANY) ,詳見上述的工具欄建立函數ONCREATE()。
應用程序的單文檔和多文檔的窗口框架類均爲CFrameWnd 的派生類,其指定工具條的停靠位置均是通過調用繼承的函數 CFrameWnd::EnableDocking(DWORD dwDockStyle)來實現的,其可選的參數除上述五種之外,還增加了CBRS_FLOAT_MULTI參數,這個參數主要是爲設計浮動工具條而增加的,其用來確定一個框架窗口中允許存在多個浮動工具欄。同樣利用應用程序嚮導AppWizard 生成的應用程序,其默認的停靠位置也是CBRS_ALIGN_ANY,即允許停靠在框架窗口的任何邊,正常顯示時爲靠近框架窗口的頂部,即爲 EnableDocking(CBRS_ALIGN_ANY),詳見上述的工具條建立函數ONCREATE()。
6.3.5.3 浮動工具條的停靠位置
當一個框架窗口中存在多個浮動工具條時,需要利用函數void DockControlBar(CControlBar *pBar,UINT nDockBarID=0,LPCRECT lpRect= NULL)來確定要控制停靠位置的工具條,它也是CFrameWnd類的成員函數,其中參數pBar用來指向被控制停靠位置的工具條對象,參數 nDockBarID用來確定工具條停靠在框架窗口的哪條邊上,取值爲:
AFX_IDW_DOCKBAR_TOP 工具條停靠在框架窗口的頂部
AFX_IDW_DOCKBAR_BOTTOM 工具條停靠在框架窗口的底部
AFX_IDW_DOCKBAR_LEFT 工具條停靠在框架窗口的左邊
AFX_IDW_DOCKBAR_RIGHT 工具條停靠在框架窗口的右邊
如果參數nDockBarID取值爲0,則工具條可以停靠在框架窗口中的任何一個可停靠的邊上,其默認位置爲頂部。
6.3.5.4 工具條的連接停靠方法
在很多應用程序中都存在將多個工具條同時停靠在某窗口的某一條邊上的同一工具條窗口中的情況,利用上述工具條控制函數DockControlBar的lpRect參數,通過控制工具條的停靠矩形區域來實現這個功能,如筆者實現的函數如下:
①在主程序實現文件MainFrm.h中增加函數定義
public:
void DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf);
②在主程序實現文件MainFrm.cpp中增加如下函數
void CMainFrame::DockControlBarLeftOf(
CToolBar* Bar,CToolBar* LeftOf)
{ //設置工具條停靠在同一邊窗口中
CRect rect;
DWORD dw;
UINT n;
RecalcLayout();//重新顯示
LeftOf->GetWindowRect(&rect);
rect.OffsetRect(1,0);//設置偏移值以停靠在同一窗口中
dw=LeftOf->GetBarStyle();
n=0;
n=(dw&CBRS_ALIGN_TOP)?AFX_IDW_DOCKBAR_TOP:n;
n=(dw&CBRS_ALIGN_BOTTOM&&n==0)?AFX_IDW_DOCKBAR_BOTTOM:n;
n=(dw&CBRS_ALIGN_LEFT&&n==0)?AFX_IDW_DOCKBAR_LEFT:n;
n=(dw&CBRS_ALIGN_RIGHT&&n==0)?AFX_IDW_DOCKBAR_RIGHT:n;
DockControlBar(Bar,n,&rect);
}
在這個函數中應注意對RecalcLayout()函數和OffsetRect()函數的調用,前一個函數用來重新顯示被調整的客戶區和工具條,後一個函數用來重新確定矩形區域,這相當於用鼠標將第二個工具條拖動到前一個工具條上。
③修改應用程序建立函數OnCreate()函數中的相應DockControlBar()函數爲DoctControlBarOf()函數,並正確設置工具條指針,見工具條的建立技巧中的有關函數。
6.3.5.5 定製工具條的頂部停靠控制
另一種工具條的停靠位置是定製工具條的停靠位置,如具有通用控制功能工具條的停靠位置,這主要實現左側定製工具條與頂部工具條之間的位置關係。其實現方法如下:
①打開菜單資源增加頂部位置控制菜單項IDD_DLGBARTOP;
②在實現文件MainFrm.h中增加成員控制變量m_bDialogTop;
BOOL m_bDialogTop;
並在構造函數中爲其設置初始值;
③利用類嚮導函數爲菜單項設置響應函數;
④在實現文件MainFrm.cpp中完善消息映射函數。
void CMainFrame::OnButtonDlgbartop()
{ //定製工具條頂部位置控制函數
if (m_bDialogTop) m_wndDlgBar.SetWindowPos(
&m_wndStatusBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
//其它工具條停靠在最頂部,該工具條停靠其下
else m_wndDlgBar.SetWindowPos(&wndTop,0,0,0,0,
SWP_NOSIZE|SWP_NOMOVE);//停靠在最頂部
RecalcLayout(); //重新顯示窗口
m_bDialogTop=!m_bDialogTop;//改變變量標誌
}
void CMainFrame::OnUpdateButtonDlgbartop(CCmdUI* pCmdUI)
{ //設置菜單項檢查狀態
pCmdUI->SetCheck(m_bDialogTop);
}
6.3.6 工具條按鈕的排序方法
利用應用程序嚮導AppWizard 生成的應用程序工具條,其按鈕均爲單行水平排列的,這在實際程序開發時既不美觀又不實用,很多大型應用程序等界面中的工具條都採用多行多列的排序方式,要在自己的應用程序中實現這種排列方式,應按下述方法在派生類中控制:
(1)在TestToolBar.h中增加函數定義和成員變量控制
class CTestToolBar : public CToolBar
{......//其它代碼
public:
CTestToolBar(); //在構造函數後增加下一行
void SetColumns(UINT nColumns);//增加列控制
......//其它代碼
protected: //增加成員變量
UINT m_nColumns; //工具條列按鈕數
......//其它代碼
}
(2)在TestToolBar.cpp中增加變量初始化和函數
CTestToolBar::CTestToolBar()
{ //在構造函數中初始化變量
m_nColumns=0; //工具條按鈕列數
......//其它代碼
}
void CTestToolBar::SetColumns(UINT nColumns)
{ //設置按鈕排列格式
m_nColumns=nColumns;//列數
int nCount=GetToolBarCtrl().GetButtonCount();//按鈕數
for(int i=0;i
UINT nStyle=GetButtonStyle(i);//按鈕風格
BOOL bWrap=(((i+1)%nColumns)==0);
if(bWrap) nStyle|=TBBS_WRAPPED;//設置換行
else nStyle&=~TBBS_WRAPPED;//不換行
SetButtonStyle(i,nStyle);//設置風格
}
Invalidate();//窗口更新
GetParentFrame()->RecalcLayout();//工具欄狀態更新
}
(3)在應用程序建立函數OnCreate()中爲相應的工具條增加列控制功能,並注意對保存和恢復工具條狀態函數的列控制參數處理,請參閱工具條建立技巧和狀態保存與恢復中的有關函數,重新編譯並執行應用程序就可以看到多行多列的工具條。
6.3.7 工具條的消息映射技巧
如果工具條上的按鈕都存在對應的菜單命令,那麼可以直接利用類嚮導ClassWizard 進行命令消息的映射,否則必須通過手工的方法來進行命令按鈕的消息映射。由於同一工具欄的命令按鈕都存在相類似的功能,所以只要注意將同一工具條中的命令按鈕ID值設置成連續值,就可以利用範圍命令處理機制統一對所有按鈕命令進行管理,即所有命令按鈕的消息可以在同一命令消息處理函數和更新命令消息處理函數中進行管理,這樣不但結構緊湊而且維護特別方便。鑑於類嚮導ClassWizard 不支持範圍命令處理功能,所以其消息映射機制必須通過手工方式來完成。按鈕命令消息既可以在框架窗口中映射,也可以在文檔和視圖中完成映射,由於文檔對象通常用來管理程序中的數據部分,並且在多文檔應用程序(MDI) 中,一個文檔可能有多個視圖與之關聯,因此把工具欄中的命令按鈕消息映射在文檔中比較合適些。其實現步驟如下:
(1)在文檔實現文件CToolDoc.h中增加函數定義和數據成員:
class CCTOOLDoc : public CDocument
{......//其它代碼
protected:
UINT iPosition; //命令按鈕消息位置
......//其它代碼
protected:
//{{AFX_MSG(CCTOOLDoc)
afx_msg void OnTool(UINT nID);//命令消息映射
afx_msg void OnUpdateTool(CCmdUI* pCmdUI);//更新消息映射
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
......//其它代碼
}
(2)在文檔實現文件CToolDoc.cpp中進行消息映射
BEGIN_MESSAGE_MAP(CCTOOLDoc,CDocument)
//{{AFX_MSG_MAP(CCTOOLDoc)
ON_COMMAND_RANGE(ID_BUTTON_LINE,
ID_BUTTON_SORT,OnTool);
ON_UPDATE_COMMAND_UI_RANGE(ID_BUTTON_LINE,
ID_BUTTON_SORT,OnUpdateTool);
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
函數中的參數ID_BUTTONLINE和ID_BUTTONSORT分別爲工具欄中第一個按鈕和最後一個按鈕的消息,如果要增加或減少了按鈕數,則必須使其ID值保持連續,並且需要修改函數中的最後一個消息值。對應函數的代碼如下:
void CtoolDoc::OnTool(UINT nID)
{ //按鈕消息處理函數
iPosition=nID-ID_BUTTON_LINE;
switch(iPosition){
case 1:
......//調用按鈕一的處理功能
case 2:
......//調用按鈕二的處理功能
......
case n:
......//調用按鈕N的處理功能
break;
}
}
void CToolDoc::OnUpdateTool(CCmdUI* pCmdUI)
{ //更新按鈕函數
pCmdUI->SetCheck(iPosition==(pCmdUI->m_nID
-ID_BUTTON_LINE));
}
由於命令按鈕比較多,其處理功能代碼部分這裏省略。
6.3.8 駕馭工具條按鈕的狀態顯示更新
(1)互斥按鈕的狀態更新
很多應用程序中都存在相互排斥的工具條按鈕,如開始和停止按鈕,初始狀態時停止按鈕被禁止,當應用程序進行數據通訊、查找和打印等功能時,停止按鈕有效而開始按鈕禁止,當命令結束或按停止按鈕後,開始按鈕有效而停止按鈕被禁止。這就是工具條中按鈕的顯示狀態更新問題。
與菜單欄一樣,工具條的構造、析構過程及其窗口的創建都是由應用程序的主框架窗口來管理的。當用戶點擊菜單條的菜單項時,Windows 就會嚮應用程序發送WM_INITMENUPOPUP消息,以便應用程序在菜單顯示之前對菜單項進行添加或修改,當利用MFC 類庫編程時只要針對某一具體的菜單項進行消息映射,加入相應的消息處理函數,MFC類庫就會發送更新消息UPDATE_COMMAND_UI,使菜單在彈出時消息處理函數能夠對菜單項進行禁止、變灰和其它處理。工具條的消息處理過程與菜單欄完全相同,它也存在工具條顯示更新消息 UPDATE_COMMAND_UI ,只不是由於工具條總是處於顯示狀態,所以對其消息處理函數的調用是在應用程序的空閒狀態時進行處理的,這點與菜單欄有所不同,當應用程序命令按鈕所要執行的功能佔用大量的處理器資源時,如進行通訊處理或大量數據計算時,應用程序的主框架窗口就無暇進入空閒狀態,於是工具條中的命令按鈕函數就不能被及時調用,按鈕的顯示狀態就不能被及時更新,這就爲工具條按鈕的顯示狀態及時更新帶來一定的困難。
基於文檔的應用程序一般情況下都是在文檔視中對工具條按鈕進行處理,所以要實現按鈕顯示狀態的及時更新,必須在命令按鈕響應函數中利用CToolBarCtrl類的成員函數EnableButton()函數對按鈕的顯示狀態進行必要的處理,其步驟如下:
①首先利用類嚮導ClassWizard 對CCTOOLView類進行開始按鈕和停止按鈕的COMMAND命令消息映射,增加對應消息的處理函數;
②在實現文件CTOOLView.h中增加成員變量:
public:
BOOL m_bContiune;//開始按鈕標誌
BOOL m_bSet;//結束按鈕標誌
③在實現文件CTOOLView.cpp中增加代碼完善按鈕狀態更新:
......//其它代碼
void DrawRectText(RECT &rc,CDC &dc);
......//其它代碼
void CCTOOLView::OnButtonStart()
{ //完善開始按鈕函數功能
CToolBar *pToolBar=(CToolBar *)//取得工具欄對象指針
(GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
//取得工具條控制指針
CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
//控制工具條按鈕狀態
pToolBarCtrl->EnableButton(ID_BUTTON_START,FALSE);
pToolBarCtrl->EnableButton(ID_BUTTON_STOP,TRUE);
CClientDC dc(this);//取得設備文本
MSG msg;//定義消息
RECT rc;
GetClientRect(&rc);//以下畫網格並顯示
m_bContinue=TRUE;
m_bSet=FALSE;
for(int ii=0;ii<2;ii++){
if(m_bContinue==TRUE){
ii=0;//狀態改變退出
DrawRectText(rc,dc);
GetMessage(&msg,NULL,0,0);//檢查消息
TranslateMessage(&msg);//解釋消息
DispatchMessage(&msg);//分配消息
}
}
ReleaseDC(&dc);
m_bSet=TRUE;
}
void DrawRectText(RECT &rc,CDC &dc)
{ //顯示文本函數
int i,j,k,nDist=10;
CPen pen,*pOldPen;
pen.CreatePen(PS_SOLID,1,RGB(180,0,0));
pOldPen=dc.SelectObject(&pen);//繪製背景網格
j=rc.right/nDist+1;
k=rc.bottom/nDist+1;
for(i=0;i
dc.MoveTo(i*nDist,0);
dc.LineTo(0,i*nDist);
if(i
dc.MoveTo(i*nDist,0);
dc.LineTo(rc.right,(j-i)*nDist);
} else {
dc.MoveTo(0,(i-j)*nDist);
dc.LineTo(rc.right,i*nDist);
}
}
dc.SelectObject(pOldPen);
DeleteObject(&pen);
CFont font,*pOldFont;
font.CreateFont(50,50,0,0,1000,//創建字體
1,1,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,DEFAULT_PITCH,NULL);
pOldFont=dc.SelectObject(&font);
dc.TextOut(10,10,"BUTTON");
dc.SelectObject(pOldFont);
DeleteObject(&font);
}
void CCTOOLView::OnButtonStop()
{ //完善停止按鈕函數
m_bContinue=FALSE;
CToolBar *pToolBar=(CToolBar *)//取得工具欄對象指針
(GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
//取得工具條控制指針
CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
//控制工具條按鈕狀態
pToolBarCtrl->EnableButton(ID_BUTTON_START,TRUE);
pToolBarCtrl->EnableButton(ID_BUTTON_STOP,FALSE);
for(int jj=0;jj<2;jj++){
if(m_bSet==FALSE){
jj=0;
GetMessage(&msg1,NULL,0,0);//檢查消息
TranslateMessage(&msg1);//解釋消息
DispatchMessage(&msg1);//分配消息
}
}
}
在開始按鈕函數中,當在屏幕上不斷顯示信息的同時必須利用消息處理函數檢查消息隊列,並解釋和分配相應的消息,否則其它按鈕就無法檢查到應用程序的鼠標等消息,無法實現顯示狀態更新;在更新按鈕顯示狀態前,必須先取得工具欄對象和相應的控制指針,然後才能實現對按鈕狀態的控制,並且要實現按鈕狀態的更新,其設置按鈕的更新功能函數必須最後執行結束纔能有效,即在停止按鈕函數中必須判斷標誌並檢測消息隊列。
④在平面工具條派生類中的鼠標按下函數中增加代碼,以實現鼠標按下開始和停止按鈕後,保證按鈕的正常按下和顯示狀態:
void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
{ //完善鼠標按下函數
......//其它代碼
if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_STOP)){
ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
ID_BUTTON_STOP)&(~TBSTATE_ENABLED&~TBSTATE_PRESSED));
ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.
GetState(ID_BUTTON_START)|TBSTATE_ENABLED);
ToolBarCtrl.Invalidate();
AfxGetApp()->OnIdle(-1);
m_nIndex=0xffff;
}
if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_START)){
ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.GetState(
ID_BUTTON_START)&(~TBSTATE_ENABLED & ~TBSTATE_PRESSED));
ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
ID_BUTTON_STOP)&TBSTATE_ENABLED);
ToolBarCtrl.Invalidate();
AfxGetApp()->OnIdle(-1);
m_nIndex=0xffff;
}
}
(2)按鈕按下狀態的切換更新
另一種按鈕狀態的更新情況是按鈕按下狀態的切換更新,這在繪圖工具條和屏幕字體效果工具條中最常見。其實現步驟如下:
①首先在MainFrm.cpp中手工增加消息範圍映射函數
ON_COMMAND_RANGE(ID_EDIT_TOGGLE,ID_EDIT_UNINDENT,OnButton)
ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_TOGGLE,
ID_EDIT_UNINDENT,OnUpdateButton)
②然後在MainFrm.h中消息映射的最後增加成員函數定義和記錄按下按鈕序號的成員控制變量
int m_bIndex;//按下按鈕序號
afx_msg void OnButton(UINT nID);
afx_msg void OnUpdateButton(CCmdUI* pCmdUI);
③最後分別增加實現消息函數的功能代碼
void CMainFrame::OnUpdateButton(CCmdUI* pCmdUI)
{ //按鈕按下檢查狀態設置函數
pCmdUI->SetCheck((UINT)(ID_EDIT_TOGGLE+
m_bIndex) == pCmdUI->m_nID);
}
void CMainFrame::OnButton(UINT nID)
{ //記錄按下按鈕的序號
m_bIndex=(int)(nID-ID_EDIT_TOGGLE);
}
如果涉及到其它控制的變化,可在這兩個函數中分別增加。
(3)按鈕的部分顯示更新
最後一種更新工具條按鈕顯示狀態的情況是部分按鈕顯示,這需要利用SetButtons()和SetButtonInfo()函數有效處理。步驟如下:
①首先在菜單欄中增加控制菜單項;
②在應用程序實現文件CTool.h中增加如下消息定義和函數定義
......//其它代碼
#define WM_IDLEACTIVATE WM_USER+1
......//其它代碼
class CCtrlbarsApp : public CWinApp
{
public:
......//其它代碼
virtual BOOL OnIdle(LONG lCount);
......//其它代碼
};
③在應用程序實現文件CTool. cpp中增加如下代碼和副本函數功能,這個函數在工具條按鈕的各種顯示狀態更新中都非常有用,是一個非常重要的程序設計技巧。
在初始化函數CCTOOLApp::InitInstance()最後增加代碼:
m_pMainWnd = pMainFrame;
完善相應的功能函數:
BOOL CCTOOLApp::OnIdle(LONG lCount)
{ //保持應用程序一個副本函數
if(lCount == 0){
ASSERT(m_pMainWnd != NULL);
for(HWND hWnd=::GetWindow(m_pMainWnd->m_hWnd,
GW_HWNDFIRST);hWnd!=NULL;hWnd=::GetNextWindow(
hWnd,GW_HWNDNEXT)){//獲取主程序子窗口
if(::GetParent(hWnd)==m_pMainWnd->m_hWnd){
if(GetActiveWindow()==hWnd&&(::GetCapture()==NULL))
m_pMainWnd->SetActiveWindow();//保持應用程序活動窗口
SendMessage(hWnd,WM_IDLEUPDATECMDUI,(WPARAM)TRUE,0L);
}//保持窗口的一個有效副本
}
}
return CWinApp::OnIdle(lCount);
}
④最後在菜單項處理函數中進行工具條按鈕的重組處理,並在需要處理應用程序窗口副本的任何函數中增加副本處理函數。
void CMainFrame::OnViewShort()
{
m_wndMainToolBar.SetButtons(NULL,7);//共計7個
m_wndMainToolBar.SetButtonInfo(0,ID_BUTTON_LINE,TBBS_BUTTON,1);
m_wndMainToolBar.SetButtonInfo(1,ID_BUTTON_CURSE,TBBS_BUTTON,5);
......
m_wndMainToolBar.SetButtonInfo(6,ID_BUTTON_TEST,TBBS_BUTTON,7);
m_wndToolBar.Invalidate();//更新工具條
AfxGetApp()->OnIdle(-1);//窗口副本處理函數
}