这个项目中需要用MFC实现一个界面功能:listctrl中水平添加按钮。
MFC本身的listctrl控件只能显示简单的文本,简单的添加按钮也不是一两句代码能解决的问题,从这方面讲,MFC开发界面真是不得已而为之。
因为需要的按钮数目是不确定的,所以只能是动态创建,然后再根据listctrl控件的位置计算出按钮应该放置的位置,然后将按钮移动到指定坐标。
对MFC里面的类和关系,我并不熟悉,所以花了很长时间搜索,最终在下载的好几个版本的代码中找了一个基本可用的,修改开发。
一、 针对我们需要管理自己动态创建的按钮,所以我们自定义了一个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(); //点击响应函数
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; //按钮带的用户自定义数据
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
} 按钮点击的响应逻辑在OnBnClicked函数中。之所以加入m_pData成员变量,是便于存放用户自定义数据,这样就可以在OnBnClicked函数中根据自定义变量做出相应的处理。
二、自定义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
//动态创建Button
23
void createItemButton( int nItem, int nSubItem, HWND hMain,LPCTSTR lpszCaption ,void * pData);
24
//释放创建的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
//重载水平滚动条滚动函数
35
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
36
}; CListCtrlEx实现如下:
1
CListCtrlEx::CListCtrlEx()
2

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

{
10
release();
11
} 然后是创建按钮的逻辑,注意我这里是在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
上面的代码中,我将按钮的高都设为了150,而不是listctrl默认的一点点高。
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
} 当完成以上代码以后,就可以在对话框中添加listctrl控件的成员变量了:CListCtrlEx m_lsPath;
然后在OnInitDialog函数中给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};//标题
7
ImageCfg * pImageCfg = new ImageCfg;//自定义数据
8
m_lsPath.createItemButton(nRow,i++,m_lsPath,caption,pImageCfg); 这样看起来一切很好,但是运行时发现,当按钮较多需要水平滚动条时,拖动水平滚动条并不能正确的显示按钮!
所以我们还需要处理CListCtrlEx的水平滚动命令:
1
void CListCtrlEx::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
2

{
3
// TODO: 在此添加消息处理程序代码和/或调用默认值
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
//调整横向的
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
} 这里操作的过程是:取得控件水平滚动条的位置,然后将所有按钮的水平坐标左移响应的值。其实这里可以优化一下:判断只有那些按钮会被显示才处理,其他的并不需要处理,例如:
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
//调整横向的
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
} 这样,按钮就能正确刷新了。
不过,还有一个小问题:在拖动滚动条时,我们发现界面有些闪烁。但是我还没找到合适的解决方法。欢迎大家给出可行的方案。