📄 treelistview.h
字号:
#if !defined(AFX_TREELISTVIEW_H__20010510_B3AE_CB5E_0BEB_0080AD509054__INCLUDED_)
#define AFX_TREELISTVIEW_H__20010510_B3AE_CB5E_0BEB_0080AD509054__INCLUDED_
#pragma once
/////////////////////////////////////////////////////////////////////////////
// CTreeListView - A TreeView with additional columns
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2001-2002 Bjarke Viksoe.
//
// Partly implemented from a MFC CTreeListView control by Gerolf K黨nel
// available at www.codeproject.com.
// Horizontal scrolling supplied by Oleg Reabciuc (olegr@compudava.com).
// Nail Kaipov fixed the horizontal scrollbar code (roof@crypt.nsk.ru).
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//
#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif
#ifndef __ATLAPP_H__
#error TreeListView.h requires atlapp.h to be included first
#endif
#ifndef __ATLCTRLS_H__
#error TreeListView.h requires atlctrls.h to be included first
#endif
#if (_WIN32_IE < 0x0400)
#error TreeListView.h requires _WIN32_IE >= 0x0400
#endif
// The TreeListView item structure
typedef struct tagTLVITEM
{
UINT mask;
int iSubItem;
UINT state;
UINT stateMask;
UINT format;
LPTSTR pszText;
UINT cchTextMax;
int iImage;
COLORREF clrText;
COLORREF clrBack;
LPARAM lParam;
} TLVITEM, *LPTLVITEM;
// TreeListView mask flags
#define TLVIF_TEXT 0x0001
#define TLVIF_IMAGE 0x0002
#define TLVIF_PARAM 0x0004
#define TLVIF_STATE 0x0008
#define TLVIF_FORMAT 0x0010
#define TLVIF_TEXTCOLOR 0x0020
// TreeListView format flags
#define TLVIFMT_LEFT 0x00000000
#define TLVIFMT_CENTER 0x00000001
#define TLVIFMT_RIGHT 0x00000002
// TreeListView state flags
#define TLVIS_BOLD 0x0001
#define TLVIS_ITALIC 0x0002
#define TLVIS_UNDERLINE 0x0004
#define TLVIS_STRIKEOUT 0x0008
template< class T, class TBase = CWindow, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CTreeListViewImpl :
public CWindowImpl< T, TBase, TWinTraits >,
public CCustomDraw< T >
{
public:
typedef CTreeListViewImpl< T , TBase, TWinTraits > thisClass;
DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
CContainedWindowT< CTreeViewCtrl > m_ctrlTree;
CContainedWindowT< CHeaderCtrl > m_ctrlHeader;
//
typedef CSimpleArray< TLVITEM* > tMapItem;
CSimpleMap< HTREEITEM, tMapItem* > m_mapItems;
CSimpleArray< RECT > m_rcColumns;
//
CFont m_fontHeader;
LONG m_cxHeader;
LONG m_nOffset;
//
UINT m_iItemState;
RECT m_rcItem;
CTreeListViewImpl() :
m_cxHeader(0),
m_nOffset(0)
{
}
// Operations
BOOL SubclassWindow(HWND hWnd)
{
ATLASSERT(m_hWnd==NULL);
ATLASSERT(::IsWindow(hWnd));
BOOL bRet = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
if( bRet ) _Init();
return bRet;
}
BOOL SetSubItem(HTREEITEM hItem, const LPTLVITEM pItem)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hItem);
ATLASSERT(!::IsBadReadPtr(pItem, sizeof(TLVITEM)));
if( pItem->iSubItem < 0 || pItem->iSubItem >= m_ctrlHeader.GetItemCount() ) return FALSE;
LPTLVITEM pItemT = _GetSubItem(hItem, pItem->iSubItem);
ATLASSERT(pItemT);
if( pItemT == NULL ) return FALSE;
// Copy attributes from caller's TLVITEM to internally
// stored TLVITEM structure.
if( pItem->mask & TLVIF_TEXT ) {
if( pItemT->mask & TLVIF_TEXT ) ATLTRY(delete [] pItemT->pszText);
ATLTRY(pItemT->pszText = new TCHAR[ ::lstrlen(pItem->pszText)+1 ]);
::lstrcpy(pItemT->pszText, pItem->pszText);
pItemT->mask |= TLVIF_TEXT;
}
if( pItem->mask & TLVIF_IMAGE ) {
pItemT->iImage = pItem->iImage;
pItemT->mask |= TLVIF_IMAGE;
}
if( pItem->mask & TLVIF_PARAM ) {
pItemT->lParam = pItem->lParam;
pItemT->mask |= TLVIF_PARAM;
}
if( pItem->mask & TLVIF_FORMAT ) {
pItemT->format = pItem->format;
pItemT->mask |= TLVIF_FORMAT;
}
if( pItem->mask & TLVIF_STATE ) {
pItemT->state &= ~pItem->stateMask;
pItemT->state |= (pItem->state & pItem->stateMask);
pItemT->mask |= TLVIF_STATE;
}
if( pItem->mask & TLVIF_TEXTCOLOR ) {
pItemT->clrText = pItem->clrText;
pItemT->mask |= TLVIF_TEXTCOLOR;
}
return TRUE;
}
BOOL GetSubItem(HTREEITEM hItem, LPTLVITEM pItem)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hItem);
ATLASSERT(!::IsBadWritePtr(pItem, sizeof(TLVITEM)));
LPTLVITEM pItemT = _GetSubItem(hItem, pItem->iSubItem);
if( pItemT == NULL ) return FALSE;
// Copy item data
UINT mask = pItem->mask;
if( mask & TLVIF_TEXT ) {
ATLASSERT(!::IsBadWritePtr(pItem->pszText, pItem->cchTextMax));
::lstrcpyn( pItem->pszText, pItemT->pszText == NULL ? _T("") : pItemT->pszText, pItem->cchTextMax );
}
if( mask & TLVIF_IMAGE ) pItem->iImage = pItemT->iImage;
if( mask & TLVIF_FORMAT ) pItem->format = pItemT->format;
if( mask & TLVIF_STATE ) {
pItem->state &= ~pItem->stateMask;
pItem->state |= pItemT->state & pItem->stateMask;
}
if( mask & TLVIF_TEXTCOLOR ) pItem->clrText = pItemT->clrText;
if( mask & TLVIF_PARAM ) pItem->lParam = pItemT->lParam;
return TRUE;
}
BOOL SetSubItemText(HTREEITEM hItem, int nSubItem, LPCTSTR pstrString)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hItem);
ATLASSERT(!::IsBadStringPtr(pstrString, (UINT)-1));
TLVITEM itm = { 0 };
itm.iSubItem = nSubItem;
itm.mask = TLVIF_TEXT;
itm.pszText = const_cast<LPTSTR>(pstrString);
return SetSubItem(hItem, &itm);
}
BOOL GetSubItemText(HTREEITEM hItem, int nSubItem, LPTSTR pstrString, UINT cchMax)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(hItem);
ATLASSERT(!::IsBadWritePtr(pstrString,cchMax));
LPTLVITEM pItem = _GetSubItem(hItem, nSubItem);
if( pItem == NULL ) return FALSE;
::lstrcpyn(pstrString, pItem->pszText, cchMax);
return TRUE;
}
CTreeViewCtrl GetTreeControl() const
{
ATLASSERT(::IsWindow(m_ctrlTree));
return m_ctrlTree;
}
CHeaderCtrl GetHeaderControl() const
{
ATLASSERT(::IsWindow(m_ctrlHeader));
return m_ctrlHeader;
}
// Implementation
void _Init()
{
ATLASSERT(::IsWindow(m_hWnd));
// This is a Platform SDK define which we need
#ifndef TVS_NOHSCROLL
const UINT TVS_NOHSCROLL = 0x8000;
#endif
// Create the tree control
// Thanks to Nicola Tufarelli for suggesting using the GetDlgCtrlID() to
// preserve the original control ID...
DWORD dwStyle = GetStyle();
UINT nID = GetDlgCtrlID();
m_ctrlTree.Create(this, 1, m_hWnd, &rcDefault, NULL, dwStyle, 0, nID);
ATLASSERT(m_ctrlTree.IsWindow());
m_ctrlTree.ModifyStyle(0, TVS_NOHSCROLL | TVS_FULLROWSELECT); // we need these
// Create the header control
dwStyle = WS_CHILD | WS_VISIBLE | HDS_BUTTONS | HDS_HORZ | HDS_DRAGDROP;
m_ctrlHeader.Create(this, 2, m_hWnd, &rcDefault, NULL, dwStyle);
ATLASSERT(m_ctrlHeader.IsWindow());
SendMessage(WM_SETTINGCHANGE);
T* pT = static_cast<T*>(this);
pT->UpdateLayout();
// FIX: When used in a view...
m_ctrlTree.ShowWindow(SW_SHOW);
m_ctrlHeader.ShowWindow(SW_SHOW);
}
LPTLVITEM _GetSubItem(HTREEITEM hItem, int iSubItem)
{
ATLASSERT(hItem);
if( iSubItem<0 || iSubItem>m_ctrlHeader.GetItemCount() ) return NULL;
tMapItem* pVal = m_mapItems.Lookup(hItem);
ATLASSERT(pVal);
ATLASSERT(iSubItem < pVal->GetSize());
if( pVal == NULL ) return NULL;
if( iSubItem >= pVal->GetSize() ) return NULL;
return (*pVal)[iSubItem];
}
void _DeleteSubItem(const LPTLVITEM pItem)
{
ATLASSERT(!::IsBadReadPtr(pItem, sizeof(TLVITEM)));
if( pItem->mask & TLVIF_TEXT ) ATLTRY(delete [] pItem->pszText);
ATLTRY(delete pItem);
}
void _CalcColumnSizes()
{
// Keep track of header sizes
m_rcColumns.RemoveAll();
m_cxHeader = 0;
int nHeaders = m_ctrlHeader.GetItemCount();
for( int i = 0; i < nHeaders; i++ ) {
RECT rc;
m_ctrlHeader.GetItemRect(i, &rc);
m_rcColumns.Add(rc);
m_cxHeader += rc.right - rc.left;
}
// FIX: Nail Kaipov fixed the horizontal scrollbar code
// If the width of all headers is bigger than the width of the client-area
// of the TreeView, then the Scrollbar is to be enabled
RECT rcClient;
GetClientRect(&rcClient);
if( GetStyle() & WS_VSCROLL ) rcClient.right += ::GetSystemMetrics(SM_CXHTHUMB);
LONG cxClient = (rcClient.right - rcClient.left);
if( m_cxHeader - cxClient >= 0 ) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nMax = m_cxHeader;
si.nMin = 0;
si.nPage = cxClient;
si.nPos = GetScrollPos(SB_HORZ); // Preserve scroller position
SetScrollInfo(SB_HORZ, &si, TRUE); // Setup scrollbar params
ModifyStyle(0, WS_HSCROLL, SWP_FRAMECHANGED);
m_nOffset = -si.nPos; // Save scrollbar position for correct drawing
if( si.nPos ) { // Scroll position is non-zero. code is stolen from OnHScroll()
RECT rcHeader, rcTree;
m_ctrlHeader.GetClientRect(&rcHeader);
m_ctrlTree.GetClientRect(&rcTree);
m_ctrlHeader.SetWindowPos(NULL,
-si.nPos,
0,
abs(si.nPos) + ::GetSystemMetrics(SM_CXVSCROLL) + 1 + rcTree.right - rcTree.left,
rcHeader.bottom - rcHeader.top,
SWP_SHOWWINDOW);
}
}
else {
SetScrollPos(SB_HORZ, 0, TRUE);
ModifyStyle(WS_HSCROLL, 0, SWP_FRAMECHANGED);
m_nOffset = 0;
Invalidate();
}
}
// Message map and handlers
BEGIN_MSG_MAP( thisClass )
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkGnd)
CHAIN_MSG_MAP( CCustomDraw< T > )
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
NOTIFY_CODE_HANDLER(TVN_DELETEITEMA, OnTreeItemDelete)
NOTIFY_CODE_HANDLER(TVN_DELETEITEMW, OnTreeItemDelete)
NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDEDA, OnTreeItemExpanded)
NOTIFY_CODE_HANDLER(TVN_ITEMEXPANDEDW, OnTreeItemExpanded)
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGEDA, OnHeaderItemChanged)
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGEDW, OnHeaderItemChanged)
NOTIFY_CODE_HANDLER(NM_RELEASEDCAPTURE, OnHeaderItemChanged)
NOTIFY_CODE_HANDLER(HDN_BEGINDRAG, OnHeaderBeginDrag)
NOTIFY_CODE_HANDLER(HDN_ENDDRAG, OnHeaderEndDrag)
FORWARD_NOTIFICATIONS()
ALT_MSG_MAP(1) // Tree
MESSAGE_HANDLER(WM_KILLFOCUS, OnTreeKillFocus)
MESSAGE_HANDLER(WM_NCDESTROY, OnTreeNcDestroy)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTreeFixButtonHit)
MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnTreeFixButtonHit)
MESSAGE_HANDLER(TVM_INSERTITEM, OnTreeItemInsert)
MESSAGE_HANDLER(TVM_SETBKCOLOR, OnTreeSetColor)
MESSAGE_HANDLER(TVM_SETTEXTCOLOR, OnTreeSetColor)
ALT_MSG_MAP(2) // Header
MESSAGE_HANDLER(HDM_INSERTITEM, OnHeaderItemInsert)
MESSAGE_HANDLER(HDM_DELETEITEM, OnHeaderItemDelete)
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// Do not allow the TreeView control to initialize here!
// We are creating new child controls ourselves in the _Init() method.
_Init();
return 0;
}
LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
if( !m_fontHeader.IsNull() ) m_fontHeader.DeleteObject();
NONCLIENTMETRICS ncm = { 0 };
ncm.cbSize = sizeof(ncm);
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
m_fontHeader.CreateFontIndirect(&ncm.lfMenuFont);
m_ctrlHeader.SetFont(m_fontHeader);
Invalidate();
return 0;
}
LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
m_ctrlTree.SetFocus();
return 0;
}
LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
T* pT = static_cast<T*>(this);
pT->UpdateLayout();
return 0;
}
LRESULT OnEraseBkGnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return 1; // Children fill entire client area
}
LPARAM OnHScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// Thanks to Oleg Reabciuc for providing the horizontal scrolling
// support for this control
int nSBCode = (int) LOWORD(wParam);
int nPos = (int) HIWORD(wParam);
RECT rcClient;
m_ctrlTree.GetClientRect(&rcClient);
int cxClient = abs(rcClient.right - rcClient.left); // One Page
const int nWidthLine = 6; // Microsoft's line-step in a CListCtrl, got from MFC - sourcecode
int nCurPos = GetScrollPos(SB_HORZ); // Current scrollingposition
int nPrevPos = nCurPos; // Save current scrolling position for calculating
int nScrollMin; // Minimum scrolling value
int nScrollMax; // Maximum scrolling value
GetScrollRange(SB_HORZ, &nScrollMin, &nScrollMax);
// Check which kind of scoll is wanted
switch( nSBCode ) {
case SB_LEFT : // Scoll to left most position
nCurPos = 0;
break;
case SB_RIGHT : // Scroll to right most position
nCurPos = nScrollMax;
break;
case SB_LINELEFT : // Scroll left with the button
nCurPos = max(nCurPos - nWidthLine, 0);
break;
case SB_LINERIGHT : // Scroll right with the button
nCurPos = min(nCurPos + nWidthLine, nScrollMax);
break;
case SB_PAGELEFT : // Scroll left with a click to the background of the scrollbar
nCurPos = max(nCurPos - cxClient, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -