Visual C++/MFC入門教程(三)

3.5 利用序列化進行文件讀寫

 

在很多應用中我們需要對數據進行保存,或是從介質上讀取數據,這就涉及到文件的操作。我們可以利用各種文件存取方法完成這些工作,但MFC中也提供了一種讀寫文件的簡單方法——“序列化”。序列化機制通過更高層次的接口功能向開發者提供了更利於使用和透明於字節流的文件操縱方法,舉一個例來講你可以將一個字串寫入文件而不需要理會具體長度,讀出時也是一樣。你甚至可以對字符串數組進行操作。在MFC提供的可自動分配內存的類的支持下你可以更輕鬆的讀/寫數據。你也可以根據需要編寫你自己的具有序列化功能的類。

 

序列化在最低的層次上應該被需要序列化的類支持,也就是說如果你需要對一個類進行序列化,那麼這個類必須支持序列化。當通過序列化進行文件讀寫時你只需要該類的序列化函數就可以了。

 

怎樣使類具有序列化功能呢?你需要以下的工作:

 

該類從CObject派生。

在類聲明中包括DECLARE_SERIAL宏定義。

提供一個缺省的構造函數。

在類中實現Serialze函數

使用IMPLEMENT_SERIAL指明類名和版本號

 

下面的代碼建立了一個簡單身份證記錄的類,同時也能夠支持序列化。

 

in H

struct strPID

{

char szName[10];

char szID[16];

struct strPID* pNext;

};

class CAllPID : public CObject

{

public:

DECLARE_SERIAL(CAllPID)

CAllPID();

~CAllPID();

 

public:// 序列化相關

struct strPID* pHead;

//其他的成員函數

void Serialize(CArchive& ar);

};

 

in CPP

IMPLEMENT_SERIAL(CAllPID,CObject,1) // version is 1,版本用於讀數據時的檢測

void CAllPID::Serialize(CArchive& ar)

{

int iTotal;

if(ar.IsStoring())

{//保存數據

iTotal=GetTotalID();//得到鏈表中的記錄數量

arr<26;i++)

ar<<&(((BYTE*)pItem)+i);//寫一個strPID中所有的數據

}

}

else

{//讀數據

ar>>iTotal;

for(int i=0;i26;j++)

ar>>*(((BYTE*)pID)+j);//讀一個strPID中所有的數據

//修改鏈表

}

}

}

 

當然上面的代碼很不完整,但已經可以說明問題。這樣CAllPID就是一個可以支持序列化的類,並且可以根據記錄的數量動態分配內存。在序列化中我們使用了CArchive類,該類用於在序列化時提供讀寫支持,它重載了<<>>運算符號,並且提供ReadWrite函數對數據進行讀寫。

 

下面看看如何在文檔中使用序列化功能,你只需要修改文檔類的Serialize(CArchive& ar)函數,並調用各個進行序列化的類的Serial進行數據讀寫就可以了。當然你也可以在文檔類的內部進行數據讀寫,下面的代碼利用序列化功能讀寫數據:

 

class CYourDoc : public CDocument

{

void Serialize(CArchive& ar);

CString m_szDesc;

CAllPID m_allPID;

......

}

 

void CYourDoc::Serialize(CArchive& ar)

{

if (ar.IsStoring())

{//由於CStringCArchive定義了<<>>操作符號,所以可以直接利用>><<

ar<>m_szDesc;

}

m_allPID.Serialize(ar);//調用數據類的序列化函數

 

 

}

3.6 MFC中所提供的各種視類介紹

 

MFC中提供了豐富的視類供開發者使用,下面對各個類進行介紹:

 

CView類是最基本的視類只支持最基本的操作。

 

CScrollView類提供了滾動的功能,你可以利用void CScrollView::SetScrollSizes( int nMapMode, SIZE sizeTotal, const SIZE& sizePage = sizeDefault, const SIZE& sizeLine = sizeDefault )設置滾動尺寸,和座標映射模式。但是在繪圖和接收用戶輸入時需要對座標進行轉換。請參見3.2 接收用戶輸入。

 

CFormView類提供用戶在資源文件中定義界面的能力,並可以將子窗口和變量進行綁定。通過UpdateData函數讓數據在變量和子窗口間交換。

 

CTreeView類利用TreeCtrl界面作爲視界面,通過調用CTreeCtrl& CTreeView::GetTreeCtrl( ) const得到CTreeCtrl的引用。

 

CListView類利用ListCtrl界面作爲視界面,通過調用CTreeCtrl& CTreeView::GetTreeCtrl( ) const得到CListCtrl的引用。

 

CEditView類利用Edit接收用戶輸入,它具有輸入框的一切功能。通過調用CEdit& CEditView::GetEditCtrl( ) const得到Edit&的引用。void CEditView::SetPrinterFont( CFont* pFont )可以設置打印字體。

 

CRichEditView類作爲Rich Text Edit(富文本輸入)的視類,提供了可以按照格式顯示文本的能力,在使用時需要CRichEditDoc的支持。

 

4.1 Button

 

按鈕窗口(控件)在MFC中使用CButton表示,CButton包含了三種樣式的按鈕,Push ButtonCheck BoxRadio Box。所以在利用CButton對象生成按鈕窗口時需要指明按鈕的風格。

 

創建按鈕:BOOL CButton::Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );其中lpszCaption是按鈕上顯示的文字,dwStyle爲按鈕風格,除了Windows風格可以使用外(如WS_CHILD|WS_VISUBLE|WS_BORDER)還有按鈕專用的一些風格。

 

BS_AUTOCHECKBOX 檢查框,按鈕的狀態會自動改變 Same as a check box, except that a check mark appears in the check box when the user selects the box; the check mark disappears the next time the user selects the box.

 

 

BS_AUTORADIOBUTTON 圓形選擇按鈕,按鈕的狀態會自動改變 Same as a radio button, except that when the user selects it, the button automatically highlights itself and removes the selection from any other radio buttons with the same style in the same group.

 

 

BS_AUTO3STATE 允許按鈕有三種狀態即:選中,未選中,未定 Same as a three-state check box, except that the box changes its state when the user selects it.

 

 

BS_CHECKBOX 檢查框 Creates a small square that has text displayed to its right (unless this style is combined with the BS_LEFTTEXT style).

 

 

BS_DEFPUSHBUTTON 默認普通按鈕 Creates a button that has a heavy black border. The user can select this button by pressing the ENTER key. This style enables the user to quickly select the most likely option (the default option).

 

 

BS_LEFTTEXT 左對齊文字 When combined with a radio-button or check-box style, the text appears on the left side of the radio button or check box.

 

 

BS_OWNERDRAW 自繪按鈕 Creates an owner-drawn button. The framework calls the DrawItem member function when a visual aspect of the button has changed. This style must be set when using the CBitmapButton class.

 

 

BS_PUSHBUTTON 普通按鈕 Creates a pushbutton that posts a WM_COMMAND message to the owner window when the user selects the button.

 

 

BS_RADIOBUTTON 圓形選擇按鈕 Creates a small circle that has text displayed to its right (unless this style is combined with the BS_LEFTTEXT style). Radio buttons are usually used in groups of related but mutually exclusive choices.

 

 

BS_3STATE 允許按鈕有三種狀態即:選中,未選中,未定 Same as a check box, except that the box can be dimmed as well as checked. The dimmed state typically is used to show that a check box has been disabled.

rect爲窗口所佔據的矩形區域,pParentWnd爲父窗口指針,nID爲該窗口的ID值。

 

獲取/改變按鈕狀態:對於檢查按鈕和圓形按鈕可能有兩種狀態,選中和未選中,如果設置了BS_3STATEBS_AUTO3STATE風格就可能出現第三種狀態:未定,這時按鈕顯示灰色。通過調用int CButton::GetCheck( ) 得到當前是否被選中,返回0:未選中,1:選中,2:未定。調用void CButton::SetCheck( int nCheck );設置當前選中狀態。

 

處理按鈕消息:要處理按鈕消息需要在父窗口中進行消息映射,映射宏爲ON_BN_CLICKED( id, memberFxn )id爲按鈕的ID值,就是創建時指定的nID值。處理函數原型爲afx_msg void memberFxn( );

 

4.2 Static Box

 

靜態文本控件的功能比較簡單,可作爲顯示字符串,圖標,位圖用。創建一個窗口可以使用成員函數:

BOOL CStatic::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );

其中dwStyle將指明該窗口的風格,除了子窗口常用的風格WS_CHILD,WS_VISIBLE外,你可以針對靜態控件指明專門的風格。

 

SS_CENTER,SS_LEFT,SS_RIGHT 指明字符顯示的對齊方式。

SS_GRAYRECT 顯示一個灰色的矩形

SS_NOPREFIX 如果指明該風格,對於字符&將直接顯示,否則&將作爲轉義符,&將不顯示而在其後的字符將有下劃線,如果需要直接顯示&必須使用&&表示。

