Super ListCtrl for MFC - 可以内嵌CheckBox, Radio, Combo, Edit, Progress, 支持排序

由于需要ListCtrl可以内嵌CheckBox, Radio, Combo, Edit, Progress, 并且支持按字符串,日期,数字格式排序,在网上搜罗一阵也没找到合适的,只好自己动手改造,效果还不错

//  ListCtrl should be set with LVS_REPORT style

 // ListCtrlCellEx.h - ListCtrl单元格以及HeadCtrl

ContractedBlock.gif ExpandedBlockStart.gif Code
#pragma  once
#include 
<list>
using namespace std;
namespace ListCtrlEx
{
    
    typedef list
<CString> CStrList;

    
//////////////////////////////////////////////////////////////////////////
    class AFX_EXT_CLASS CInPlaceCombo : public CComboBox
    {
    
public:
        
static CInPlaceCombo* GetInstance(); 
        
static void DeleteInstance(); 
        BOOL ShowComboCtrl(DWORD dwStyle, 
const CRect& rCellRect, CWnd* pParentWnd, UINT uiResourceID,
            
int iRowIndex, int iColumnIndex, CStrList &lstDropDown, CString strCurSel = _T(""), int iCurSel = -1);
        
virtual BOOL PreTranslateMessage(MSG* pMsg);

    
protected:
        afx_msg 
int OnCreate(LPCREATESTRUCT lpCreateStruct);
        afx_msg 
void OnKillFocus(CWnd* pNewWnd);
        afx_msg 
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
        afx_msg 
void OnCloseup();

        DECLARE_MESSAGE_MAP()
    
private:
        CInPlaceCombo();
        CInPlaceCombo (CInPlaceCombo
&) {}
        
const CInPlaceCombo& operator = (CInPlaceCombo) { return *this; }
        
virtual ~CInPlaceCombo();

        
static CInPlaceCombo* m_pInPlaceCombo;
        
int m_iRowIndex;
        
int m_iColumnIndex;
        CString m_strWindowText;
        CStrList m_DropDownList;
        
// To indicate whether ESC key was pressed
        BOOL m_bESC;
    };
    
//////////////////////////////////////////////////////////////////////////
    
//////////////////////////////////////////////////////////////////////////
    
//////////////////////////////////////////////////////////////////////////

    
class AFX_EXT_CLASS CInPlaceEdit : public CEdit
    {
    
#define CTRL_C    0x3
    
#define CTRL_V    0x16
    
#define CTRL_X    0x18
    
public:
        
static CInPlaceEdit* GetInstance(); 
        
static void DeleteInstance(); 
        BOOL ShowEditCtrl(DWORD dwStyle, 
const RECT& rCellRect, CWnd* pParentWnd, 
            UINT uiResourceID, 
int iRowIndex, int iColumnIndex,
            CString
& strValidChars, CString& rstrCurSelection);
        
virtual BOOL PreTranslateMessage(MSG* pMsg);

    
protected:    
        
// Generated message map functions
        
//{{AFX_MSG(CInPlaceEdit)
        afx_msg void OnKillFocus(CWnd* pNewWnd);
        afx_msg 
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
        afx_msg LRESULT OnPaste(WPARAM wParam, LPARAM lParam);
        afx_msg 
int OnCreate(LPCREATESTRUCT lpCreateStruct);
        
//}}AFX_MSG

        DECLARE_MESSAGE_MAP()

    
private:
        CInPlaceEdit();
        CInPlaceEdit (CInPlaceEdit
&) {}
        
const CInPlaceEdit& operator = (CInPlaceEdit) { return *this; }
        
virtual ~CInPlaceEdit();
        
        
static CInPlaceEdit* m_pInPlaceEdit;
        
int m_iRowIndex;
        
int m_iColumnIndex;
        CString m_strValidChars;
        CString m_strWindowText;
        
// To indicate whether ESC key was pressed
        BOOL m_bESC;
        
    };

    
//////////////////////////////////////////////////////////////////////////
    // CSortHeadCtrl
    class AFX_EXT_CLASS CListCtrlExSortHead : public CHeaderCtrl
    {
        DECLARE_DYNAMIC(CListCtrlExSortHead)

    
public:
        CListCtrlExSortHead();

        
virtual ~CListCtrlExSortHead();

        
void            SetSortArrow( const int nSortColumn, const BOOL bDesc );
        inline 
int        GetCrntSortCol() const;
        inline BOOL    GetCrntSortDirection() 
const;
    
protected:
        
int m_iSortColumn;
        BOOL m_bSortDesc;
        DECLARE_MESSAGE_MAP()
        afx_msg 
void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
        
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
        
    };


}

//////////////////////////////////////////////////////////////////////////

 // ListCtrlCellEx.cpp

ContractedBlock.gif ExpandedBlockStart.gif Code
#include "stdafx.h"
#include 
"ListCtrlCellEx.h"

using namespace ListCtrlEx;

//////////////////////////////////////////////////////////////////////////
// ------------ for CInPlaceCombo --------------------//
CInPlaceCombo* CInPlaceCombo::m_pInPlaceCombo = NULL; 

CInPlaceCombo::CInPlaceCombo()
{
    m_iRowIndex 
= -1;
    m_iColumnIndex 
= -1;
    m_bESC 
= FALSE;
}

CInPlaceCombo::
~CInPlaceCombo()
{
    
if (::IsWindow(m_pInPlaceCombo->m_hWnd))
    {
        SendMessage(WM_CLOSE);
    }
}

BEGIN_MESSAGE_MAP(CInPlaceCombo, CComboBox)
    
//{{AFX_MSG_MAP(CInPlaceCombo)
    ON_WM_CREATE()
    ON_WM_KILLFOCUS()
    ON_WM_CHAR()
    ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)
    
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

int CInPlaceCombo::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    
if (CComboBox::OnCreate(lpCreateStruct) == -1)
    {
        
return -1;
    }

    CFont
* pFont = GetParent()->GetFont();
    SetFont(pFont);
    SetFocus();
    ResetContent(); 
    CStrList::iterator iter
=m_DropDownList.begin();
    
for (; iter!=m_DropDownList.end(); ++iter)
    {
        AddString(
*iter);
    }

    
return 0;
}

BOOL CInPlaceCombo::PreTranslateMessage(MSG
* pMsg) 
{
    
if (pMsg->message == WM_KEYDOWN)
    {
        
if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
        {
            ::TranslateMessage(pMsg);
            ::DispatchMessage(pMsg);
            
return TRUE;                
        }
    }

    
return CComboBox::PreTranslateMessage(pMsg);
}

void CInPlaceCombo::OnKillFocus(CWnd* pNewWnd) 
{
    CComboBox::OnKillFocus(pNewWnd);

    CString str;
    GetWindowText(str);
    
// Send Notification to parent of ListView ctrl
    LV_DISPINFO dispinfo;
    dispinfo.hdr.hwndFrom 
= GetParent()->m_hWnd;
    dispinfo.hdr.idFrom 
= GetDlgCtrlID();
    dispinfo.hdr.code 
= LVN_ENDLABELEDIT;

    dispinfo.item.mask 
= LVIF_TEXT;
    dispinfo.item.iItem 
= m_iRowIndex;
    dispinfo.item.iSubItem 
= m_iColumnIndex;
    dispinfo.item.pszText 
= m_bESC ? LPTSTR((LPCTSTR)m_strWindowText) : LPTSTR((LPCTSTR)str);
    dispinfo.item.cchTextMax 
= m_bESC ? m_strWindowText.GetLength() : str.GetLength();

    GetParent()
->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&dispinfo);
    PostMessage(WM_CLOSE);
}

void CInPlaceCombo::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    
// If the key is "Esc" set focus back to the list control
    if (nChar == VK_ESCAPE || nChar == VK_RETURN)
    {
        
if (nChar == VK_ESCAPE)
        {
            m_bESC 
= TRUE;
        }
        GetParent()
->SetFocus();
        
return;
    }

    CComboBox::OnChar(nChar, nRepCnt, nFlags);
}

void CInPlaceCombo::OnCloseup() 
{
    GetParent()
->SetFocus();
}

CInPlaceCombo
* CInPlaceCombo::GetInstance()
{
    
if(m_pInPlaceCombo == NULL)
    {
        m_pInPlaceCombo 
= new CInPlaceCombo;
    }
    
return m_pInPlaceCombo;
}

void CInPlaceCombo::DeleteInstance()
{
    delete m_pInPlaceCombo;
    m_pInPlaceCombo 
= NULL;
}

BOOL CInPlaceCombo::ShowComboCtrl(DWORD dwStyle, 
const CRect &rCellRect, CWnd* pParentWnd, UINT uiResourceID,
                                  
int iRowIndex, int iColumnIndex, CStrList &lstDropDown, CString strCurSel, int iCurSel )
{

    m_iRowIndex 
= iRowIndex;
    m_iColumnIndex 
= iColumnIndex;
    m_bESC 
= FALSE;

    m_DropDownList.clear();
    m_DropDownList.insert(m_DropDownList.begin(), lstDropDown.begin(), lstDropDown.end());

    BOOL bRetVal 
= TRUE;

    
if (-1 != iCurSel)
    {
        GetLBText(iCurSel, m_strWindowText);
    }
    
else if (!strCurSel.IsEmpty()) 
    {
        m_strWindowText 
= strCurSel;
    }

    
if (NULL == m_pInPlaceCombo->m_hWnd) 
    {
        bRetVal 
= m_pInPlaceCombo->Create(dwStyle, rCellRect, pParentWnd, uiResourceID); 
    }

    SetCurSel(iCurSel);

    
return bRetVal;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// --------------- for CInPlaceEdit -------------------//
CInPlaceEdit* CInPlaceEdit::m_pInPlaceEdit=NULL;

CInPlaceEdit::CInPlaceEdit()
{
    m_iRowIndex
= -1;
    m_iColumnIndex 
= -1;
    m_bESC 
= FALSE;
    m_strValidChars.Empty();
}

CInPlaceEdit::
~CInPlaceEdit()
{
    
if (::IsWindow(m_pInPlaceEdit->m_hWnd))
    {
        SendMessage(WM_CLOSE);
    }
}

BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)
    
//{{AFX_MSG_MAP(CInPlaceEdit)
    ON_WM_KILLFOCUS()
    ON_WM_CHAR()
    ON_MESSAGE(WM_PASTE, CInPlaceEdit::OnPaste)
    ON_WM_CREATE()
    
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CInPlaceEdit message handlers

LRESULT CInPlaceEdit::OnPaste(WPARAM 
/*wParam*/, LPARAM /*lParam*/)
{
    
if (!m_strValidChars.IsEmpty())
    {
        CString strFromClipboard;
        
// get the text from clipboard
        if(OpenClipboard()) {
            HANDLE l_hData 
= GetClipboardData(CF_TEXT);
            
if(NULL == l_hData) {
                
return 1;
            }

            
char *l_pBuffer = (char*)GlobalLock(l_hData);
            
if(NULL != l_pBuffer) {
                strFromClipboard 
= l_pBuffer;
            }

            GlobalUnlock(l_hData);
            CloseClipboard();
        }
        
// Validate the characters before pasting 
        for(int iCounter_ = 0; iCounter_ < strFromClipboard.GetLength(); iCounter_++)
        {
            
if (-1 == m_strValidChars.Find(strFromClipboard.GetAt(iCounter_)))
            {
                
return 1;
            }
        }
    }

    
//let the individual control handle other processing
    CEdit::Default();    
    
return 1;    
}

void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd) 
{
    CEdit::OnKillFocus(pNewWnd);

    CString strEdit;
    GetWindowText(strEdit);

    
// Send Notification to parent of edit ctrl
    LV_DISPINFO dispinfo;
    dispinfo.hdr.hwndFrom 
= GetParent()->m_hWnd;
    dispinfo.hdr.idFrom 
= GetDlgCtrlID();
    dispinfo.hdr.code 
= LVN_ENDLABELEDIT;

    dispinfo.item.mask 
= LVIF_TEXT;
    dispinfo.item.iItem 
= m_iRowIndex;
    dispinfo.item.iSubItem 
= m_iColumnIndex; 
    
// escape key is down so use old string
    dispinfo.item.pszText = m_bESC ? LPTSTR((LPCTSTR)m_strWindowText) : LPTSTR((LPCTSTR)strEdit);
    dispinfo.item.cchTextMax 
= m_bESC ? m_strWindowText.GetLength() : strEdit.GetLength();

    GetParent()
->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&dispinfo);
    PostMessage(WM_CLOSE);
}

void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    
// TODO: Add your message handler code here and/or call default
    if ( (m_strValidChars.IsEmpty()) // no valid chars
        || ((-1 != m_strValidChars.Find(static_cast<TCHAR> (nChar)))
        
|| (nChar == VK_BACK) || (nChar == CTRL_C) || (nChar == CTRL_V) || (nChar == CTRL_X)))
    {
        CEdit::OnChar(nChar, nRepCnt, nFlags);
    }
    
else
    {
        MessageBeep(MB_ICONEXCLAMATION);
        
return;
    }
}

BOOL CInPlaceEdit::PreTranslateMessage(MSG
* pMsg) 
{
    
if (WM_KEYDOWN == pMsg->message && (VK_ESCAPE == pMsg->wParam || VK_RETURN == pMsg->wParam))
    {
        
if (VK_ESCAPE == pMsg->wParam)
        {
            m_bESC 
= TRUE;
        }

        GetParent()
->SetFocus();
        
return TRUE;
    }

    
return CEdit::PreTranslateMessage(pMsg);
}

int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    
if (CEdit::OnCreate(lpCreateStruct) == -1)
        
return -1;

    
// Set the proper font
    CFont* pFont = GetParent()->GetFont();
    SetFont(pFont);

    ShowWindow(SW_SHOW);
    SetWindowText(m_strWindowText);
    SetSel(
0-1);
    SetFocus();

    
return 0;
}

CInPlaceEdit
* CInPlaceEdit::GetInstance()
{
    
if(m_pInPlaceEdit == NULL)
    {
        m_pInPlaceEdit 
= new CInPlaceEdit;
    }
    
return m_pInPlaceEdit;
}

void CInPlaceEdit::DeleteInstance()
{
    delete m_pInPlaceEdit;
    m_pInPlaceEdit 
= NULL;
}

BOOL CInPlaceEdit::ShowEditCtrl(DWORD dwStyle, 
const RECT &rCellRect, CWnd* pParentWnd, 
                                UINT uiResourceID, 
int iRowIndex, int iColumnIndex,
                                CString
& strValidChars, CString& rstrCurSelection)
{
    m_iRowIndex 
= iRowIndex;
    m_iColumnIndex 
= iColumnIndex;
    m_strValidChars 
= strValidChars;
    m_strWindowText 
= rstrCurSelection;
    m_bESC 
= FALSE;

    
if (NULL == m_pInPlaceEdit->m_hWnd) 
    {
        
return m_pInPlaceEdit->Create(dwStyle, rCellRect, pParentWnd, uiResourceID); 
    }    

    
return TRUE;
}

//////////////////////////////////////////////////////////////////////////
// CListCtrlExSortHead

IMPLEMENT_DYNAMIC(CListCtrlExSortHead, CHeaderCtrl)

CListCtrlExSortHead::CListCtrlExSortHead()
: m_iSortColumn(
-1),m_bSortDesc(FALSE)
{

}

CListCtrlExSortHead::
~CListCtrlExSortHead()
{

}


BEGIN_MESSAGE_MAP(CListCtrlExSortHead, CHeaderCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, 
&CListCtrlExSortHead::OnNMCustomdraw)
END_MESSAGE_MAP()



// CSortHeadCtrl message handlers
void CListCtrlExSortHead::SetSortArrow( const int nSortColumn, const BOOL bDesc )
{
    
if (nSortColumn<0 || nSortColumn>=this->GetItemCount())
    {
        ASSERT(FALSE);
    }
    m_iSortColumn
=nSortColumn;
    m_bSortDesc
=bDesc;

    
// change the item to owner drawn.
    HD_ITEM hditem;

    hditem.mask 
= HDI_FORMAT;
    VERIFY( GetItem( nSortColumn, 
&hditem ) );
    hditem.fmt 
|= HDF_OWNERDRAW;
    VERIFY( SetItem( nSortColumn, 
&hditem ) );

    
// invalidate the header control so it gets redrawn
    Invalidate();
    
//UpdateWindow();
}

inline 
int     CListCtrlExSortHead::GetCrntSortCol() const
{
    
return m_iSortColumn;
}

inline BOOL CListCtrlExSortHead::GetCrntSortDirection() 
const
{
    
return m_bSortDesc;
}

void CListCtrlExSortHead::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    CDC
* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);

    
// Get the column rect
    CRect rcLabel(lpDrawItemStruct->rcItem);

    
// Draw the background
    CBrush br(::GetSysColor(COLOR_3DFACE));
    pDC
->FillRect(rcLabel, &br);    

    
// Labels are offset by a certain amount  
    
// This offset is related to the width of a space character
    int offset = pDC->GetTextExtent(_T(" "), 1).cx*2;

    
// Get the column text and format
    TCHAR buf[256];
    HD_ITEM hditem;    

    hditem.mask 
= HDI_TEXT | HDI_FORMAT;
    hditem.pszText 
= buf;
    hditem.cchTextMax 
= 255;

    GetItem(lpDrawItemStruct
->itemID, &hditem);

    
// Determine format for drawing column label
    UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS ;

    
if (hditem.fmt & HDF_CENTER)
        uFormat 
|= DT_CENTER;
    
else if (hditem.fmt & HDF_RIGHT)
        uFormat 
|= DT_RIGHT;
    
else
        uFormat 
|= DT_LEFT;

    
// Adjust the rect if the mouse button is pressed on it
    if (lpDrawItemStruct->itemState == ODS_SELECTED)
    {
        rcLabel.left
++;
        rcLabel.top 
+= 2;
        rcLabel.right
++;
    }

    
// Adjust the rect further if Sort arrow is to be displayed
    if (lpDrawItemStruct->itemID == static_cast<UINT>(m_iSortColumn))
    {
        rcLabel.right 
-= 3 * offset;
    }

    rcLabel.left 
+= offset;
    rcLabel.right 
-= offset;

    
// Draw column label
    pDC->DrawText(buf, -1, rcLabel, uFormat);

    
// Draw the Sort arrow
    if (lpDrawItemStruct->itemID == static_cast<UINT>(m_iSortColumn))
    {
        CRect rcIcon(lpDrawItemStruct
->rcItem);

        
// Set up pens to use for drawing the triangle
        CPen penLight(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
        CPen penShadow(PS_SOLID, 
1, GetSysColor(COLOR_3DSHADOW));
        CPen 
*pOldPen = pDC->SelectObject(&penLight);

        
if (!m_bSortDesc)
        {
            
// Draw triangle pointing upwards
            pDC->MoveTo(rcIcon.right - 2*offset, offset - 1);
            pDC
->LineTo(rcIcon.right - 3*offset/2, rcIcon.bottom - offset);
            pDC
->LineTo(rcIcon.right - 5*offset/2-2, rcIcon.bottom - offset);
            pDC
->MoveTo(rcIcon.right - 5*offset/2-1, rcIcon.bottom - offset - 1);

            pDC
->SelectObject(&penShadow);
            pDC
->LineTo(rcIcon.right - 2*offset, offset-2);
        }
        
else    
        {
            
// Draw triangle pointing downwards
            pDC->MoveTo(rcIcon.right - 3*offset/2, offset-1);
            pDC
->LineTo(rcIcon.right - 2*offset-1, rcIcon.bottom - offset + 1);
            pDC
->MoveTo(rcIcon.right - 2*offset-1, rcIcon.bottom - offset);

            pDC
->SelectObject(&penShadow);
            pDC
->LineTo(rcIcon.right - 5*offset/2-1, offset - 1);
            pDC
->LineTo(rcIcon.right - 3*offset/2, offset - 1);
        }

        
// Restore the pen
        pDC->SelectObject(pOldPen);
    }

}


void ListCtrlEx::CListCtrlExSortHead::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMCUSTOMDRAW pNMCD 
= reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
    
switch(pNMCD->dwDrawStage)
    {
    
case CDDS_PREPAINT:
        
*pResult = CDRF_NOTIFYSUBITEMDRAW;          // ask for subitem notifications.
        *pResult = CDRF_DODEFAULT;
        
break;
    
case CDDS_ITEMPREPAINT:
        
*pResult = CDRF_NOTIFYSUBITEMDRAW;          // ask for subitem notifications.
        *pResult = CDRF_DODEFAULT;
        
break;
    
case CDDS_ITEMPREPAINT|CDDS_SUBITEM:            // recd when CDRF_NOTIFYSUBITEMDRAW is returned in
        *pResult = CDRF_DODEFAULT;
        
break;
    
default:                                                        // it wasn't a notification that was interesting to us.
        *pResult = CDRF_DODEFAULT;
        
break;
    }
    
