www.pudn.com > ListCtrl_Column_Picker.zip > CListCtrl_Column_Picker.cpp
#include "stdafx.h"
#include "CListCtrl_Column_Picker.h"
BEGIN_MESSAGE_MAP(CListCtrl_Column_Picker, CListCtrl)
ON_MESSAGE(LVM_DELETECOLUMN, OnDeleteColumn)
ON_MESSAGE(LVM_INSERTCOLUMN, OnInsertColumn)
ON_MESSAGE(LVM_SETCOLUMNWIDTH, OnSetColumnWidth)
ON_NOTIFY_EX(HDN_BEGINTRACKA, 0, OnHeaderBeginResize)
ON_NOTIFY_EX(HDN_BEGINTRACKW, 0, OnHeaderBeginResize)
ON_NOTIFY_EX(HDN_ENDDRAG, 0, OnHeaderEndDrag)
ON_WM_CONTEXTMENU() // OnContextMenu
ON_WM_KEYDOWN() // OnKeyDown
END_MESSAGE_MAP()
int CListCtrl_Column_Picker::GetFirstVisibleColumn()
{
int nColCount = GetHeaderCtrl()->GetItemCount();
for(int i = 0; i < nColCount; ++i)
{
int nCol = GetHeaderCtrl()->OrderToIndex(i);
if (IsColumnVisible(nCol))
{
return nCol;
}
}
return -1;
}
BOOL CListCtrl_Column_Picker::ShowColumn(int nCol, bool bShow)
{
SetRedraw(FALSE);
ColumnState& columnState = GetColumnState(nCol);
int nColCount = GetHeaderCtrl()->GetItemCount();
int* pOrderArray = new int[nColCount];
VERIFY( GetColumnOrderArray(pOrderArray, nColCount) );
if (bShow)
{
// Restore the position of the column
int nCurIndex = -1;
for(int i = 0; i < nColCount ; ++i)
{
if (pOrderArray[i]==nCol)
nCurIndex = i;
else
if (nCurIndex!=-1)
{
if (i <= columnState.m_OrgPosition)
{
pOrderArray[nCurIndex] = pOrderArray[i];
pOrderArray[i] = nCol;
nCurIndex = i;
}
}
}
}
else
{
// Move the column to the front of the display order list
int nCurIndex(-1);
for(int i = nColCount-1; i >=0 ; --i)
{
if (pOrderArray[i]==nCol)
{
// Backup the current position of the column
columnState.m_OrgPosition = i;
nCurIndex = i;
}
else
if (nCurIndex!=-1)
{
pOrderArray[nCurIndex] = pOrderArray[i];
pOrderArray[i] = nCol;
nCurIndex = i;
}
}
}
VERIFY( SetColumnOrderArray(nColCount, pOrderArray) );
delete [] pOrderArray;
if (bShow)
{
// Restore the column width
columnState.m_Visible = true;
VERIFY( SetColumnWidth(nCol, columnState.m_OrgWidth) );
}
else
{
// Backup the column width
int orgWidth = GetColumnWidth(nCol);
VERIFY( SetColumnWidth(nCol, 0) );
columnState.m_Visible = false;
columnState.m_OrgWidth = orgWidth;
}
SetRedraw(TRUE);
Invalidate(FALSE);
return TRUE;
}
BOOL CListCtrl_Column_Picker::SetColumnWidthAuto(int nCol, bool includeHeader)
{
if (nCol == -1)
{
for(int i = 0; i < GetHeaderCtrl()->GetItemCount() ; ++i)
{
SetColumnWidthAuto(i, includeHeader);
}
return TRUE;
}
else
{
if (includeHeader)
return SetColumnWidth(nCol, LVSCW_AUTOSIZE_USEHEADER);
else
return SetColumnWidth(nCol, LVSCW_AUTOSIZE);
}
}
CListCtrl_Column_Picker::ColumnState& CListCtrl_Column_Picker::GetColumnState(int nCol)
{
VERIFY( nCol >=0 && nCol < m_ColumnStates.GetSize() );
return m_ColumnStates[nCol];
}
bool CListCtrl_Column_Picker::IsColumnVisible(int nCol)
{
return GetColumnState(nCol).m_Visible;
}
int CListCtrl_Column_Picker::GetColumnStateCount()
{
return m_ColumnStates.GetSize();
}
void CListCtrl_Column_Picker::InsertColumnState(int nCol, bool bVisible, int nOrgWidth)
{
VERIFY( nCol >=0 && nCol <= m_ColumnStates.GetSize() );
ColumnState columnState;
columnState.m_OrgWidth = nOrgWidth;
columnState.m_Visible = bVisible;
if (nCol == m_ColumnStates.GetSize())
{
// Append column picker to the end of the array
m_ColumnStates.Add(columnState);
}
else
{
// Insert column in the middle of the array
CSimpleArray newArray;
for(int i=0 ; i < m_ColumnStates.GetSize(); ++i)
{
if (i == nCol)
newArray.Add(columnState);
newArray.Add(m_ColumnStates[i]);
}
m_ColumnStates = newArray;
}
}
void CListCtrl_Column_Picker::DeleteColumnState(int nCol)
{
VERIFY( nCol >=0 && nCol < m_ColumnStates.GetSize() );
m_ColumnStates.RemoveAt(nCol);
}
void CListCtrl_Column_Picker::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (point.x==-1 && point.y==-1)
{
// OBS! point is initialized to (-1,-1) if using SHIFT+F10 or VK_APPS
}
else
{
CPoint pt = point;
ScreenToClient(&pt);
CRect headerRect;
GetHeaderCtrl()->GetClientRect(&headerRect);
if (headerRect.PtInRect(pt))
{
// Show context-menu with the option to show hide columns
CMenu menu;
if (menu.CreatePopupMenu())
{
for( int i = GetColumnStateCount()-1 ; i >= 0; --i)
{
UINT uFlags = MF_BYPOSITION | MF_STRING;
// Put check-box on context-menu
if (IsColumnVisible(i))
uFlags |= MF_CHECKED;
else
uFlags |= MF_UNCHECKED;
// Retrieve column-title
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT;
TCHAR sColText[256];
lvc.pszText = sColText;
lvc.cchTextMax = sizeof(sColText)-1;
VERIFY( GetColumn(i, &lvc) );
menu.InsertMenu(0, uFlags, i, lvc.pszText);
}
menu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this, 0);
}
}
}
}
// Handle context-menu event for showing / hiding columns
BOOL CListCtrl_Column_Picker::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (HIWORD(wParam) == 0)
{
int nCol = LOWORD(wParam);
ShowColumn(nCol, !IsColumnVisible(nCol));
}
return TRUE;
}
void CListCtrl_Column_Picker::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch(nChar)
{
case VK_ADD: // CTRL + NumPlus (Auto size all columns)
{
if (GetKeyState(VK_CONTROL) < 0)
{
// Special handling to avoid showing "hidden" columns
SetColumnWidthAuto(-1);
return;
}
} break;
}
CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}
BOOL CListCtrl_Column_Picker::OnHeaderBeginResize(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
// Check that column is allowed to be resized
NMHEADER* pNMH = (NMHEADER*)pNMHDR;
int nCol = (int)pNMH->iItem;
if (!IsColumnVisible(nCol))
{
*pResult = TRUE; // Block resize
return TRUE; // Block event
}
return FALSE;
}
LRESULT CListCtrl_Column_Picker::OnSetColumnWidth(WPARAM wParam, LPARAM lParam)
{
// Check that column is allowed to be resized
int nCol = (int)wParam;
if (!IsColumnVisible(nCol))
{
return FALSE;
}
// Let the CListCtrl handle the event
return DefWindowProc(LVM_SETCOLUMNWIDTH, wParam, lParam);
}
BOOL CListCtrl_Column_Picker::OnHeaderEndDrag(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
NMHEADER* pNMH = (NMHEADER*)pNMHDR;
if (pNMH->pitem->mask & HDI_ORDER)
{
// Correct iOrder so it is just after the last hidden column
int nColCount = GetHeaderCtrl()->GetItemCount();
int* pOrderArray = new int[nColCount];
VERIFY( GetColumnOrderArray(pOrderArray, nColCount) );
for(int i = 0; i < nColCount ; ++i)
{
if (IsColumnVisible(pOrderArray[i]))
{
pNMH->pitem->iOrder = max(pNMH->pitem->iOrder,i);
break;
}
}
delete [] pOrderArray;
}
return FALSE;
}
LRESULT CListCtrl_Column_Picker::OnDeleteColumn(WPARAM wParam, LPARAM lParam)
{
// Let the CListCtrl handle the event
LRESULT lRet = DefWindowProc(LVM_DELETECOLUMN, wParam, lParam);
if (lRet == FALSE)
return FALSE;
// Book keeping of columns
DeleteColumnState((int)wParam);
return lRet;
}
LRESULT CListCtrl_Column_Picker::OnInsertColumn(WPARAM wParam, LPARAM lParam)
{
// Let the CListCtrl handle the event
LRESULT lRet = DefWindowProc(LVM_INSERTCOLUMN, wParam, lParam);
if (lRet == -1)
return -1;
int nCol = (int)lRet;
// Book keeping of columns
if (GetColumnStateCount() < GetHeaderCtrl()->GetItemCount())
InsertColumnState((int)nCol, true); // Insert as visible
return lRet;
}
namespace {
LRESULT EnableWindowTheme(HWND hwnd, LPCWSTR classList, LPCWSTR subApp, LPCWSTR idlist)
{
HMODULE hinstDll;
HRESULT (__stdcall *pSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
HANDLE (__stdcall *pOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
HRESULT (__stdcall *pCloseThemeData)(HANDLE hTheme);
hinstDll = ::LoadLibrary("UxTheme.dll");
if (hinstDll)
{
(FARPROC&)pOpenThemeData = ::GetProcAddress(hinstDll, TEXT("OpenThemeData"));
(FARPROC&)pCloseThemeData = ::GetProcAddress(hinstDll, TEXT("CloseThemeData"));
(FARPROC&)pSetWindowTheme = ::GetProcAddress(hinstDll, TEXT("SetWindowTheme"));
::FreeLibrary(hinstDll);
if (pSetWindowTheme && pOpenThemeData && pCloseThemeData)
{
HANDLE theme = pOpenThemeData(hwnd,classList);
if (theme!=NULL)
{
VERIFY(pCloseThemeData(theme)==S_OK);
return pSetWindowTheme(hwnd, subApp, idlist);
}
}
}
return S_FALSE;
}
}
void CListCtrl_Column_Picker::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Focus retangle is not painted properly without double-buffering
#if (_WIN32_WINNT >= 0x501)
SetExtendedStyle(LVS_EX_DOUBLEBUFFER | GetExtendedStyle());
#endif
SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT);
SetExtendedStyle(GetExtendedStyle() | LVS_EX_HEADERDRAGDROP);
SetExtendedStyle(GetExtendedStyle() | LVS_EX_GRIDLINES);
// Enable Vista-look if possible
EnableWindowTheme(GetSafeHwnd(), L"ListView", L"Explorer", NULL);
}