SS_BITMAP 顯示位圖

SS_ICON 顯示圖標

SS_CENTERIMAGE 圖象居中顯示

 

控制顯示的文本利用成員函數SetWindowText/GetWindowText用於設置/得到當前顯示的文本。

 

控制顯示的圖標利用成員函數SetIcon/GetIcon用於設置/得到當前顯示的圖標。

 

控制顯示的位圖利用成員函數SetBitmap/GetBitmap用於設置/得到當前顯示的位圖。下面一段代碼演示如何創建一個顯示位圖的靜態窗口並設置位圖

 

CStatic* pstaDis=new CStatic;

pstaDis->Create("",WS_CHILD|WS_VISIBLE|SS_BITMAP|SSCENTERIMAGE,

CRect(0,0,40,40),pWnd,1);

CBitmap bmpLoad;

bmpLoad.LoadBitmap(IDB_TEST);

pstaDis->SetBitmap(bmpLoad.Detach());

 

4.3 Edit Box

 

Edit窗口是用來接收用戶輸入最常用的一個控件。創建一個輸入窗口可以使用成員函數:

BOOL CEdit::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );

其中dwStyle將指明該窗口的風格,除了子窗口常用的風格WS_CHILD,WS_VISIBLE外,你可以針對輸入控件指明專門的風格。

 

ES_AUTOHSCROLL,ES_AUTOVSCROLL 指明輸入文字超出顯示範圍時自動滾動。

ES_CENTER,ES_LEFT,ES_RIGHT 指定對齊方式

ES_MULTILINE 是否允許多行輸入

ES_PASSWORD 是否爲密碼輸入框,如果指明該風格則輸入的文字顯示爲*

ES_READONLY 是否爲只讀

ES_UPPERCASE,ES_LOWERCASE 顯示大寫/小寫字符

 

控制顯示的文本利用成員函數SetWindowText/GetWindowText用於設置/得到當前顯示的文本。

 

通過GetLimitText/SetLimitText可以得到/設置在輸入框中輸入的字符數量。

 

由於在輸入時用戶可能選擇某一段文本,所以通過void CEdit::GetSel( int& nStartChar, int& nEndChar )得到用戶選擇的字符範圍,通過調用void CEdit::SetSel( int nStartChar, int nEndChar, BOOL bNoScroll = FALSE )可以設置當前選擇的文本範圍,如果指定nStartChar=0 nEndChar=-1則表示選中所有的文本。void ReplaceSel( LPCTSTR lpszNewText, BOOL bCanUndo = FALSE )可以將選中的文本替換爲指定的文字。

 

此外輸入框還有一些和剪貼板有關的功能,void Clear( );刪除選中的文本,void Copy( );可將選中的文本送入剪貼板,void Paste( );將剪貼板中內容插入到當前輸入框中光標位置,void Cut( );相當於CopyClear結合使用。

 

最後介紹一下輸入框幾種常用的消息映射宏:

 

ON_EN_CHANGE 輸入框中文字更新後產生

ON_EN_ERRSPACE 輸入框無法分配內存時產生

ON_EN_KILLFOCUS / ON_EN_SETFOCUS 在輸入框失去/得到輸入焦點時產生

使用以上幾種消息映射的方法爲定義原型如:afx_msg void memberFxn( );的函數,並且定義形式如ON_Notification( id, memberFxn )的消息映射。如果在對話框中使用輸入框,Class Wizard會自動列出相關的消息,並能自動產生消息映射代碼。

 

4.5 List Box/Check List Box

 

ListBox窗口用來列出一系列的文本,每條文本佔一行。創建一個列表窗口可以使用成員函數:

BOOL CListBox::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );

其中dwStyle將指明該窗口的風格,除了子窗口常用的風格WS_CHILD,WS_VISIBLE外,你可以針對列表控件指明專門的風格。

 

LBS_MULTIPLESEL 指明列表框可以同時選擇多行

LBS_EXTENDEDSEL 可以通過按下Shift/Ctrl鍵選擇多行

LBS_SORT 所有的行按照字母順序進行排序

 

在列表框生成後需要向其中加入或是刪除行,可以利用:

int AddString( LPCTSTR lpszItem )添加行,

int DeleteString( UINT nIndex )刪除指定行,

int InsertString( int nIndex, LPCTSTR lpszItem )將行插入到指定位置。

void ResetContent( )可以刪除列表框中所有行。