*pResult = 0;
}

 // ListCtrlEx.h - 去除 //#define USING_CUSTOM_DRAW前面的注释,即启用自绘效果

ContractedBlock.gif ExpandedBlockStart.gif Code
#ifndef __LISTCTRLEX__
#define __LISTCTRLEX__

#include 
<map>
#include 
<vector>
#include 
<list>
#include 
"ListCtrlCellEx.h"
//////////////////////////////////////////////////////////////////////////
//#define USING_CUSTOM_DRAW // define this to control the cell control theme (whether or not to draw them hand on ))
//////////////////////////////////////////////////////////////////////////
namespace ListCtrlEx
{
    
using namespace std;

    
enum CellType
    {
        Normal, 
// can show icon
        CheckBox, // can be checked
        RadioBox,  // can be checked
        ComboBox, // can be shown in place
        EditBox,  // can be shown in place
        Progress  // can show progress here
    };

    
enum SortType
    {
        SortByString,
        SortByDigit,
        SortByDate
    };

    
struct CheckCellMsg
    {
        CheckCellMsg(
int nRow=-1int nCol=-1, BOOL bChecked=TRUE)
            :m_nRow(nRow), m_nColumn(nCol), m_bChecked(bChecked)
        {}
        
int m_nRow;
        
int m_nColumn;
        BOOL m_bChecked;
    } ;

    
struct EnableCellMsg 
    {
        EnableCellMsg(
int nRow=-1int nCol=-1, BOOL bEnabled=TRUE)
            :m_nRow(nRow), m_nColumn(nCol), m_bEnabled(bEnabled)
        {}
        
int m_nRow;
        
int m_nColumn;
        BOOL m_bEnabled;
    } ;
    
// user messages
    #define WM_ListCtrlEx_LBUTTONDOWN        WM_USER+8001
    
#define WM_ListCtrlEx_LBUTTONDBLCLK    WM_USER+8002
    
#define WM_ListCtrlEx_RBUTTONDOWN        WM_USER+8003
    
#define WM_ListCtrlEx_CHECKCELL            WM_USER+8004
    
#define WM_ListCtrlEx_ENABLECELL            WM_USER+8005
    
#define WM_ListCtrlEx_CHECK_CHANGED  WM_USER+8006
    
#define WM_ListCtrlEx_ENABLE_CHANGED WM_USER+8007
    
// User define message 
    
// This message is posted to the parent
    
// The message can be handled to make the necessary validations, if any
    #define WM_VALIDATE        WM_USER + 8008

    
// User define message 
    
// This message is posted to the parent
    
// The message should be handled to spcify the items to the added to the combo
    #define WM_SET_ITEMS    WM_USER + 8009

    
// CListCtrlEx
    class AFX_EXT_CLASS CListCtrlEx : public CListCtrl
    {
        DECLARE_DYNAMIC(CListCtrlEx)
    
public:
    
        
struct CellData 
        {
        
private:
            DWORD        m_dwData; 
// enabled at the first bit and checked at the second bit (count from the back)
            /*BOOL            m_bEnabled;
            BOOL            m_bChecked;
*/
        
public:
            
#define Enable_Mask  0x00000001
            
#define Check_Mask  0x00000002
            CellData(BOOL bEnabled
=TRUE, BOOL bChecked=FALSE, int nRadioGroup=0int nImage=-1, UINT uProgMaxVal=100
                : m_nRadioGroup(nRadioGroup), m_strValidChars(_T(
"")),
                m_nImage(nImage), m_uProgresVal(
0), m_uProgressMax(uProgMaxVal)
            {
                SetEnable(bEnabled);
                SetCheck(bChecked);
            };

            
int                m_nImage;
            
            
int                m_nRadioGroup;
            CStrList        m_lstString;
            CString        m_strValidChars;
            UINT            m_uProgresVal;
            UINT            m_uProgressMax;

            inline 
void SetEnable(BOOL bEnbaled=TRUE) {
                
if (bEnbaled)
                    m_dwData 
|= Enable_Mask;
                
else
                    m_dwData 
&=(~Enable_Mask);
            }

            inline BOOL GetEnable() 
const {
                
return (m_dwData&Enable_Mask);
            }

            inline 
void SetCheck(BOOL bChecked=TRUE){
                
if(bChecked)
                    m_dwData
|=Check_Mask;
                
else
                    m_dwData 
&=(~Check_Mask);
            }

            inline BOOL GetCheck() 
const {
                
return (m_dwData&Check_Mask);
            }
            
        };

        typedef CellType ColumnType;
        typedef map
<int, ColumnType> ColMap; // column to column type
        typedef pair<intint> CellIndex; // row, column
        typedef map<CellIndex, CellData> CellDataMap; // cell index(row, column) to cell data
        typedef map<CellIndex, CProgressCtrl*> ProgressMap;
    
public:
        CListCtrlEx();
        
virtual ~CListCtrlEx();
// overrides
    public:
        CImageList
*    SetImageList( CImageList* pImageList, int nImageListType );
        
int                    InsertColumn(int nCol, CString strColHead, int nWidth=-1,ColumnType eColType=Normal, 
                                                
int nFormat= LVCFMT_CENTER , SortType eSortBy=SortByString, int nSubItem= -1 );
        BOOL                DeleteColumn( 
int nCol );
        BOOL                DeleteAllItems( );
        BOOL                DeleteItem( 
int nItem );

// operations
    public:
        
static BOOL        DrawGradientRect(CDC *pDC, CRect &rect, COLORREF crTopLeft, COLORREF crBottomRight);
        BOOL                GetCheckRect(
int iRow, int iCol, CRect &rect);
        BOOL                GetCellRect(CellIndex ix, CRect 
&rect);
        BOOL                GetCellRect(
int iRow, int iCol, CRect &rect, int nArea=LVIR_BOUNDS);
        BOOL                GetRowRect(
int nRow, CRect &rcRow);
        BOOL                GetColRect(
int nCol, CRect &rcCol);
        
int                    GetColumnCount() { return GetHeaderCtrl()->GetItemCount(); }
        
int                    GetRowCount() { return GetItemCount(); }

        ColumnType    GetColumnType(
int nColIndex);
        
void                SetColumnType(int nColIndex, ColumnType eColType);
        BOOL                IsSupportSort();
        
void                SetSupportSort(BOOL bSuptSort=TRUE);
        SortType        GetColumnSortBy(
int nColIndex);
        
void                SetColumnSortBy(int nColIndex, SortType eSortBy=SortByString);
        BOOL                IsColumnSortAsc(
int nColIndex);
        
// for all cell types
        BOOL                GetCellEnabled(int nRow, int nCol);
        
void                SetCellEnabled(int nRow, int nCol, BOOL bEnabled=TRUE);
        
// for normal & edit box
        void                SetCellImage(int nRow, int nCol, int nImage=-1);
        
// for radio & check box
        BOOL                GetCellChecked(int nRow, int nCol);
        
void                SetCellChecked(int nRow, int nCol, BOOL bChecked=TRUE);
        
// for radio only
        int                    GetCellRadioGrp(int nRow, int nCol);
        
void                SetCellRadioGrp(int nRow, int nCol, int nGroupNO=0);
        
// for combo box only
        void                SetCellStringList(int nRow, int nCol, CStrList &lstStrings);
        
void                AddCellString(int nRow, int nCol, CString &str);
        
// for edit box only
        void                SetCellValidChars(int nRow, int nCol, CString &strValidChars);
        
// for progress bar only
        BOOL                IsShowPogressPercent();
        
void                SetShowProgressPercent(BOOL bShowProgPercent=TRUE);
        
void                SetCellProgressMaxValue(int nRow, int nCol, UINT uMaxValue=100);
        
void                SetCellProgressValue(int nRow, int nCol, UINT uValue);
        
//
        CellIndex            Point2Cell(const CPoint &point);

    
protected:
        
virtual void        PreSubclassWindow();
        DECLARE_MESSAGE_MAP()
        
// message handlers
        afx_msg void    OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
        afx_msg 
void    OnLButtonDown(UINT nFlags, CPoint point);
        afx_msg 
void    OnLButtonDblClk(UINT nFlags, CPoint point);
        afx_msg 
void    OnRButtonDown(UINT nFlags, CPoint point);
        afx_msg 
void    OnLvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
        afx_msg 
void    OnLvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
        afx_msg 
void    OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult);
        afx_msg 
void    OnDestroy();
    
// implementation
    private:
        inline 
void        DrawCell(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, 
                                            
int nRow, int nCol, LRESULT *pResult);
        
void                DrawNormal(CDC *pDC, CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData, int uLvcFmt=LVCFMT_CENTER);
        
void                DrawCheckBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData);    
        
void                DrawRadioBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData);
        
void                DrawComboBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData);
        
void                DrawEditBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData, int uLvcFmt=LVCFMT_CENTER);
        
void                DrawProgress(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData);

        
int                    GetColumnFmt(int nCol);
        inline 
void        ShowCellInPlace(CellIndex ix, CellType eCellType);
        
void                ShowInPlaceCombo(CellIndex ix);
        
void                ShowInPlaceEdit(CellIndex ix);
    
private:    
        
void                SetColumnSortDirection(int nColIndex, BOOL bIsAsc=TRUE);
        
int                    GetProgBarSize(const CellData &cellData, CRect &rcProg, float *pPersent=NULL);
        CImageList
*    TheImageList();
        CellData
&        FindCellData(CellIndex ix);
        CellData
&        FindCellData(int nRow, int nCol);
        CellDataMap    m_mapCell2Data;
        ColMap            m_mapCol2ColType;
        DWORD            m_dwEditStyle;
        DWORD            m_dwComboStyle;
        
int                    m_nImageListType;
        DWORD            m_dwListCtrlExStyle; 
// 

    
protected:
        
struct _ColumnSort_t
        {
            _ColumnSort_t(SortType eSortType
=SortByString, BOOL bIsAsc=TRUE)
                :m_eSortType(eSortType), m_bIsAsc(bIsAsc)
            {};
            SortType    m_eSortType;
            BOOL            m_bIsAsc;
        };

        
struct _SortParam_t 
        {
            _ColumnSort_t  m_ColParam;
            
int                     m_nSortCol;
            CListCtrlEx         
*m_pTheList;
        };
        typedef map
<int, _ColumnSort_t> ColSortMap;
        ColSortMap                m_mapCol2Sort;    
        CListCtrlExSortHead    m_ctlSortHead;
        
static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,  LPARAM lParamSort);
        
static bool        IsDigitStr(LPCTSTR str);
        
static int        IntCompare(LPCTSTR strInt1st, LPCTSTR strInt2nd);
        
