這個項(xiàng)目中需要用MFC實(shí)現(xiàn)一個界面功能:listctrl中水平添加按鈕。
MFC本身的listctrl控件只能顯示簡單的文本,簡單的添加按鈕也不是一兩句代碼能解決的問題,從這方面講,MFC開發(fā)界面真是不得已而為之。
因?yàn)樾枰陌粹o數(shù)目是不確定的,所以只能是動態(tài)創(chuàng)建,然后再根據(jù)listctrl控件的位置計(jì)算出按鈕應(yīng)該放置的位置,然后將按鈕移動到指定坐標(biāo)。
對MFC里面的類和關(guān)系,我并不熟悉,所以花了很長時(shí)間搜索,最終在下載的好幾個版本的代碼中找了一個基本可用的,修改開發(fā)。
一、 針對我們需要管理自己動態(tài)創(chuàng)建的按鈕,所以我們自定義了一個CButton的子類。
1
2
class CButtonEx : public CButton
3

{
4
DECLARE_DYNAMIC(CButtonEx)
5
6
public:
7
CButtonEx();
8
CButtonEx( int nItem, int nSubItem, CRect rect, HWND hParent,void * pData );
9
virtual ~CButtonEx();
10
11
protected:
12
DECLARE_MESSAGE_MAP()
13
public:
14
afx_msg void OnBnClicked(); //點(diǎn)擊響應(yīng)函數(shù)
15
int m_inItem; //所屬listctrl的行
16
int m_inSubItem; //所屬listctrl的列
17
CRect m_rect; //按鈕所在的位置
18
HWND m_hParent; //按鈕的父窗口
19
BOOL bEnable;
20
void * m_pData; //按鈕帶的用戶自定義數(shù)據(jù)
21
};
1
CButtonEx::CButtonEx( int nItem, int nSubItem, CRect rect, HWND hParent,void * pData )
2

{
3
m_inItem = nItem;
4
m_inSubItem = nSubItem;
5
m_rect = rect;
6
m_hParent = hParent;
7
bEnable = TRUE;
8
m_pData = pData;
9
} 按鈕點(diǎn)擊的響應(yīng)邏輯在OnBnClicked函數(shù)中。之所以加入m_pData成員變量,是便于存放用戶自定義數(shù)據(jù),這樣就可以在OnBnClicked函數(shù)中根據(jù)自定義變量做出相應(yīng)的處理。
二、自定義listctrl子類
1
#pragma once
2
3
#include "ButtonEx.h"
4
#include <map>
5
using namespace std;
6
7
typedef map<int,CButtonEx*> button_map;
8
// CListCtrlEx
9
10
class CListCtrlEx : public CListCtrl
11

{
12
DECLARE_DYNAMIC(CListCtrlEx)
13
14
public:
15
CListCtrlEx();
16
virtual ~CListCtrlEx();
17
18
protected:
19
DECLARE_MESSAGE_MAP()
20
21
public:
22
//動態(tài)創(chuàng)建Button
23
void createItemButton( int nItem, int nSubItem, HWND hMain,LPCTSTR lpszCaption ,void * pData);
24
//釋放創(chuàng)建的Button
25
void release();
26
void deleteItemEx( int nItem );
27
button_map m_mButton;
28
29
public:
30
UINT m_uID;
31
CFont font ; //按鈕上面的字體
32
void updateListCtrlButtonPos(); //更新按鈕的位置
33
//void enableButton( BOOL bFlag, int iItem );
34
//重載水平滾動條滾動函數(shù)
35
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
36
}; CListCtrlEx實(shí)現(xiàn)如下:
1
CListCtrlEx::CListCtrlEx()
2

{
3
m_uID = 0;
4
5
font.CreatePointFont(100,"宋體");
6
}
7
8
CListCtrlEx::~CListCtrlEx()
9

{
10
release();
11
} 然后是創(chuàng)建按鈕的邏輯,注意我這里是在ListCtrl控件中水平添加按鈕(同一行的每一列),而不是垂直(同一列的每一行):
1
void CListCtrlEx::createItemButton( int nItem, int nSubItem, HWND hMain,LPCTSTR lpszCaption ,void * pData)
2

