MFC的listctrl控件中水平添加按钮并刷新

MFC的listctrl控件中水平添加按钮并刷新

      这个项目中需要用MFC实现一个界面功能:listctrl中水平添加按钮。
      MFC本身的listctrl控件只能显示简单的文本,简单的添加按钮也不是一两句代码能解决的问题,从这方面讲,MFC开发界面真是不得已而为之。
   
      因为需要的按钮数目是不确定的,所以只能是动态创建,然后再根据listctrl控件的位置计算出按钮应该放置的位置,然后将按钮移动到指定坐标。
      对MFC里面的类和关系,我并不熟悉,所以花了很长时间搜索,最终在下载的好几个版本的代码中找了一个基本可用的,修改开发。

   一、      针对我们需要管理自己动态创建的按钮,所以我们自定义了一个CButton的子类。

 1
 2 class  CButtonEx :  public  CButton
 3 {
 4    DECLARE_DYNAMIC(CButtonEx)
 5
 6public:
 7    CButtonEx();
 8    CButtonEx( int nItem, int nSubItem, CRect rect, HWND hParent,void * pData );
 9    virtual ~CButtonEx();
10
11protected:
12    DECLARE_MESSAGE_MAP()
13public:
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
14public:
15    CListCtrlEx();
16    virtual ~CListCtrlEx();
17
18protected:
19    DECLARE_MESSAGE_MAP()
20
21public:
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
29public:
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}
 
这样,按钮就能正确刷新了。

不过,还有一个小问题:在拖动滚动条时,我们发现界面有些闪烁。但是我还没找到合适的解决方法。欢迎大家给出可行的方案。

你可能感兴趣的:(MFC的listctrl控件中水平添加按钮并刷新)