static int        StrCompare(LPCTSTR str1st, LPCTSTR str2nd);
        
void                PreSortItems( vector<DWORD_PTR> &vec2StoreData );
        
void                PostSortItems( vector<DWORD_PTR> &vec2StoreData );
};

}
#endif

 // ListCtrlEx.cpp

ContractedBlock.gif ExpandedBlockStart.gif Code

#include 
"stdafx.h"
#include 
"ListCtrlEx.h"

#include 
"PerfTimer.h"
using namespace ListCtrlEx;
#define IDC_CELL_EDIT         1000001
#define IDC_CELL_COMBO  1000002
#define IDC_PROGRESS_START IDC_CELL_COMBO+1

#define COLOR_FRAME #336699
#define COLOR_MARK #33ff99

#define SHOW_PROGRESS_PERCENT 0x00000001
#define SUPPORT_SORT    0x00000002
//////////////////////////////////////////////////////////////////////////
// CListCtrlEx
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)

CListCtrlEx::CListCtrlEx()
{
    m_nImageListType
=LVSIL_NORMAL;
    m_dwListCtrlExStyle
=0;
    m_dwEditStyle 
= ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_NOHIDESEL;
    m_dwComboStyle 
= WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | 
        CBS_DROPDOWNLIST 
| CBS_DISABLENOSCROLL;
}

CListCtrlEx::
~CListCtrlEx()
{
    CInPlaceCombo::DeleteInstance();
    CInPlaceEdit::DeleteInstance();
}

BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, 
&CListCtrlEx::OnNMCustomdraw)
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_RBUTTONDOWN()
    ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, 
&CListCtrlEx::OnLvnBeginlabeledit)
    ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, 
&CListCtrlEx::OnLvnEndlabeledit)
    ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, 
&CListCtrlEx::OnLvnColumnclick)
    ON_WM_DESTROY()
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////
// override CListCtrl interfaces
CImageList* CListCtrlEx::SetImageList( CImageList* pImageList, int nImageListType )
{
    m_nImageListType
=nImageListType;
    
return CListCtrl::SetImageList(pImageList, nImageListType);
}

int CListCtrlEx::InsertColumn(int nCol, CString strColHead, int nWidth, ColumnType eColType, int nFormat, 
                                        SortType eSortBy, 
int nSubItem )
{
    
//
    m_mapCol2ColType[nCol]=eColType;
    m_mapCol2Sort[nCol].m_eSortType
=eSortBy;
    
return CListCtrl::InsertColumn(nCol, strColHead, nFormat, nWidth, nSubItem);
}

BOOL CListCtrlEx::DeleteColumn( 
int nCol  )
{
    
if (!CListCtrl::DeleteColumn(nCol))
    {
        
return FALSE;
    }
    m_mapCol2ColType.erase(nCol);
    
return TRUE;
}

BOOL    CListCtrlEx::DeleteAllItems( )
{
    
if (!CListCtrl::DeleteAllItems())
    {
        
return FALSE;
    }
    m_mapCell2Data.clear();
    
return TRUE;
}

BOOL CListCtrlEx::DeleteItem( 
int nItem )
{
    
if (!CListCtrl::DeleteItem(nItem))
    {
        
return FALSE;
    }
    
// erase all of the row
    int nColCnt=GetColumnCount();
    
for (int nCol=0; nCol<nColCnt; ++nCol)
    {
        m_mapCell2Data.erase(make_pair(nItem, nCol));
    }
    
    
return TRUE;
}
// public interfaces
CListCtrlEx::CellIndex CListCtrlEx::Point2Cell(const CPoint &point)
{
    LVHITTESTINFO lvHitTestInfo;
    CRect rcItem;
    lvHitTestInfo.pt 
= point;
    
if (HitTest(&lvHitTestInfo) >= 0 && lvHitTestInfo.iItem >= 0)
    {
        
int nRow = lvHitTestInfo.iItem;
        
int nColCnt=GetColumnCount();
        
for (int nCol=0; nCol<nColCnt; ++nCol)
        {
            
if (GetCellRect(nRow, nCol, rcItem))
            {
                
if (rcItem.PtInRect(point))
                {
                    
return make_pair(nRow, nCol);
                }
            }
        }
    }
    
return make_pair(-1-1);
}
//////////////////////////////////////////////////////////////////////////
// need to deal with delete column
void CListCtrlEx::SetColumnType(int nColIndex, ColumnType eColType)
{
    ASSERT(
0<=nColIndex && nColIndex<GetColumnCount());
    
// init if empty
    if (m_mapCol2ColType.empty())
    {
        
for (int i=0; i<GetColumnCount(); ++i)
        {
            m_mapCol2ColType.insert(ColMap::value_type(i, Normal));
        }
    }
    
// assign
    m_mapCol2ColType[nColIndex]=eColType;
    
// maybe should update window here
}

CListCtrlEx::ColumnType CListCtrlEx::GetColumnType(
int nColIndex)
{
    
// insert new if not found
    ColMap::iterator iter=m_mapCol2ColType.find(nColIndex);
    
if (iter==m_mapCol2ColType.end())
    {
        m_mapCol2ColType[nColIndex]
=Normal;
    }
    
return m_mapCol2ColType[nColIndex];
}

SortType CListCtrlEx::GetColumnSortBy(
int nColIndex)
{
    
// insert new if not found
    ColSortMap::iterator iter=m_mapCol2Sort.find(nColIndex);
    
if (m_mapCol2Sort.end()==iter)
    {
        m_mapCol2Sort[nColIndex]
=_ColumnSort_t();
    }
    
return m_mapCol2Sort[nColIndex].m_eSortType;
}

void CListCtrlEx::SetColumnSortBy(int nColIndex, SortType eSortBy)
{
    
// init sort map
    if (m_mapCol2Sort.empty())
    {
        
for (int i=0; i<GetColumnCount(); ++i)
        {
            m_mapCol2Sort[i]
=_ColumnSort_t();
        }
    }
    
// assign
    m_mapCol2Sort[nColIndex].m_eSortType=eSortBy;
}
//////////////////////////////////////////////////////////////////////////
// need to deal with delete items
void CListCtrlEx::SetCellEnabled(int nRow, int nCol, BOOL bEnabled)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    
//FindCellData(nRow, nCol).m_bEnabled=bEnabled;
    FindCellData(nRow, nCol).SetEnable(bEnabled);
    CRect rcCell;
    
if(GetCellRect(nRow, nCol, rcCell))
    {
        InvalidateRect(
&rcCell);
        UpdateWindow();
        
// send message to parent
        CWnd *pWnd=NULL;
        
if ((pWnd = GetParent())!=NULL )
        {
            EnableCellMsg msg(nRow, nCol, bEnabled);
            pWnd
->SendMessage(WM_ListCtrlEx_ENABLE_CHANGED, (WPARAM)this, (LPARAM)&msg);
        }
    }
}

BOOL CListCtrlEx::GetCellEnabled(
int nRow, int nCol)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    
return FindCellData(nRow, nCol).GetEnable();
}


void CListCtrlEx::SetCellImage(int nRow, int nCol, int nImage)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    FindCellData(nRow, nCol).m_nImage
=nImage;
}

void CListCtrlEx::SetCellChecked(int nRow, int nCol, BOOL bChecked)
{
    
int nColCnt=GetColumnCount();
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol<nColCnt );
    ColumnType eColType
=m_mapCol2ColType[nCol];
    
// only check box and radio box can be checked
    if (CheckBox==eColType)
    {
        FindCellData(nRow, nCol).SetCheck(bChecked);
        CRect rcCell;
        
if(GetCellRect(nRow, nCol, rcCell))
        {
            InvalidateRect(
&rcCell); // update check box here immediately
            UpdateWindow();
            
// send message to parent
            CWnd *pWnd=NULL;
            
if ((pWnd = GetParent())!=NULL )
            {
                CheckCellMsg msg(nRow, nCol, bChecked);
                pWnd
->SendMessage(WM_ListCtrlEx_CHECKCELL, (WPARAM)this, (LPARAM)&msg);
            }
        }
    }
    
else if(RadioBox==eColType)
    {
        
int nGrpNo=FindCellData(nRow, nCol).m_nRadioGroup;
        
int nRowCnt=GetItemCount();
        CRect rcCell;
        
for (int y=0; y<nColCnt; ++y)
        {
            
if (RadioBox==m_mapCol2ColType[y])
            {
                
for (int x=0; x<nRowCnt; ++x)
                {
                    
if (nGrpNo==FindCellData(x,y).m_nRadioGroup && FindCellData(x,y).GetCheck()) // only need to update checked one
                    {
                        FindCellData(x,y).SetCheck(FALSE);
                        
if(GetCellRect(x, y, rcCell))
                        {
                            InvalidateRect(
&rcCell); // update radio box here immediately
                            UpdateWindow();    
                        }
                    }
                }
            }
        }
        bChecked
=TRUE; // check on a radio will always cause to be true
        FindCellData(nRow, nCol).SetCheck(bChecked); 
        
if(GetCellRect(nRow, nCol, rcCell))
        {
            InvalidateRect(
&rcCell); // update radio box here immediately
            UpdateWindow();    
        }
        
// send message to parent
        CWnd *pWnd=NULL;
        
if ((pWnd = GetParent())!=NULL )
        {
            CheckCellMsg msg(nRow, nCol, bChecked);
            pWnd
->SendMessage(WM_ListCtrlEx_CHECKCELL, (WPARAM)this, (LPARAM)&msg);
        }
    }
    
}

BOOL CListCtrlEx::GetCellChecked(
int nRow, int nCol)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    
return FindCellData(nRow, nCol).GetCheck();
}

int    CListCtrlEx::GetCellRadioGrp(int nRow, int nCol)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    
return FindCellData(nRow, nCol).m_nRadioGroup;
}

void CListCtrlEx::SetCellRadioGrp(int nRow, int nCol, int nGroupNO)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    FindCellData(nRow, nCol).m_nRadioGroup
=nGroupNO;
}

void CListCtrlEx::SetCellStringList(int nRow, int nCol, CStrList &lstStrings)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    CStrList 
&aCellStrList=FindCellData(nRow, nCol).m_lstString;
    aCellStrList.clear();
    aCellStrList.insert(aCellStrList.begin(), lstStrings.begin(), lstStrings.end());
}

void CListCtrlEx::AddCellString(int nRow, int nCol, CString &str)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    CStrList 
&aCellStrList=FindCellData(nRow, nCol).m_lstString;
    aCellStrList.push_back(str);
}