通過調用int GetCount( )得到當前列表框中行的數量。

 

如果需要得到/設置當前被選中的行,可以調用int GetCurSel( )/int SetCurSel(int iIndex)。如果你指明瞭選擇多行的風格,你就需要先調用int GetSelCount( )得到被選中的行的數量,然後int GetSelItems( int nMaxItems, LPINT rgIndex )得到所有選中的行,參數rgIndex爲存放被選中行的數組。通過調用int GetLBText( int nIndex, LPTSTR lpszText )得到列表框內指定行的字符串。

 

此外通過調用int FindString( int nStartAfter, LPCTSTR lpszItem )可以在當前所有行中查找指定的字符傳的位置,nStartAfter指明從那一行開始進行查找。

int SelectString( int nStartAfter, LPCTSTR lpszItem )可以選中包含指定字符串的行。

 

MFC 4.2版本中添加了CCheckListBox類,該類是由CListBox派生並擁有CListBox的所有功能,不同的是可以在每行前加上一個檢查框。必須注意的是在創建時必須指明LBS_OWNERDRAWFIXEDLBS_OWNERDRAWVARIABLE風格。

 

通過void SetCheckStyle( UINT nStyle )/UINT GetCheckStyle( )可以設置/得到檢查框的風格,關於檢查框風格可以參考4.1 Button中介紹。通過void SetCheck( int nIndex, int nCheck )/int GetCheck( int nIndex )可以設置和得到某行的檢查狀態,關於檢查框狀態可以參考4.1 Button中介紹。

 

最後介紹一下列表框幾種常用的消息映射宏:

 

ON_LBN_DBLCLK 鼠標雙擊

ON_EN_ERRSPACE 輸入框無法分配內存時產生

ON_EN_KILLFOCUS / ON_EN_SETFOCUS 在輸入框失去/得到輸入焦點時產生

ON_LBN_SELCHANGE 選擇的行發生改變

使用以上幾種消息映射的方法爲定義原型如:afx_msg void memberFxn( );的函數,並且定義形式如ON_Notification( id, memberFxn )的消息映射。如果在對話框中使用列表框,Class Wizard會自動列出相關的消息,並能自動產生消息映射代碼。

 

4.6 Combo Box

 

組合窗口是由一個輸入框和一個列表框組成。創建一個組合窗口可以使用成員函數:

BOOL CListBox::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );

其中dwStyle將指明該窗口的風格,除了子窗口常用的風格WS_CHILD,WS_VISIBLE外,你可以針對列表控件指明專門的風格。

 

CBS_DROPDOWN 下拉式組合框

CBS_DROPDOWNLIST 下拉式組合框,但是輸入框內不能進行輸入

CBS_SIMPLE 輸入框和列表框同時被顯示

LBS_SORT 所有的行按照字母順序進行排序

 

由於組合框內包含了列表框,所以列表框的功能都能夠使用,如可以利用:

int AddString( LPCTSTR lpszItem )添加行,

int DeleteString( UINT nIndex )刪除指定行,

int InsertString( int nIndex, LPCTSTR lpszItem )將行插入到指定位置。

void ResetContent( )可以刪除列表框中所有行。

通過調用int GetCount( )得到當前列表框中行的數量。

 

如果需要得到/設置當前被選中的行的位置,可以調用int GetCurSel( )/int SetCurSel(int iIndex)。通過調用int GetLBText( int nIndex, LPTSTR lpszText )得到列表框內指定行的字符串。

 

此外通過調用int FindString( int nStartAfter, LPCTSTR lpszItem )可以在當前所有行中查找指定的字符傳的位置,nStartAfter指明從那一行開始進行查找。

int SelectString( int nStartAfter, LPCTSTR lpszItem )可以選中包含指定字符串的行。

 

此外輸入框的功能都能夠使用,如可以利用:

DWORD GetEditSel( ) /BOOL SetEditSel( int nStartChar, int nEndChar )得到或設置輸入框中被選中的字符位置。

BOOL LimitText( int nMaxChars )設置輸入框中可輸入的最大字符數。

輸入框的剪貼板功能Copy,Clear,Cut,Paste動可以使用。

 

最後介紹一下列表框幾種常用的消息映射宏:

 

ON_CBN_DBLCLK 鼠標雙擊

ON_CBN_DROPDOWN 列表框被彈出

ON_CBN_KILLFOCUS / ON_CBN_SETFOCUS 在輸入框失去/得到輸入焦點時產生

