MFC下,提供了List Control控件,當選擇Report模式時,可以方便的做數據報表之類的應用。類似下圖:
![MFC-ListCtrl 可編輯重寫 - ″x.悶、 - 半邊天 MFC-ListCtrl 可編輯重寫 - ″x.悶、 - 半邊天]()
但是有個不大不小的問題是,當List Control選擇可編輯模式時,只有每一行的第一列的單元格才能編輯,而且在默認情況下,當選中的時候,也只有被選中的這一行的第一個單元格纔會反色顯示~~這未免太BT了~
在網上找了一些相關的帖子,解決整行選中的問題可以採用爲List Control控件增加LVS_EX_FULLROWSELECT樣式的方法來實現:
m_Result.SetExtendedStyle(m_Result.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_TWOCLICKACTIVATE);
上面的代碼中,LVS_EX_GRIDLINES是希望顯示網格;LVS_EX_FULLROWSELECT是希望被選中時整行反色顯 示;LVS_EX_HEADERDRAGDROP是讓其支持點擊表頭排序;LVS_EX_TWOCLICKACTIVATE是希望有鼠標在未被選中的行上 移動的時候有一些效果~
整行選中的效果算是搞定了,接下來做任意單元格的編輯~
MFC的List Control控件本身是沒有辦法達到這個目的了,那怎麼辦呢?一個比較簡單的方法是:虛擬出來一個編輯框,覆蓋到被編輯的單元格上 :-)
所以,接下來的工作就是,基於CListCtrl類創建一個自己的ListCtrl類,並重載它的鼠標點擊事件處理函數,以便判斷用戶需要修改的單元格,並動態顯示或隱藏一個文本框,用來表示需要編譯的單元格~
首先創建自己的類:
![]() Code 1 #if !defined(AFX_MYLISTCTRL_H__7FDA9396_E298_4F10_B778_EB8ADFD82F9A__INCLUDED_) 2 #define AFX_MYLISTCTRL_H__7FDA9396_E298_4F10_B778_EB8ADFD82F9A__INCLUDED_ 3![]() 4 #if _MSC_VER > 1000 5 #pragma once 6 #endif // _MSC_VER > 1000 7 // MyListCtrl.h : header file 8 // 9 #define IDC_MY_LIST_EDITBOX 0xffff 10![]() 11 #define MLSM_ITEMCHANGED (WM_USER + 200) 12![]() /**////////////////////////////////////////////////////////////////////////////// 13 // CMyListCtrl window 14![]() 15 class CMyListCtrl : public CListCtrl 16![]() ![]() { 17 // Construction 18 public: 19 CMyListCtrl(); 20![]() 21 // Attributes 22 public: 23 // Operations 24 public: 25![]() 26 // Overrides 27 // ClassWizard generated virtual function overrides 28 //{{AFX_VIRTUAL(CMyListCtrl) 29 public: 30 virtual BOOL PreTranslateMessage(MSG* pMsg); 31 //}}AFX_VIRTUAL 32![]() 33 // Implementation 34 public: 35 virtual ~CMyListCtrl(); 36![]() 37 // Generated message map functions 38 protected: 39 CEdit m_EditItem; 40 int m_Row; 41 int m_Col; 42 //{{AFX_MSG(CMyListCtrl) 43 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 44 afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); 45 //}}AFX_MSG 46![]() 47 DECLARE_MESSAGE_MAP() 48 }; 49![]() 50![]() /**////////////////////////////////////////////////////////////////////////////// 51![]() 52 //{{AFX_INSERT_LOCATION}} 53 // Microsoft Visual C++ will insert additional declarations immediately before the previous line. 54![]() 55 #endif // !defined(AFX_MYLISTCTRL_H__7FDA9396_E298_4F10_B778_EB8ADFD82F9A__INCLUDED_)
|
其中需要重載或定義的關鍵的幾個成員:
CEdit m_EditItem;
int m_Row;
int m_Col;
//{{AFX_MSG(CMyListCtrl)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
virtual BOOL PreTranslateMessage(MSG* pMsg);
m_EditItem是用來做編輯動作的文本框;
m_Row和m_Col用來標識需要編輯的單元格,以便在不同的成員函數之間傳遞;
OnLButtonDown用來處理鼠標左鍵單擊事件;
OnLButtonDblClk用來處理鼠標左鍵雙擊事件;
PreTrahslateMessage函數用來截獲按鍵事件,主要用來截獲回車和ESC鍵,表示在編輯過程中確認修改或者取消修改
在鼠標左鍵雙擊事件中,主要的任務是需要判斷被雙擊的單元格,如果單元格有效,則需要進入編輯模式,並顯示文本框用來響應用戶的輸入,代碼如下:
![]()
Code
1
void CMyListCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
2![]()
![]()
{
3
// TODO: Add your message handler code here and/or call default
4
LVHITTESTINFO hi;
5
hi.pt = point;
6![]()
7
if(SubItemHitTest(&hi) != -1 )
8![]()
{
9
m_Row = hi.iItem;
10
m_Col = hi.iSubItem;
11
if(m_EditItem.m_hWnd == NULL)
12![]()
{
13
RECT rect;
14
rect.left = rect.top = 0;
15
rect.bottom = 20;
16
rect.right = 100;
17
m_EditItem.Create(WS_CHILD | ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | ES_WANTRETURN | ES_MULTILINE, rect, this, IDC_MY_LIST_EDITBOX);
18
m_EditItem.SetFont(this->GetFont(), FALSE);
19
}
20
CRect rect;
21
GetSubItemRect(hi.iItem, hi.iSubItem, LVIR_BOUNDS, rect);
22
m_EditItem.SetWindowText(this->GetItemText(hi.iItem, hi.iSubItem));
23
m_EditItem.MoveWindow(&rect, TRUE);
24
m_EditItem.ShowWindow(1);
25
}
26
CListCtrl::OnLButtonDblClk(nFlags, point);
27
}
在鼠標單擊事件中,我們需要判斷用戶單擊的是不是其他的單元格,如果是,表示用戶希望退出編輯模式了,此時,需要保存編輯之後的文本:
![]()
Code
1
void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
2![]()
![]()
{
3
// TODO: Add your message handler code here and/or call default
4
if(m_EditItem.m_hWnd != NULL)
5![]()
{
6
m_EditItem.ShowWindow(0);
7
if(m_Row != -1)
8![]()
{
9
CString ItemText;
10
m_EditItem.GetWindowText(ItemText);
11
this->SetItemText(m_Row, m_Col, ItemText);
12
::PostMessage(GetParent()->m_hWnd, MLSM_ITEMCHANGED, (WPARAM)MAKELONG(m_Row, m_Col), (LPARAM)this->m_hWnd);
13
}
14
}
15
m_Col = m_Row = -1;
16
CListCtrl::OnLButtonDown(nFlags, point);
17
}
在PreTranslateMessage函數中,需要截獲按鍵時間中的回車鍵和ESC鍵,並保存或取消編輯:
![]()
Code
1
BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
2![]()
![]()
{
3
// TODO: Add your specialized code here and/or call the base class
4
BOOL bHandledMsg = FALSE;
5![]()
6
if(pMsg->hwnd == m_EditItem.m_hWnd)
7![]()
{
8
switch (pMsg->message)
9![]()
{
10
case WM_KEYDOWN:
11![]()
{
12
switch (pMsg->wParam)
13![]()
{
14
case VK_RETURN://回車
15
if(m_Row != -1)
16![]()
{
17
CString ItemText;
18
m_EditItem.GetWindowText(ItemText);
19
this->SetItemText(m_Row, m_Col, ItemText);
20
::PostMessage(GetParent()->m_hWnd, MLSM_ITEMCHANGED, (WPARAM)MAKELONG(m_Row, m_Col), (LPARAM)this->m_hWnd);
21
}
22
case VK_ESCAPE://ESC鍵
23
m_EditItem.ShowWindow(0);
24
m_Col = m_Row = -1;
25
bHandledMsg = TRUE;
26
break;
27
default:
28
break;
29
}
30
}// case WM_KEYDOWN
31
break;
32
default:
33
break;
34
}// switch(pMsg->message)
35
}// if(pMsg->hwnd![]()
36
return (bHandledMsg ? TRUE : CListCtrl::PreTranslateMessage(pMsg));
37
}
OK,一個可以支持編輯的List Control控件就製作完成了,試試吧 :-)