void CListCtrlEx::SetCellValidChars(int nRow, int nCol, CString &strValidChars)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    FindCellData(nRow, nCol).m_strValidChars
=strValidChars;
}

void CListCtrlEx::SetCellProgressMaxValue(int nRow, int nCol, UINT uMaxValue)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    FindCellData(nRow, nCol).m_uProgressMax
=uMaxValue;
    CRect rcCell;
    
if(GetCellRect(nRow, nCol, rcCell))
    {
        InvalidateRect(
&rcCell); // update progress bar here immediately
        UpdateWindow();    
    }
}

void CListCtrlEx::SetCellProgressValue(int nRow, int nCol, UINT uValue)
{
    ASSERT(
0<=nRow && nRow<GetItemCount() && nCol>=0 && nCol< GetColumnCount());
    FindCellData(nRow, nCol).m_uProgresVal
=uValue;
    CRect rcCell;
    
if(GetCellRect(nRow, nCol, rcCell))
    {
        InvalidateRect(
&rcCell); // update progress bar here immediately
        UpdateWindow();    
    }
}

BOOL CListCtrlEx::IsShowPogressPercent()

    
return m_dwListCtrlExStyle & SHOW_PROGRESS_PERCENT; 
}
void CListCtrlEx::SetShowProgressPercent(BOOL bShowProgPercent)
{
    
if (bShowProgPercent)
        m_dwListCtrlExStyle
|=SHOW_PROGRESS_PERCENT;
    
else
        m_dwListCtrlExStyle
&=(~SHOW_PROGRESS_PERCENT);
}

BOOL CListCtrlEx::IsSupportSort()
{
    
return m_dwListCtrlExStyle & SUPPORT_SORT;
}

void CListCtrlEx::SetSupportSort(BOOL bSuptSort)
{
    
if (bSuptSort)
        m_dwListCtrlExStyle
|=SUPPORT_SORT;
    
else
        m_dwListCtrlExStyle
&=(~SUPPORT_SORT);
}

//////////////////////////////////////////////////////////////////////////
BOOL CListCtrlEx::GetCellRect(CellIndex ix, CRect &rect)
{
    
return GetCellRect(ix.first, ix.second, rect);
}

BOOL CListCtrlEx::GetCellRect(
int iRow, int iCol, CRect &rect, int nArea)
{
    ASSERT(
0<=iRow && iRow<GetItemCount() && iCol>=0 && iCol< GetColumnCount());
    
if(iCol)
        
return GetSubItemRect(iRow, iCol, nArea, rect);

    
if(GetColumnCount()== 1)
        
return GetItemRect(iRow, rect, nArea);

    iCol 
= 1;
    CRect rCol1;
    
if(!GetSubItemRect(iRow, iCol, nArea, rCol1))
        
return FALSE;

    
if(!GetItemRect(iRow, rect, nArea))
        
return FALSE;

    rect.right 
= rCol1.left;

    
return TRUE;
}

BOOL    CListCtrlEx::GetCheckRect(
int iRow, int iCol, CRect &rect)
{
    
// this rect is to do action rect, so it's larger than the draw box
    if (GetCellRect(iRow, iCol, rect))
    {
        
//rect.bottom -= 1;
        
//rect.top+=1;
        rect.left += 7;        
        rect.right 
= rect.left + rect.Height();    // width = height
        return TRUE;
    }
    
return FALSE;
}



BOOL    CListCtrlEx::GetRowRect(
int nRow, CRect &rcRow)
{
    ASSERT(
0<=nRow && nRow<GetItemCount());
    
return GetItemRect(nRow, rcRow, LVIR_BOUNDS);
}

BOOL    CListCtrlEx::GetColRect(
int nCol, CRect &rcCol)
{
    ASSERT(nCol
>=0 && nCol< GetColumnCount());
    rcCol 
=CRect(0,0,0,0);
    CRect rcCellInCol(
0,0,0,0);
    
int nRowCnt=GetItemCount();
    
for (int i=0; i<nRowCnt; ++i)
    {
        
if (GetCellRect(i, nCol, rcCellInCol))
        {
            
if (0==i)
            {
                rcCol
=rcCellInCol;
            }
            
else
            {
                rcCol.UnionRect(
&rcCol, &rcCellInCol);
            }
        }
        
else
        {
            
return FALSE;
        }
    }
    
return TRUE;
}

int CListCtrlEx::GetColumnFmt(int nCol)
{
    ASSERT(
0<=nCol && nCol<GetColumnCount());
    LVCOLUMN lvColumn;
    ZeroMemory(
&lvColumn, sizeof(lvColumn));
    
if (GetColumn(nCol, &lvColumn))
    {
        
return lvColumn.fmt;
    }
    
return LVCFMT_CENTER;
}
//////////////////////////////////////////////////////////////////////////
// customer draw
void CListCtrlEx::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
    
//FuncTime(_T("OnNMCustomdraw"));
    
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR);
    LPNMLVCUSTOMDRAW  lplvcd = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    
int iCol = lplvcd->iSubItem;
    
int iRow = (int)lplvcd->nmcd.dwItemSpec;
    
switch(lplvcd->nmcd.dwDrawStage)
    {
    
case CDDS_PREPAINT:
        
*pResult = CDRF_NOTIFYSUBITEMDRAW;          // ask for subitem notifications.
        break;
    
case CDDS_ITEMPREPAINT:
        
*pResult = CDRF_NOTIFYSUBITEMDRAW;          // ask for subitem notifications.
        break;
    
case CDDS_ITEMPREPAINT|CDDS_SUBITEM:            // recd when CDRF_NOTIFYSUBITEMDRAW is returned in
        {    
            
// response to CDDS_ITEMPREPAINT.
            *pResult = CDRF_DODEFAULT;            
            CString strText 
= GetItemText(iRow, iCol);
            CRect rcCell;
            
if (GetCellRect(iRow, iCol, rcCell))
            {
                
bool bSelected=GetItemState(iRow, LVIS_SELECTED) == LVIS_SELECTED;
                
// get the device context.
                CDC *pDC= CDC::FromHandle(lplvcd->nmcd.hdc);
                
// draw the cell.
                DrawCell(pDC, strText, rcCell, bSelected, iRow, iCol, pResult ); // *pResult will be changed here
            }    

            
break;
        }
    
default:                                                        // it wasn't a notification that was interesting to us.
        *pResult = CDRF_DODEFAULT;
        
break;
    }
    
return;
}

void CListCtrlEx::DrawCell(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, 
                                        
int nRow, int nCol, LRESULT *pResult)
{
    
switch(GetColumnType(nCol))
    {
    
default:
    
case Normal:
        
*pResult |= CDRF_SKIPDEFAULT;
        DrawNormal(pDC, strText, rcCell, bSelected, FindCellData(nRow, nCol), GetColumnFmt(nCol));
        
return;
    
case CheckBox:
        
*pResult |= CDRF_SKIPDEFAULT;
        DrawCheckBox(pDC, strText, rcCell, bSelected, FindCellData(nRow, nCol));
        
return;
    
case RadioBox:
        
*pResult |= CDRF_SKIPDEFAULT;
        DrawRadioBox(pDC, strText, rcCell, bSelected, FindCellData(nRow, nCol));
        
return;
    
case ComboBox:
        
*pResult |= CDRF_SKIPDEFAULT;
        DrawComboBox(pDC, strText, rcCell, bSelected, FindCellData(nRow, nCol));
        
return;
    
case EditBox:
        
*pResult|=CDRF_SKIPDEFAULT;
        DrawEditBox(pDC, strText, rcCell, bSelected, FindCellData(nRow, nCol), GetColumnFmt(nCol));
        
return;
    
case Progress:
        
*pResult|=CDRF_SKIPDEFAULT;
        DrawProgress(pDC, strText, rcCell, bSelected, FindCellData(nRow,nCol));
        
return;
    }
}

// draw normal text
void CListCtrlEx::DrawNormal(CDC *pDC, CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData, int uLvcFmt)
{
    UNREFERENCED_PARAMETER(cellData);
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    
// fill background
    CRect rcText(rcCell), rcIcon(rcCell);
    pDC
->FillSolidRect(&rcCell, crTextBkgrnd);
    
//-- draw icon--//
    CImageList *pImageList=TheImageList();
    
if (cellData.m_nImage>=0 && pImageList)
    {
        rcIcon.right
=rcIcon.left+rcIcon.Height();
        
//UINT fStyle=bSelected?ILD_NORMAL:(ILD_NORMAL|ILD_SELECTED);
        pImageList->DrawIndirect(    pDC, cellData.m_nImage, rcIcon.TopLeft(), rcIcon.Size(), CPoint(00), ILD_NORMAL, 
                                            SRCCOPY,crTextBkgrnd, crTextBkgrnd );
        
//pImageList->DrawIndirect(    pDC, cellData.m_nImage, rcIcon.TopLeft(), rcIcon.Size(), CPoint(0, 0));
    }
    
else
    {
        rcIcon.right
=rcIcon.left;
    }
    
//------------draw text---------------------/
    UINT nFormat=DT_CENTER;
    
switch(uLvcFmt)
    {
    
default:
    
case LVCFMT_CENTER: break;
    
case LVCFMT_LEFT: nFormat=DT_LEFT; break;
    
case LVCFMT_RIGHT: nFormat=DT_RIGHT; break;
    }
    COLORREF crOldText
=pDC->SetTextColor(crText);
    rcText.left
=rcIcon.right+3;
    pDC
->DrawText(strText, &rcText, nFormat);
    pDC
->SetTextColor(crOldText);
}