ON_CBN_SELCHANGE 列表框中選擇的行發生改變

ON_CBN_EDITUPDATE 輸入框中內容被更新

使用以上幾種消息映射的方法爲定義原型如:afx_msg void memberFxn( );的函數,並且定義形式如ON_Notification( id, memberFxn )的消息映射。如果在對話框中使用組合框,Class Wizard會自動列出相關的消息,並能自動產生消息映射代碼。

 

4.7 Tree Ctrl

 

樹形控件TreeCtrl和下節要講的列表控件 ListCtrl在系統中大量被使用,例如Windows資源管理器就是一個典型的例子。

 

樹形控件可以用於樹形的結構,其中有一個根接點(Root)然後下面有許多子結點,而每個子結點上有允許有一個或多個或沒有子結點。MFC中使用CTreeCtrl類來封裝樹形控件的各種操作。通過調用

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );創建一個窗口,dwStyle中可以使用以下一些樹形控件的專用風格:

 

TVS_HASLINES 在父/子結點之間繪製連線

TVS_LINESATROOT 在根/子結點之間繪製連線

TVS_HASBUTTONS 在每一個結點前添加一個按鈕,用於表示當前結點是否已被展開

TVS_EDITLABELS 結點的顯示字符可以被編輯

TVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結點

TVS_DISABLEDRAGDROP 不允許Drag/Drop

TVS_NOTOOLTIPS 不使用ToolTip顯示結點的顯示字符

在樹形控件中每一個結點都有一個句柄(HTREEITEM),同時添加結點時必須提供的參數是該結點的父結點句柄,(其中根Root結點只有一個,既不可以添加也不可以刪除)利用

HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );可以添加一個結點,pszItem爲顯示的字符,hParent代表父結點的句柄,當前添加的結點會排在hInsertAfter表示的結點的後面,返回值爲當前創建的結點的句柄。下面的代碼會建立一個如下形式的樹形結構: +--- Parent1 +--- Child1_1 +--- Child1_2 +--- Child1_3 +--- Parent2 +--- Parent3 /*假設m_tree爲一個CTreeCtrl對象,而且該窗口已經創建*/ HTREEITEM hItem,hSubItem; hItem = m_tree.InsertItem("Parent1",TVI_ROOT); 在根結點上添加Parent1 hSubItem = m_tree.InsertItem("Child1_1",hItem); //Parent1上添加一個子結點 hSubItem = m_tree.InsertItem("Child1_2",hItem,hSubItem);//Parent1上添加一個子結點,排在Child1_1後面 hSubItem = m_tree.InsertItem("Child1_3",hItem,hSubItem); hItem = m_tree.InsertItem("Parent2",TVI_ROOT,hItem); hItem = m_tree.InsertItem("Parent3",TVI_ROOT,hItem); 如果你希望在每個結點前添加一個小圖標,就必需先調用CImageList* SetImageList( CImageList * pImageList, int nImageListType );指明當前所使用的ImageListnImageListTypeTVSIL_NORMAL。在調用完成後控件中使用圖片以設置的ImageList中圖片爲準。然後調用

HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);添加結點,nImage爲結點沒被選中時所使用圖片序號,nSelectedImage爲結點被選中時所使用圖片序號。下面的代碼演示了ImageList的設置。 /*m_list CImageList對象 IDB_TREE 16*(16*4)的位圖,每個圖片爲16*164個圖標*/ m_list.Create(IDB_TREE,16,4,RGB(0,0,0)); m_tree.SetImageList(&m_list,TVSIL_NORMAL); m_tree.InsertItem("Parent1",0,1);//添加, 選中時顯示圖標1,未選中時顯示圖標0

 

此外CTreeCtrl還提供了一些函數用於得到/修改控件的狀態。

HTREEITEM GetSelectedItem( );將返回當前選中的結點的句柄。BOOL SelectItem( HTREEITEM hItem );將選中指明結點。

BOOL GetItemImage( HTREEITEM hItem, int& nImage, int& nSelectedImage ) / BOOL SetItemImage( HTREEITEM hItem, int nImage, int nSelectedImage )用於得到/修改某結點所使用圖標索引。

CString GetItemText( HTREEITEM hItem ) /BOOL SetItemText( HTREEITEM hItem, LPCTSTR lpszItem );用於得到/修改某一結點的顯示字符。

BOOL DeleteItem( HTREEITEM hItem );用於刪除某一結點,BOOL DeleteAllItems( );將刪除所有結點。

 