{
3
CRect rect;
4
/**//*if( !EnsureVisible(nItem, TRUE))
5
return ;*/
6
7
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
8
rect.bottom = rect.top + 150;
9
//rect.right = rect.left + 150;
10
11
DWORD dwStyle = WS_CHILD | WS_VISIBLE | BS_MULTILINE;
12
CButtonEx *pButton = new CButtonEx(nItem,nSubItem,rect,hMain,pData);
13
m_uID++;
14
15
pButton->Create(lpszCaption,dwStyle, rect, this, m_uID);
16
//CDC* pDC = pButton->GetDC();
17
//pDC->SetTextColor(RGB(255,0,0));
18
pButton->SetFont(&font);
19
20
// m_mButton.insert( make_pair( nItem, pButton ) ); //縱向添加用
21
m_mButton.insert( make_pair( nSubItem, pButton ) ); //單行橫向添加用
22
23
return;
24
}
25
上面的代碼中,我將按鈕的高都設(shè)為了150,而不是listctrl默認(rèn)的一點(diǎn)點(diǎn)高。
1
void CListCtrlEx::release()
2

{
3
button_map::iterator iter = m_mButton.begin();
4
while ( iter != m_mButton.end() )
5
{
6
delete iter->second;
7
iter->second = NULL;
8
iter++;
9
}
10
m_mButton.clear();
11
} 當(dāng)完成以上代碼以后,就可以在對話框中添加listctrl控件的成員變量了:CListCtrlEx m_lsPath;
然后在OnInitDialog函數(shù)中給listctrl控件添加按鈕:
1
int i = 0;
2
m_lsPath.InsertColumn(i,_T(""),LVCFMT_LEFT,150);
3
4
nRow = m_lsPath.InsertItem(0, "tim");
5
6
TCHAR caption[1000] =
{0};//標(biāo)題
7
ImageCfg * pImageCfg = new ImageCfg;//自定義數(shù)據(jù)
8
m_lsPath.createItemButton(nRow,i++,m_lsPath,caption,pImageCfg); 這樣看起來一切很好,但是運(yùn)行時(shí)發(fā)現(xiàn),當(dāng)按鈕較多需要水平滾動條時(shí),拖動水平滾動條并不能正確的顯示按鈕!
所以我們還需要處理CListCtrlEx的水平滾動命令:
1
void CListCtrlEx::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
2

{
3
// TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值
4
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
5
updateListCtrlButtonPos();
6
//Invalidate(FALSE);
7
}
1
void CListCtrlEx::updateListCtrlButtonPos()
2

{
3
button_map::iterator iter = m_mButton.begin();
4
button_map::iterator itrEnd = m_mButton.end();
5
//調(diào)整橫向的
6
int posx = GetScrollPos(SB_HORZ);//取得水平滾動條的位置
7
for (;iter != itrEnd;++iter)
8
{
9
CRect rect;
10
rect = iter->second->m_rect;
11
rect.left -= posx;
12
rect.right -= posx;
13
iter->second->ShowWindow( SW_HIDE );
14
15
iter->second->MoveWindow( &rect );
16
iter->second->ShowWindow( SW_SHOW );
17
/**//*if( iLine < iTopIndex )
18
{
19
iterUp->second->ShowWindow( SW_HIDE );
20
}*/
21
}
22
return;
23
} 這里操作的過程是:取得控件水平滾動條的位置,然后將所有按鈕的水平坐標(biāo)左移響應(yīng)的值。其實(shí)這里可以優(yōu)化一下:判斷只有那些按鈕會被顯示才處理,其他的并不需要處理,例如:
1
void CListCtrlEx::updateListCtrlButtonPos()
2

{
3
button_map::iterator iter = m_mButton.begin();
4
button_map::iterator itrEnd = m_mButton.end();
5
6
CRect rect;
7
GetClientRect(rect);
8
LONG width = rect.right;
9
//調(diào)整橫向的
10
int posx = GetScrollPos(SB_HORZ);//取得水平滾動條的位置
11
for (;iter != itrEnd;++iter)
12
{
13
iter->second->ShowWindow( SW_HIDE );
14
15
rect = iter->second->m_rect;
16
rect.left -= posx;
17
rect.right -= posx;
18
if (rect.right > 0)
19
{
20
if (rect.left > width)
21
{
22
//其他的都超出了顯示范圍
23
break;
24
}
25
iter->second->MoveWindow( &rect );
26
iter->second->ShowWindow( SW_SHOW );
27
}
28
29
/**//*if( iLine < iTopIndex )
30
{
31
iterUp->second->ShowWindow( SW_HIDE );
32
}*/
33
}
34
return;
35
} 這樣,按鈕就能正確刷新了。
不過,還有一個小問題:在拖動滾動條時(shí),我們發(fā)現(xiàn)界面有些閃爍。但是我還沒找到合適的解決方法。歡迎大家給出可行的方案。