// draw a check box cell
void CListCtrlEx::DrawCheckBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData)
{
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    COLORREF crCheckFrame
=cellData.GetEnable()?RGB(51,102,153):RGB(192,192,192);
    COLORREF crCheckMark
=cellData.GetEnable()?RGB(51,255,153):RGB(192,192,192);
    
//------------calculate the check box & Text rect ---------------------/
    CRect rcText(rcCell);
    CRect rcCheck(rcCell);
    rcCheck.bottom 
-= 2;
    rcCheck.top
+=2;
    rcCheck.left 
+= 7;        
    rcCheck.right 
= rcCheck.left + rcCheck.Height();    // width = height
    rcText.left=rcCheck.right+3;
    
//------------draw the inside check box region---------------------/
    pDC->FillSolidRect(&rcText, crTextBkgrnd); 
    pDC
->FillSolidRect(&rcCheck, GetSysColor(COLOR_WINDOW)); // refresh the check box
    
//------------draw the check box frame border ---------------------/
#ifdef USING_CUSTOM_DRAW
    
//CBrush brush(crCheckFrame);
    
//pDC->FrameRect(&rcCheck, &brush); //fill frame of the check box
    CPen penFrame(PS_SOLID, 2,crCheckFrame);
    CPen 
*pOldPen=pDC->SelectObject(&penFrame);
    pDC
->Rectangle(&rcCheck);
    
//------------draw the check mark---------------------/
    if (cellData.GetCheck() ) // draw the checked Mark
    {
        
int deta=rcCheck.Width()/4;
        CPen penMark(PS_SOLID, 
1,crCheckMark);
        pDC
->SelectObject(&penMark);
        
int x = rcCheck.left + deta*3;
        
int y = rcCheck.top + deta;
        
int i;
        
for (i = 0; i < 4; i++)
        {
            pDC
->MoveTo(x, y);
            pDC
->LineTo(x, y+3);
            x
--;
            y
++;
        }
        
for (i = 0; i < 3; i++)
        {
            pDC
->MoveTo(x, y);
            pDC
->LineTo(x, y+3);
            x
--;
            y
--;
        }
        
    }
    pDC
->SelectObject(pOldPen);
#else
    crCheckMark;crCheckFrame; 
// only for kill the warnings
    UINT nState=DFCS_BUTTONCHECK;
    
if (cellData.GetCheck() ) // draw the checked Mark
    {
        nState
|=DFCS_CHECKED;
    }
    
if (bSelected)
    {
        nState
|=DFCS_HOT;
    }
    
if (!cellData.GetEnable())
    {
        nState
|=DFCS_INACTIVE;
    }
    pDC
->DrawFrameControl(&rcCheck, DFC_BUTTON, nState);
#endif
    
//------------draw text---------------------/
    COLORREF crOldText=pDC->SetTextColor(crText);
    pDC
->DrawText(strText, &rcText, DT_LEFT);
    pDC
->SetTextColor(crOldText);
}
// draw a Radio box cell
void CListCtrlEx::DrawRadioBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData)
{
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    COLORREF crRadioFrame
=cellData.GetEnable()?RGB(51,102,153):RGB(192,192,192);
    COLORREF crRadioFrameBk
=GetSysColor(COLOR_WINDOW);
    COLORREF crRadioMark
=cellData.GetEnable()?RGB(0,0,0):RGB(192,192,192);
    
//------------calculate the check box & Text rect ---------------------/
    CRect rcText(rcCell);
    CRect rcRadio(rcCell);
    rcRadio.bottom 
-= 1;
    rcRadio.top
+=1;
    rcRadio.left 
+= 7;        
    rcRadio.right 
= rcRadio.left + rcRadio.Height();    // width = height
    rcText.left=rcRadio.right+3;
    
//------------draw the inside radio box region---------------------/
    pDC->FillSolidRect(&rcText, crTextBkgrnd); 
    pDC
->FillSolidRect(&rcRadio, crRadioFrameBk); // refresh the check box
    
//------------draw the radio box frame border ---------------------/
#ifdef USING_CUSTOM_DRAW
    
// using three dot to draw the radio box
    CPoint pos=rcRadio.TopLeft();    
    pos 
+=CSize(rcRadio.Width()/2, rcRadio.Height()/2);
    
int nPenWid=rcRadio.Height();
    
// ----draw frame----/
    
// outer frame
    CPen pen(PS_DOT, nPenWid, crRadioFrame); 
    CPen 
*pOldPen=pDC->SelectObject(&pen);
    pDC
->MoveTo(pos);
    pDC
->LineTo(pos);
    
// inner frame
    pDC->SelectObject(pOldPen);
    pen.DeleteObject();
    nPenWid
=((nPenWid-4)>0)?(nPenWid-4):4;
    pen.CreatePen(PS_DOT, nPenWid, crRadioFrameBk);
    pDC
->SelectObject(&pen);
    pDC
->MoveTo(pos);
    pDC
->LineTo(pos);
    
// draw mark
    if (cellData.GetCheck())
    {
        pDC
->SelectObject(pOldPen);
        pen.DeleteObject();
        nPenWid
=((nPenWid-4)>0)?(nPenWid-4):4;
        pen.CreatePen(PS_DOT, nPenWid, crRadioMark);
        pDC
->SelectObject(&pen);
        pDC
->MoveTo(pos);
        pDC
->LineTo(pos);
    }
    pDC
->SelectObject(pOldPen);
#else
    crRadioFrame;crRadioMark; 
// only for kill the warnings
    UINT nState=DFCS_BUTTONRADIO;
    
if (cellData.GetCheck() ) // draw the checked Mark
    {
        nState
|=DFCS_CHECKED;
    }
    
if (!cellData.GetEnable())
    {
        nState
|=DFCS_INACTIVE;
    }
    pDC
->DrawFrameControl(&rcRadio, DFC_BUTTON, nState);
#endif
    
//------------draw text---------------------/
    COLORREF crOldText=pDC->SetTextColor(crText);
    pDC
->DrawText(strText, &rcText, DT_LEFT);
    pDC
->SetTextColor(crOldText);
    
}
// draw a combo box cell
void CListCtrlEx::DrawComboBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData)
{
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    COLORREF crComboTL
=cellData.GetEnable()?RGB(255,255,255):RGB(192,192,192); //#d4e1fc:#c0c0c0;
    COLORREF crComboBR=cellData.GetEnable()? RGB(177,203,250):RGB(192,192,192);
    COLORREF crComboMark
=cellData.GetEnable()?RGB(77,97,133):RGB(0,0,0);
    
//------------calculate the check box & Text rect ---------------------/
    CRect rcText(rcCell);
    CRect rcCombo(rcCell);
    rcCombo.bottom 
-= 0;
    rcCombo.top
+=1;
    rcCombo.right 
-= 2;        
    rcCombo.left 
= rcCombo.right - rcCombo.Height();    // width = height
    rcText.right=rcCombo.left-3;
    rcText.left
+=2;
    
//---------refresh the cell------/
    pDC->FillSolidRect(&rcCell, GetSysColor(COLOR_WINDOW));
    pDC
->FillSolidRect(&rcText, crTextBkgrnd); 
    
//------------draw the combo box frame border ---------------------/
#ifdef USING_CUSTOM_DRAW
    CBrush brBottomRight(crComboBR);
    CBrush 
*pOldBrush=pDC->SelectObject(&brBottomRight);
    
/*CRect rcInner(rcCombo);
    rcInner.DeflateRect(1,1,1,1);
*/
    
//pDC->FillSolidRect(&rcInner, crComboBR);
    DrawGradientRect(pDC, rcCombo,  crComboTL, crComboBR);
    
//pDC->Draw3dRect(&rcCombo, crComboTL, crComboBR);
    pDC->SelectObject(pOldBrush);
    
    CPen penMark(PS_SOLID, 
1, crComboMark);
    CPen 
*pOldPen=pDC->SelectObject(&penMark);
    
// draw the arrow
    {
        
int xOrg=rcCombo.Width()/2+rcCombo.left;
        
int yOrg=rcCombo.Height()/2+rcCombo.top+2;
        
int xOff=max(rcCombo.Width()/4,1);
        
int yOff=max(rcCombo.Height()/4,1);
        
int y=yOrg-yOff;
        
for (int x=(xOrg-xOff); x<xOrg; ++x, ++y)
        {
            pDC
->MoveTo(x,y);
            pDC
->LineTo(x+2*(xOrg-x), y);
        }
    }

    pDC
->SelectObject(pOldPen);

#else
    crComboTL;crComboBR;crComboMark;  
// only for kill the warnings
    UINT nState=DFCS_SCROLLCOMBOBOX;
    
if (cellData.GetCheck() ) // draw the checked Mark
    {
        nState
|=DFCS_CHECKED;
    }
    
if (bSelected)
    {
        nState
|=DFCS_HOT;
    }
    
if (!cellData.GetEnable())
    {
        nState
|=DFCS_INACTIVE;
    }
    pDC
->DrawFrameControl(&rcCombo, DFC_SCROLL, nState);

#endif
    
//------------draw text---------------------/
    COLORREF crOldText=pDC->SetTextColor(crText);
    pDC
->DrawText(strText, &rcText, DT_LEFT);
    pDC
->SetTextColor(crOldText);
}


// draw a edit box cell
void CListCtrlEx::DrawEditBox(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData, int uLvcFmt)
{
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    COLORREF crTextUnabled
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):RGB(192,192,192);

    CRect rcText(rcCell), rcIcon(rcCell);
    
// fill background
    pDC->FillSolidRect(&rcCell, crTextBkgrnd);
    
//-- draw icon--//
    CImageList *pImageList=TheImageList();
    
if (cellData.m_nImage>=0 && pImageList)
    {
        rcIcon.right
=rcIcon.left+rcIcon.Height();
        pImageList
->DrawIndirect(    pDC, cellData.m_nImage, rcIcon.TopLeft(), rcIcon.Size(), CPoint(00), ILD_NORMAL, 
                                    SRCCOPY,crTextBkgrnd, crTextBkgrnd );
    }
    
else
    {
        rcIcon.right
=rcIcon.left;
    }
#ifdef USING_CUSTOM_DRAW
#else
#endif    
    
//------------draw text---------------------/
    UINT nFormat=DT_CENTER;
    
switch(uLvcFmt)
    {
    
default:
    
case LVCFMT_CENTER: break;
    
case LVCFMT_LEFT: nFormat=DT_LEFT; break;
    
case LVCFMT_RIGHT: nFormat=DT_RIGHT; break;
    }
    COLORREF crOldText
=pDC->SetTextColor(crText);
    
if (!cellData.GetEnable())
    {
        pDC
->SetTextColor(crTextUnabled);
    }
    rcText.left
=rcIcon.right+3;
    pDC
->DrawText(strText, &rcText, nFormat);
    pDC
->SetTextColor(crOldText);
}