此外如果想遍歷樹可以使用下面的函數:

HTREEITEM GetRootItem( );得到根結點。

HTREEITEM GetChildItem( HTREEITEM hItem );得到子結點。

HTREEITEM GetPrevSiblingItem/GetNextSiblingItem( HTREEITEM hItem );得到指明結點的上/下一個兄弟結點。

HTREEITEM GetParentItem( HTREEITEM hItem );得到父結點。

 

樹形控件的消息映射使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn )wNotifyCode爲通知代碼,id爲產生該消息的窗口IDmemberFxn爲處理函數,函數的原型如同void OnXXXTree(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR爲一數據結構,在具體使用時需要轉換成其他類型的結構。對於樹形控件可能取值和對應的數據結構爲:

 

TVN_SELCHANGED 在所選中的結點發生改變後發送,所用結構:NMTREEVIEW

TVN_ITEMEXPANDED 在某結點被展開後發送,所用結構:NMTREEVIEW

TVN_BEGINLABELEDIT 在開始編輯結點字符時發送,所用結構:NMTVDISPINFO

TVN_ENDLABELEDIT 在結束編輯結點字符時發送,所用結構:NMTVDISPINFO

TVN_GETDISPINFO 在需要得到某結點信息時發送,(如得到結點的顯示字符)所用結構:NMTVDISPINFO

關於ON_NOTIFY有很多內容,將在以後的內容中進行詳細講解。

 

關於動態提供結點所顯示的字符:首先你在添加結點時需要指明lpszItem參數爲:LPSTR_TEXTCALLBACK。在控件顯示該結點時會通過發送TVN_GETDISPINFO來取得所需要的字符,在處理該消息時先將參數pNMHDR轉換爲LPNMTVDISPINFO,然後填充其中item.pszText。但是我們通過什麼來知道該結點所對應的信息呢,我的做法是在添加結點後設置其lParam參數,然後在提供信息時利用該參數來查找所對應的信息。下面的代碼說明了這種方法: char szOut[8][3]={"No.1","No.2","No.3"}; //添加結點 HTREEITEM hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...) m_tree.SetItemData(hItem, 0 ); hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...) m_tree.SetItemData(hItem, 1 ); //處理消息 void CParentWnd::OnGetDispInfoTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; pTVDI->item.pszText=szOut[pTVDI->item.lParam];//通過lParam得到 需要顯示的字符在數組中的位置 *pResult = 0; }

 

 

關於編輯結點的顯示字符:首先需要設置樹形控件的TVS_EDITLABELS風格,在開始編輯時該控件將會發送TVN_BEGINLABELEDIT,你可以通過在處理函數中返回TRUE來取消接下來的編輯,在編輯完成後會發送TVN_ENDLABELEDIT,在處理該消息時需要將參數pNMHDR轉換爲LPNMTVDISPINFO,然後通過其中的item.pszText得到編輯後的字符,並重置顯示字符。如果編輯在中途中取消該變量爲NULL。下面的代碼說明如何處理這些消息: //處理消息 TVN_BEGINLABELEDIT void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; if(pTVDI->item.lParam==0);//判斷是否取消該操作 *pResult = 1; else *pResult = 0; } //處理消息 TVN_BEGINLABELEDIT void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; if(pTVDI->item.pszText==NULL);//判斷是否已經取消取消編輯 m_tree.SetItemText(pTVDI->item.hItem,pTVDI->pszText);//重置顯示字符 *pResult = 0; } 上面講述的方法所進行的消息映射必須在父窗口中進行(同樣WM_NOTIFY的所有消息都需要在父窗口中處理)。

 

4.8 List Ctrl

 

列表控件可以看作是功能增強的ListBox,它提供了四種風格,而且可以同時顯示一列的多中屬性值。MFC中使用CListCtrl類來封裝列表控件的各種操作。通過調用

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );創建一個窗口,dwStyle中可以使用以下一些列表控件的專用風格:

 

LVS_ICON LVS_SMALLICON LVS_LIST LVS_REPORT 這四種風格決定控件的外觀,同時只可以選擇其中一種,分別對應:大圖標顯示,小圖標顯示,列表顯示,詳細報表顯示

LVS_EDITLABELS 結點的顯示字符可以被編輯,對於報表風格來講可編輯的只爲第一列。

LVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結點

LVS_SINGLESEL 同時只能選中列表中一項

首先你需要設置列表控件所使用的ImageList,如果你使用大圖標顯示風格,你就需要以如下形式調用:

CImageList* SetImageList( CImageList* pImageList, LVSIL_NORMAL);

如果使用其它三種風格顯示而不想顯示圖標你可以不進行任何設置,否則需要以如下形式調用:

CImageList* SetImageList( CImageList* pImageList, LVSIL_SMALL);

 

通過調用int InsertItem( int nItem, LPCTSTR lpszItem );可以在列表控件中nItem指明位置插入一項,lpszItem爲顯示字符。除LVS_REPORT風格外其他三種風格都只需要直接調用InsertItem就可以了,但如果使用報表風格就必須先設置列表控件中的列信息。

 

通過調用int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat , int nWidth, int nSubItem);可以插入列。iCol爲列的位置,從零開始,lpszColumnHeading爲顯示的列名,nFormat爲顯示對齊方式,nWidth爲顯示寬度,nSubItem爲分配給該列的列索引。

 

在有多列的列表控件中就需要爲每一項指明其在每一列中的顯示字符,通過調用

BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );可以設置每列的顯示字符。nItem爲設置的項的位置,nSubItem爲列位置,lpszText爲顯示字符。下面的代碼演示瞭如何設置多列並插入數據: m_list.SetImageList(&m_listSmall,LVSIL_SMALL);//設置ImageList m_list.InsertColumn(0,"Col 1",LVCFMT_LEFT,300,0);//設置列 m_list.InsertColumn(1,"Col 2",LVCFMT_LEFT,300,1); m_list.InsertColumn(2,"Col 3",LVCFMT_LEFT,300,2); m_list.InsertItem(0,"Item 1_1");//插入行 m_list.SetItemText(0,1,"Item 1_2");//設置該行的不同列的顯示字符 m_list.SetItemText(0,2,"Item 1_3");

 

 

此外CListCtrl還提供了一些函數用於得到/修改控件的狀態。

COLORREF GetTextColor( )/BOOL SetTextColor( COLORREF cr );用於得到/設置顯示的字符顏色。

COLORREF GetTextBkColor( )/BOOL SetTextBkColor( COLORREF cr );用於得到/設置顯示的背景顏色。

void SetItemCount( int iCount );用於得到添加進列表中項的數量。

BOOL DeleteItem(int nItem);用於刪除某一項,BOOL DeleteAllItems( );將刪除所有項。

BOOL SetBkImage(HBITMAP hbm, BOOL fTile , int xOffsetPercent, int yOffsetPercent);用於設置背景位圖。

CString GetItemText( int nItem, int nSubItem );用於得到某項的顯示字符。

 

列表控件的消息映射同樣使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn )wNotifyCode爲通知代碼,id爲產生該消息的窗口IDmemberFxn爲處理函數,函數的原型如同void OnXXXList(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR爲一數據結構,在具體使用時需要轉換成其他類型的結構。對於列表控件可能取值和對應的數據結構爲:

 

LVN_BEGINLABELEDIT 在開始某項編輯字符時發送,所用結構:NMLVDISPINFO

LVN_ENDLABELEDIT 在結束某項編輯字符時發送,所用結構:NMLVDISPINFO

LVN_GETDISPINFO 在需要得到某項信息時發送,(如得到某項的顯示字符)所用結構:NMLVDISPINFO

關於ON_NOTIFY有很多內容,將在以後的內容中進行詳細講解。

 

關於動態提供結點所顯示的字符:首先你在項時需要指明lpszItem參數爲:LPSTR_TEXTCALLBACK。在控件顯示該結點時會通過發送TVN_GETDISPINFO來取得所需要的字符,在處理該消息時先將參數pNMHDR轉換爲LPNMLVDISPINFO,然後填充其中item.pszText。通過item中的iItem,iSubItem可以知道當前顯示的爲那一項。下面的代碼演示了這種方法: char szOut[8][3]={"No.1","No.2","No.3"}; //添加結點 m_list.InsertItem(LPSTR_TEXTCALLBACK,...) m_list.InsertItem(LPSTR_TEXTCALLBACK,...) //處理消息 void CParentWnd::OnGetDispInfoList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR; pLVDI->item.pszText=szOut[pTVDI->item.iItem];//通過iItem得到需要 顯示的字符在數組中的位置 *pResult = 0; }

 

 

關於編輯某項的顯示字符:(在報表風格中只對第一列有效)首先需要設置列表控件的LVS_EDITLABELS風格,在開始編輯時該控件將會發送LVN_BEGINLABELEDIT,你可以通過在處理函數中返回TRUE來取消接下來的編輯,在編輯完成後會發送LVN_ENDLABELEDIT,在處理該消息時需要將參數pNMHDR轉換爲LPNMLVDISPINFO,然後通過其中的item.pszText得到編輯後的字符,並重置顯示字符。如果編輯在中途中取消該變量爲NULL。下面的代碼說明如何處理這些消息: //處理消息 LVN_BEGINLABELEDIT void CParentWnd::OnBeginEditList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR; if(pLVDI->item.iItem==0);//判斷是否取消該操作 *pResult = 1; else *pResult = 0; } //處理消息 LVN_BEGINLABELEDIT void CParentWnd::OnBeginEditList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR; if(pLVDI->item.pszText==NULL);//判斷是否已經取消取消編輯 m_list.SetItemText(pLVDI->item.iItem,0,pLVDI->pszText);//重置顯示字符 *pResult = 0; } 上面講述的方法所進行的消息映射必須在父窗口中進行(同樣WM_NOTIFY的所有消息都需要在父窗口中處理)。

 

 

如何得到當前選中項位置:在列表控件中沒有一個類似於ListBoxGetCurSel()的函數,但是可以通過調用GetNextItem( -1, LVNI_ALL | LVNI_SELECTED);得到選中項位置。

 

4.9 Tab Ctrl

 

Tab屬性頁控件可以在一個窗口中添加不同的頁面,然後在頁選擇發生改變時得到通知。MFC中使用CTabCtrl類來封裝屬性頁控件的各種操作。通過調用

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );創建一個窗口,dwStyle中可以使用以下一些屬性頁控件的專用風格:

 