// draw a progress bar
void CListCtrlEx::DrawProgress(CDC *pDC,  CString &strText, CRect &rcCell, BOOL bSelected, const CellData &cellData)
{
    COLORREF crText
=bSelected?GetSysColor(COLOR_HIGHLIGHTTEXT):GetSysColor(COLOR_WINDOWTEXT);
    COLORREF crTextBkgrnd
=bSelected?GetSysColor(COLOR_HIGHLIGHT):GetSysColor(COLOR_WINDOW);
    COLORREF crProgBack
=bSelected?GetSysColor(COLOR_HOTLIGHT):GetSysColor(COLOR_BTNFACE);
    COLORREF crProgFill
=bSelected?COLOR_FRAME:COLOR_FRAME;

    CRect rcProg(rcCell), rcIcon(rcCell);
    
// fill background
    pDC->FillSolidRect(&rcCell, crTextBkgrnd);
    
//-- draw icon--//
    CImageList *pImageList=TheImageList();
    
if (cellData.m_nImage>=0 && pImageList)
    {
        rcIcon.right
=rcIcon.left+rcIcon.Height();
        pImageList
->DrawIndirect(    pDC, cellData.m_nImage, rcIcon.TopLeft(), rcIcon.Size(), CPoint(00), ILD_NORMAL, 
            SRCCOPY,crTextBkgrnd, crTextBkgrnd );
    }
    
else
    {
        rcIcon.right
=rcIcon.left;
    }
    rcProg.left
=rcIcon.right+3;


    pDC
->FillSolidRect(&rcProg, crProgBack);
    pDC
->DrawEdge(&rcProg, EDGE_SUNKEN, BF_RECT);
    CRect rcFill(rcProg);
    rcFill.DeflateRect(
1,1,1,1);
    
float fPersent=0.0f;
    
int nFill=GetProgBarSize(cellData, rcProg, &fPersent);
    rcFill.right
=min(rcFill.left+nFill, rcFill.right);
    pDC
->FillSolidRect(&rcFill, crProgFill);
    COLORREF crOldText
=pDC->SetTextColor(crText);

    
if (IsShowPogressPercent())
    {
        strText.Format(_T(
"%.2f%%"), fPersent*100);
    }
    pDC
->DrawText(strText, &rcProg, DT_CENTER);
    pDC
->SetTextColor(crOldText);
#ifdef USING_CUSTOM_DRAW    
#else
#endif    
    
}
//////////////////////////////////////////////////////////////////////////
// kernel data 
int    CListCtrlEx::GetProgBarSize(const CListCtrlEx::CellData &cellData, CRect &rcProg, float *pPersent)
{
    
float fScale=0.0f;
    
if (cellData.m_uProgresVal>=cellData.m_uProgressMax)
        fScale
=1.0f;
    
else if(cellData.m_uProgresVal<=0 || cellData.m_uProgressMax<=0)
        fScale
=0.0f;
    
else
        fScale
=(float)cellData.m_uProgresVal/(float)cellData.m_uProgressMax;

    
if (pPersent)
    {
        
*pPersent=fScale;
    }
    
return (int)(rcProg.Width()*fScale);
}

CImageList
* CListCtrlEx::TheImageList()
{
    
return GetImageList(m_nImageListType);
}

CListCtrlEx::CellData
& CListCtrlEx::FindCellData(CellIndex ix)
{
    
return FindCellData(ix.first, ix.second);
}

CListCtrlEx::CellData
& CListCtrlEx::FindCellData(int nRow, int nCol)
{
    
// init if empty
    if (m_mapCell2Data.empty())
    {
        
for (int x=0; x<GetItemCount(); ++x)
        {
            
for (int y=0; y< GetColumnCount(); ++y)
            {
                m_mapCell2Data[make_pair(x,y)]
=CellData(TRUE, FALSE, y);
            }
        }
    }

    CellDataMap::iterator iter
=m_mapCell2Data.find(make_pair(nRow, nCol));
    
if (iter==m_mapCell2Data.end())
    {
        m_mapCell2Data[make_pair(nRow, nCol)]
=CellData(TRUE, FALSE, nCol);
    }
    
return m_mapCell2Data[make_pair(nRow, nCol)];
}

//////////////////////////////////////////////////////////////////////////
// show control
void CListCtrlEx::ShowCellInPlace(CListCtrlEx::CellIndex ix, CellType eCellType)
{
    
// roll up make the cell be visible
    if (!EnsureVisible(ix.first, TRUE))
    {
        
return;
    }
    
switch(eCellType)
    {
    
default:
    
case Normal:
    
case CheckBox:
    
case RadioBox:
        
return;
    
case ComboBox:
        ShowInPlaceCombo(ix);
        
break;
    
case EditBox:
        ShowInPlaceEdit(ix);
        
break;
    }
}

// show combo box in place
void CListCtrlEx::ShowInPlaceCombo(CellIndex ix)
{
    
if (ix.first>=0 && ix.first<GetItemCount() && ix.second>=0 && ix.second< GetColumnCount())
    {
        CString strCurSel
= GetItemText(ix.first, ix.second);
        CRect rcCell;
        
if (GetCellRect(ix, rcCell))
        {    
            GetParent()
->SendMessage(WM_SET_ITEMS, (WPARAM)this, (LPARAM)&ix);  
            CInPlaceCombo
* pInPlaceComboBox = CInPlaceCombo::GetInstance();
            ASSERT(pInPlaceComboBox); 
            pInPlaceComboBox
->ShowComboCtrl(m_dwComboStyle, rcCell, this, IDC_CELL_COMBO, 
                                                ix.first, ix.second, FindCellData(ix).m_lstString, strCurSel );
            pInPlaceComboBox
->SelectString(-1, strCurSel);
        }
    }
}

// show edit box in place
void CListCtrlEx::ShowInPlaceEdit(CellIndex ix)
{
    
if (ix.first>=0 && ix.first<GetItemCount() && ix.second>=0 && ix.second< GetColumnCount())
    {
        CString strCurSel
= GetItemText(ix.first, ix.second);
        CRect rcCell;
        
if (GetCellRect(ix, rcCell))
        {    
            GetParent()
->SendMessage(WM_SET_ITEMS, (WPARAM)this, (LPARAM)&ix);  
            CInPlaceEdit
* pInPlaceEdit = CInPlaceEdit::GetInstance();
            ASSERT(pInPlaceEdit); 
            pInPlaceEdit
->ShowEditCtrl(m_dwEditStyle, rcCell, this, IDC_CELL_EDIT, ix.first, ix.second,
                                                    FindCellData(ix).m_strValidChars, strCurSel);
        }
    }
}
//////////////////////////////////////////////////////////////////////////
// message handlers
void ListCtrlEx::CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
    
//FuncTime(_T("OnLButtonDown"));
    CWnd* pWnd=NULL;
    
bool bShouldAction=false;
    CellIndex ix
=Point2Cell(point);
    
    bShouldAction
=(ix.first>=0 && ix.second>=0 && ix.first<GetItemCount() && ix.second<GetColumnCount());
    
    CListCtrl::OnLButtonDown(nFlags, point);

    
// If the SHIFT or CTRL key is down call the base class
    
// Check the high bit of GetKeyState to determine whether SHIFT or CTRL key is down
    if ((GetKeyState(VK_SHIFT) & 0x80|| (GetKeyState(VK_CONTROL) & 0x80))
    {
        
return;
    }

    
if (bShouldAction && GetCellEnabled(ix.first, ix.second))
    {
        
/*CRect rcItem;
        GetRowRect(ix.first, rcItem);
*/
        
switch(m_mapCol2ColType[ix.second])
        {
        
case CheckBox:
        
case RadioBox:
            { 
// can check cell 
                CRect rcCheck;
                
bool bShouldCheck=(GetCheckRect(ix.first, ix.second, rcCheck) && rcCheck.PtInRect(point));
                
if (bShouldCheck)
                {
                    SetCellChecked(ix.first, ix.second, 
!GetCellChecked(ix.first, ix.second));                    
                    
if ((pWnd = GetParent())!=NULL)
                    {
                        CheckCellMsg msg(ix.first, ix.second, GetCellChecked(ix.first, ix.second));
                        pWnd
->SendMessage(WM_ListCtrlEx_CHECK_CHANGED, (WPARAM)this, (LPARAM)&msg);
                    }
                }        
            }
            
break;
        
case ComboBox:
        
case EditBox:
            { 
// can show or edit in place cell
                ShowCellInPlace(ix, m_mapCol2ColType[ix.second]);
            }
            
break;
        
case Normal:
        
default:            
            
break;
        }
        
//InvalidateRect(&rcItem); // normal or default should be update immediately for the focus state
        
//UpdateWindow();    
    }
    
// notify parent with user messages
    if ((pWnd = GetParent())!=NULL )
    {
        pWnd
->SendMessage(WM_ListCtrlEx_LBUTTONDOWN, (WPARAM)this, (LPARAM)&ix);
    }

    
    
}

void ListCtrlEx::CListCtrlEx::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    CWnd
* pWnd=NULL;
    
bool bShouldAction=false;
    CellIndex ix
=Point2Cell(point);
    bShouldAction
=(ix.first>=0 && ix.second>=0 && ix.first<GetItemCount() && ix.second<GetColumnCount());
    
// actions
    if (bShouldAction && GetCellEnabled(ix.first, ix.second))
    {
        CRect rcItem;
        GetRowRect(ix.first, rcItem);
        CRect rcCheck;
        
bool bShouldCheck=(GetCheckRect(ix.first, ix.second, rcCheck) && rcCheck.PtInRect(point));
        
if (bShouldCheck)
        {
            
//SetCellChecked(ix.first, ix.second, !GetCellChecked(ix.first, ix.second));
            
//InvalidateRect(&rcCheck); // should update window here immediately
            
//UpdateWindow();
        }    
        InvalidateRect(
&rcItem); // normal or default should be update immediately for the focus state
        UpdateWindow();
    }
    
// notify parent with user messages
    if ((pWnd = GetParent())!=NULL )
    {
        pWnd
->PostMessage(WM_ListCtrlEx_LBUTTONDBLCLK, (WPARAM)this, (LPARAM)&ix);
    }
    
// do default action
    CListCtrl::OnLButtonDblClk(nFlags, point);
    
}

void ListCtrlEx::CListCtrlEx::OnRButtonDown(UINT nFlags, CPoint point)
{
    CWnd
* pWnd=NULL;
    
bool bShouldAction=false;
    CellIndex ix
=Point2Cell(point);
    bShouldAction
=(ix.first>=0 && ix.second>=0 && ix.first<GetItemCount() && ix.second<GetColumnCount());
    
// actions

    
// notify parent with user messages
    if ((pWnd = GetParent())!=NULL )
    {
        pWnd
->PostMessage(WM_ListCtrlEx_RBUTTONDOWN,  (WPARAM)this, (LPARAM)&ix);
    }
    
// do default action
    CListCtrl::OnRButtonDown(nFlags, point);
    
    
//////////////////////////////////////////////////////////////////////////
    // for test
#ifdef _DEBUG
    
/*int nHot=GetHotItem();
    vector ivec;
    POSITION pos = GetFirstSelectedItemPosition();
    while (pos)
    {
        ivec.push_back(GetNextSelectedItem(pos));
    }
    CString str, strTmp;
    str.Format(_T("Hot item: %d \r\n"), nHot);
    vector::iterator iter=ivec.begin();
    for (; iter!=ivec.end(); ++iter)
    {
        strTmp.Format(_T("Selected item: %d\r\n"), *iter);
        str+=strTmp;
    }
    strTmp.Format(_T("Click point cell: row=%d, col=%d\r\n"), ix.first, ix.second);
    str=_T("Testing: \r\n")+str+strTmp;
    AfxMessageBox(str);
*/
#endif
}