TCS_BUTTONS 使用按鈕來表示頁選擇位置

TCS_MULTILINE 分行顯示頁選擇位置

TCS_SINGLELINE 只使用一行顯示頁選擇位置

在控件創建後必需向其中添加頁面纔可以使用,添加頁面的函數爲:

BOOL InsertItem( int nItem, LPCTSTR lpszItem );nItem爲位置,從零開始,lpszItem爲頁選擇位置上顯示的文字。如果你希望在頁選擇位置處顯示一個圖標,你可以調用

BOOL InsertItem( int nItem, LPCTSTR lpszItem, int nImage );nImage指明所使用的圖片位置。(在此之前必須調用CImageList * SetImageList( CImageList * pImageList );設置正確的ImageList

 

此外CTabCtrl還提供了一些函數用於得到/修改控件的狀態。

int GetCurSel( )/int SetCurSel( int nItem );用於得到/設置當前被選中的頁位置。

BOOL DeleteItem( int nItem )/BOOL DeleteAllItems( );用於刪除指定/所有頁面。

void RemoveImage( int nImage );用於刪除某頁選擇位置上的圖標。

 

屬性頁控件的消息映射同樣使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn )wNotifyCode爲通知代碼,id爲產生該消息的窗口IDmemberFxn爲處理函數,函數的原型如同void OnXXXTab(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR爲一數據結構,在具體使用時需要轉換成其他類型的結構。對於列表控件可能取值和對應的數據結構爲:

 

TCN_SELCHANGE 在當前頁改變後發送,所用結構:NMHDR

TCN_SELCHANGING 在當前頁改變時發送可以通過返回TRUE來禁止頁面的改變,所用結構:NMHDR

 

一般來講在當前頁發生改變時需要隱藏當前的一些子窗口,並顯示其它的子窗口。下面的僞代碼演示瞭如何使用屬性頁控件:

 

CParentWnd::OnCreate(...)

{

m_tab.Create(...);

m_tab.InsertItem(0,"Option 1");

m_tab.InsertItem(1,"Option 2");

Create a edit box as the m_tab's Child

Create a static box as the m_tab's Child

edit_box.ShowWindow(SW_SHOW); // edit box在屬性頁的第一頁

static_box.ShowWindow(SW_HIDE); // static box在屬性頁的第二頁

}

void CParentWnd::OnSelectChangeTab(NMHDR* pNMHDR, LRESULT* pResult)

{//處理頁選擇改變後的消息

if(m_tab.GetCurSel()==0)

{//根據當前頁顯示/隱藏不同的子窗口

edit_box.ShowWindow(SW_SHOW);

static_box.ShowWindow(SW_HIDE);

}

else

{//

edit_box.ShowWindow(SW_HIDE);

static_box.ShowWindow(SW_SHOW);

}

}

 

發佈了8 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章