void ListCtrlEx::CListCtrlEx::OnLvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLVDISPINFO 
*pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    
// TODO: Add your control notification handler code here
    if (!GetCellEnabled(pDispInfo->item.iItem, pDispInfo->item.iSubItem))
    {
        
*pResult = 1;
        
return;
    }
    
*pResult = 0;
}

void ListCtrlEx::CListCtrlEx::OnLvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
    NMLVDISPINFO 
*pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    
// TODO: Add your control notification handler code here
    
// Update the item text with the new text
    SetItemText(pDispInfo->item.iItem, pDispInfo->item.iSubItem, pDispInfo->item.pszText);
    GetParent()
->SendMessage(WM_VALIDATE, GetDlgCtrlID(), (LPARAM)pDispInfo); 

    
*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// static members
int CALLBACK CListCtrlEx::CompareFunc(LPARAM lParam1, LPARAM lParam2, 
                         LPARAM lParamSort)
{
    _SortParam_t 
*pSortParam_t=reinterpret_cast<_SortParam_t*>(lParamSort);
    
if (NULL==pSortParam_t)
        
return 0;
    CListCtrlEx 
*pList=pSortParam_t->m_pTheList;
    ASSERT(pList
!=NULL && ::IsWindow(pList->m_hWnd));
    CString strCell1 
= pList->GetItemText((int)lParam1, pSortParam_t->m_nSortCol);   
    CString strCell2 
= pList->GetItemText((int)lParam2, pSortParam_t->m_nSortCol);   
    
int nResult=0;
    
try
    {
        
switch(pSortParam_t->m_ColParam.m_eSortType)
        {
        
default:
        
case SortByString:
            nResult
=StrCompare(strCell1,strCell2);
            
break;
        
case SortByDigit:
            nResult
=IntCompare(strCell1,strCell2);
            
break;
        }
    }
    
catch ()
    {
        nResult
=StrCompare(strCell1,strCell2);
    }

    
return (pSortParam_t->m_ColParam.m_bIsAsc?nResult:-nResult);
}

#ifdef _DEBUG
#define ASSERT_VALID_STRING( str ) ASSERT( !IsBadStringPtr( str, 0xfffff ) )
#else    //    _DEBUG
#define ASSERT_VALID_STRING( str ) ( (void)0 )
#endif    //    _DEBUG

bool CListCtrlEx::IsDigitStr(LPCTSTR str)
{
    
int nLen=(int)_tcslen(str);
    
int nPointCnt=0;
    TCHAR ch
=0;
    
for (int i=0; i<nLen; ++i)
    {
        ch
=str[i];
        
// for negative
        if (0==&& _T('-')==ch) {
            
continue;
        }
else if ( 0!=&& _T('-')==ch) {
            
return false;
        }

        
if (_T('.')==ch) {
            
++nPointCnt;
        }

        
if (nPointCnt>1) {
            
return false;
        }

        
if ((ch<48 || ch>57&& _T('.')!=ch) {
            
return false;
        }
    }
    
return true;
}

int CListCtrlEx::IntCompare(LPCTSTR strInt1st,LPCTSTR strInt2nd)
{
    ASSERT_VALID_STRING( strInt1st );
    ASSERT_VALID_STRING( strInt2nd);
    
if (!IsDigitStr(strInt1st) || !IsDigitStr(strInt2nd))
    {
        
//AfxThrowInvalidArgException();
        return StrCompare(strInt1st, strInt2nd);
    }

    
const int num1st=_ttoi(strInt1st);
    
const int num2nd=_ttoi(strInt2nd);
    
return (num1st-num2nd);

}

int CListCtrlEx::StrCompare(LPCTSTR str1st, LPCTSTR str2nd)
{
    ASSERT_VALID_STRING( str1st );
    ASSERT_VALID_STRING( str2nd);
    
return _tcscmp(str1st, str2nd);
}

BOOL CListCtrlEx::DrawGradientRect(CDC 
*pDC, CRect &rect, COLORREF crTopLeft, COLORREF crBottomRight)
{
    ASSERT(pDC);
    
if (0>=rect.Width() || 0>=rect.Height())
    {
        
return FALSE;
    }
    
// draw a rect with 45 degree slope gradient color from top left to bottom right
    CBrush brColor(crBottomRight);
    pDC
->FillRect(&rect, &brColor);
    
int nSteps=max(rect.Width(), rect.Height());
    
int rBR=GetRValue(crBottomRight), gBR=GetGValue(crBottomRight), bBR=GetBValue(crBottomRight);
    
int rTL=GetRValue(crTopLeft), gTL=GetGValue(crTopLeft), bTL=GetBValue(crTopLeft);
    
int rDiff=(rBR-rTL)/nSteps;
    
int gDiff=(gBR-gTL)/nSteps;
    
int bDiff=(bBR-bTL)/nSteps;

    CRgn rgRect, rgRound, rg2Fill;
    rgRect.CreateRectRgnIndirect(
&rect);
    rg2Fill.CreateRectRgn(
0,0,0,0);
    
for (int i=nSteps; i>=0--i)
    {
        CRect rcRound(rect.left
-i, rect.top-i, rect.left+i, rect.top+i);
        rgRound.CreateEllipticRgnIndirect(
&rcRound);
        
int nCombineResult =rg2Fill.CombineRgn(&rgRect, &rgRound, RGN_AND);
        
if( nCombineResult != ERROR && nCombineResult != NULLREGION ){    
            pDC
->FillRgn(&rg2Fill, &brColor);
        }
        rgRound.DeleteObject();
        
// calculate new color
        brColor.DeleteObject();
        brColor.CreateSolidBrush(RGB(rTL
+i*rDiff, gTL+i*gDiff, bTL+i*bDiff));
    }

    
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void ListCtrlEx::CListCtrlEx::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMLISTVIEW pNMLV 
= reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    
if(IsSupportSort())
    {
        vector
<DWORD_PTR> vecItemDatas(GetItemCount());
        
//map mapOldRow2NewRow(GetItemCount());
        PreSortItems(vecItemDatas);
        
//SortType eSortBy=GetColumnSortBy(pNMLV->iSubItem);
        BOOL bIsAsc=!IsColumnSortAsc(pNMLV->iSubItem);
        SetColumnSortDirection(pNMLV
->iSubItem, bIsAsc);
        _SortParam_t aSortParam;
        aSortParam.m_ColParam
=m_mapCol2Sort[pNMLV->iSubItem];
        aSortParam.m_nSortCol
=pNMLV->iSubItem;
        aSortParam.m_pTheList
=this;
        SortItems(CompareFunc, (DWORD_PTR)
&aSortParam);
        
//m_ctlSortHead.SetSortArrow(pNMLV->iSubItem, !bIsAsc);
        PostSortItems(vecItemDatas);
    }
    
    
*pResult = 0;
}

// before sort items, backup old item data here, then set the item data with old row NO.
// vec2StoreData must be initialized as size of "GetItemCount()"
void CListCtrlEx::PreSortItems( vector<DWORD_PTR> &vec2StoreData )
{
    
int nRowCnt=GetItemCount();
    
for (int i=0; i<nRowCnt; ++i)
    {
        vec2StoreData[i]
=GetItemData(i);
        SetItemData(i, (DWORD_PTR)i);
    }
}

// after sort items, re-mapping the cell data here and restore the old item data here
// vec2StoreData must be initialized as size of "GetItemCount()" and assigned with old item data
void CListCtrlEx::PostSortItems( vector<DWORD_PTR> &vec2StoreData )
{
    
// adjust the cell data map
    CellDataMap newMap;
    
int nRowCnt=GetItemCount();
    
int nColCnt=GetColumnCount();
    
for (int i=0; i<nRowCnt; ++i)
    {
        
int nOldRow=(int)GetItemData(i);
        
for (int j=0; j<nColCnt; ++j)
        {
            newMap[make_pair(i,j)]
=m_mapCell2Data[make_pair(nOldRow, j)];    
        }
        
// restore item data
        SetItemData(i, (DWORD_PTR)vec2StoreData[nOldRow]);
    }
    m_mapCell2Data.swap(newMap);    
}

void ListCtrlEx::CListCtrlEx::PreSubclassWindow()
{
    
// TODO: Add your specialized code here and/or call the base class
    ASSERT( GetStyle() & LVS_REPORT );    
    CListCtrl::PreSubclassWindow();
    VERIFY(m_ctlSortHead.SubclassWindow(
this->GetHeaderCtrl()->GetSafeHwnd()) );
}
BOOL    CListCtrlEx::IsColumnSortAsc(
int nColIndex)
{
    
// insert new if not found
    ColSortMap::iterator iter=m_mapCol2Sort.find(nColIndex);
    
if (m_mapCol2Sort.end()==iter)
    {
        m_mapCol2Sort[nColIndex]
=_ColumnSort_t();
    }
    
return m_mapCol2Sort[nColIndex].m_bIsAsc;
}

void CListCtrlEx::SetColumnSortDirection(int nColIndex, BOOL bIsAsc )
{
    
// init sort map
    if (m_mapCol2Sort.empty())
    {
        
for (int i=0; i<GetColumnCount(); ++i)
        {
            m_mapCol2Sort[i]
=_ColumnSort_t();
        }
    }
    
// assign
    m_mapCol2Sort[nColIndex].m_bIsAsc=bIsAsc;
}

void ListCtrlEx::CListCtrlEx::OnDestroy()
{
    CListCtrl::OnDestroy();

    
if (::IsWindow(CInPlaceCombo::GetInstance()->m_hWnd))
    {
        CInPlaceCombo::GetInstance()
->PostMessage(WM_CLOSE);
    }

    
if (::IsWindow(CInPlaceEdit::GetInstance()->m_hWnd))
    {
        CInPlaceEdit::GetInstance()
->PostMessage(WM_CLOSE);
    }

}

// demo

Super ListCtrl for MFC - 可以内嵌CheckBox, Radio, Combo, Edit, Progress, 支持排序_第1张图片 

ListCtrlExDemo

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/dlbrant/archive/2009/02/17/1392597.html

你可能感兴趣的:(Super ListCtrl for MFC - 可以内嵌CheckBox, Radio, Combo, Edit, Progress, 支持排序)