📄 treelistview.h
字号:
break;
case SB_PAGERIGHT : // Scroll left with a click to the background of the scrollbar
nCurPos = min(nCurPos + cxClient, nScrollMax);
break;
case SB_THUMBPOSITION : // Scroll by moving the scrollbutton with the mouse
case SB_THUMBTRACK : // Drop the scrollbarbutton
// Check for illegal positions and correct them (out of the scrollbar?)
if( nPos == 0 ) {
nCurPos = 0;
}
else {
nCurPos = min( StretchWidth(nPos, nWidthLine), nScrollMax);
}
}
// Move the scrollbarbutton to the position (graphically)
SetScrollPos(SB_HORZ, nCurPos, TRUE);
m_nOffset = -nCurPos;
// Smoothly Scroll the Tree control
RECT rcTree;
m_ctrlTree.GetClientRect(&rcTree);
m_ctrlTree.ScrollWindowEx(nPrevPos - nCurPos, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE);
RECT rcHeader;
GetClientRect(&rcClient);
m_ctrlHeader.GetClientRect(&rcHeader);
if( rcTree.right - rcTree.left != 0 ) {
int iVSWidth = ::GetSystemMetrics(SM_CXVSCROLL) + 1;
m_ctrlHeader.SetWindowPos(NULL, m_nOffset, 0, abs(m_nOffset) + iVSWidth + rcTree.right - rcTree.left, rcHeader.bottom - rcHeader.top, SWP_SHOWWINDOW);
}
// Redraw the treecontrol so you can see the scrolling
m_ctrlTree.Invalidate();
return 0;
}
// Tree control
LRESULT OnTreeNcDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// Clean up memory on destruction
m_ctrlTree.DeleteAllItems();
bHandled = FALSE;
return 0;
}
LRESULT OnTreeKillFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// HACK: Fix the TreeView focus paint problem
m_ctrlTree.Invalidate();
bHandled = FALSE;
return 0;
}
LRESULT OnTreeFixButtonHit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
// If a click is to the right of the label then we're still clicking on the item...
// Handling TVM_HITTEST would be a lot nicer, but the TreeView control doesn't
// actually use it internally :-(
RECT rcClient;
GetClientRect(&rcClient);
int x = GET_X_LPARAM(lParam) - m_nOffset;
int y = GET_Y_LPARAM(lParam);
if( x > rcClient.right - rcClient.left ) x = rcClient.right - 40;
TVHITTESTINFO hti;
hti.pt.x = x;
hti.pt.y = y;
m_ctrlTree.HitTest(&hti);
if( hti.flags == TVHT_ONITEMRIGHT ) {
RECT rc;
m_ctrlTree.GetItemRect(hti.hItem, &rc, TRUE);
x = rc.left;
}
lParam = MAKELONG(x, y);
return m_ctrlTree.DefWindowProc(uMsg, wParam, lParam);
}
LRESULT OnTreeItemInsert(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
HTREEITEM hItem = (HTREEITEM) m_ctrlTree.DefWindowProc(uMsg, wParam, lParam);
if( hItem == NULL ) return (LRESULT) hItem;
// Create a new item
tMapItem* pVal;
ATLTRY(pVal = new tMapItem);
ATLASSERT(pVal);
LPTLVITEM pItem;
int nHeaders = m_ctrlHeader.GetItemCount();
for( int i = 0; i < nHeaders; i++ ) {
ATLTRY(pItem = new TLVITEM);
ATLASSERT(pItem);
::ZeroMemory(pItem, sizeof(TLVITEM));
pVal->Add( pItem );
}
// Add the new item
m_mapItems.Add(hItem, pVal);
// Then add the item structure...
if( nHeaders > 0 ) {
// Convert TVITEM into a TLVITEM
LPTVINSERTSTRUCT pTVIS = reinterpret_cast<LPTVINSERTSTRUCT>(lParam);
ATLASSERT(pTVIS);
TLVITEM itm = { 0 };
itm.iSubItem = 0;
if( pTVIS->item.mask & TVIF_TEXT ) {
ATLASSERT(pTVIS->item.pszText != LPSTR_TEXTCALLBACK); // Not supported
itm.mask |= TLVIF_TEXT;
itm.pszText = pTVIS->item.pszText;
}
SetSubItem(hItem, &itm);
}
return (LRESULT) hItem;
}
LRESULT OnTreeItemDelete(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
LPNMTREEVIEW pnmt = (LPNMTREEVIEW) pnmh;
HTREEITEM hItem = pnmt->itemOld.hItem;
tMapItem* pVal = m_mapItems.Lookup(hItem);
ATLASSERT(pVal);
if( pVal == NULL ) return 0;
int cnt = pVal->GetSize();
for( int i = 0; i < cnt; i++ ) {
LPTLVITEM pItem = (*pVal)[i];
_DeleteSubItem(pItem);
}
ATLTRY(delete pVal);
m_mapItems.Remove(hItem);
bHandled = FALSE;
return 0;
}
LRESULT OnTreeItemExpanded(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
{
// Auto-scale the first column to the largest tree-item
if( m_ctrlHeader.GetItemCount() > 0 ) {
int cx = 0;
HTREEITEM hItem = m_ctrlTree.GetNextItem(NULL, TVGN_FIRSTVISIBLE);
while( hItem != NULL ) {
RECT rc;
m_ctrlTree.GetItemRect(hItem, &rc, TRUE);
if( rc.left > cx ) cx = rc.left;
hItem = m_ctrlTree.GetNextItem(hItem, TVGN_NEXTVISIBLE);
}
HDITEM hdr = { 0 };
hdr.mask = HDI_WIDTH;
m_ctrlHeader.GetItem(0, &hdr);
cx = max(hdr.cxy, cx);
if( hdr.cxy != cx ) {
hdr.cxy = cx;
m_ctrlHeader.SetItem(0, &hdr);
}
}
bHandled = FALSE;
return 0;
}
LRESULT OnTreeSetColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
LRESULT lRes = m_ctrlTree.DefWindowProc(uMsg, wParam, lParam);
T* pT = static_cast<T*>(this);
BOOL bDummy;
pT->OnSettingChange(0,0,0,bDummy);
return lRes;
}
// Header control
LRESULT OnHeaderItemInsert(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
ATLASSERT((int)wParam==m_ctrlHeader.GetItemCount()); // Only supports appending right now!
// When adding a header we need to place a TLVITEM in
// each tree item as well...
LRESULT lRes = m_ctrlHeader.DefWindowProc(uMsg, wParam, lParam);
if( lRes != -1 ) {
LPTLVITEM pItem;
for( int i = 0; i < m_mapItems.GetSize(); i++ ) {
tMapItem* pVal = m_mapItems.GetValueAt(i);
ATLTRY(pItem = new TLVITEM);
ATLASSERT(pItem);
::ZeroMemory(pItem, sizeof(TLVITEM));
pVal->Add( pItem );
}
_CalcColumnSizes();
}
return lRes;
}
LRESULT OnHeaderItemDelete(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
// Deleting a column header also means cleaning up all
// the TLVITEM structures in each of the column's sub-items.
for( int i = 0; i < m_mapItems.GetSize(); i++ ) {
tMapItem* pVal = m_mapItems.GetValueAt(i);
ATLASSERT(pVal);
LPTLVITEM pItem = (*pVal)[ (int) wParam ];
ATLASSERT(pItem);
pVal->RemoveAt( (int) wParam );
_DeleteSubItem(pItem);
}
bHandled = FALSE;
return 0;
}
LRESULT OnHeaderItemChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
{
// Reposition first header columns
BOOL bDummy;
OnTreeItemExpanded(0, NULL, bDummy);
// Recalc column sizes
_CalcColumnSizes();
// Repaint, just in case...
T* pT = static_cast<T*>(this);
pT->UpdateLayout();
bHandled = FALSE;
return 0;
}
LRESULT OnHeaderBeginDrag(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
LPNMHEADER pnmhd = (LPNMHEADER) pnmh;
if( pnmhd->iItem == 0 ) return TRUE; // Cannot drag first column!
bHandled = FALSE;
return 0;
}
LRESULT OnHeaderEndDrag(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
LPNMHEADER pnmhd = (LPNMHEADER) pnmh;
// Cannot drag first column, really!
// Bug in MS control requires this extra check.
if( pnmhd->iItem == 0 ) return TRUE;
RECT rcItem;
m_ctrlHeader.GetItemRect(0, &rcItem);
DWORD dwPos = ::GetMessagePos();
POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
ScreenToClient(&pt);
if( pt.x <= rcItem.right ) return TRUE; // Cannot re-order first column
// Need to reposition the header
T* pT = static_cast<T*>(this);
pT->UpdateLayout();
bHandled = FALSE;
return 0;
}
// Helper methods
static long StretchWidth(long nWidth, long nMeasure)
{
return ((nWidth / nMeasure) + 1) * nMeasure;
}
// Custom draw
DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
if( lpNMCustomDraw->hdr.hwndFrom != m_ctrlTree ) return CDRF_DODEFAULT;
::SetViewportOrgEx(lpNMCustomDraw->hdc, m_nOffset, 0, NULL);
return CDRF_NOTIFYITEMDRAW; // We need per-item notifications
}
DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
if( lpNMCustomDraw->hdr.hwndFrom != m_ctrlTree ) return CDRF_DODEFAULT;
// Reset the focus because it will be drawn by us
m_iItemState = lpNMCustomDraw->uItemState;
lpNMCustomDraw->uItemState &= ~(CDIS_FOCUS | CDIS_SELECTED);
// Remember the drawing rectangle of the item so we can draw it ourselves
m_ctrlTree.GetItemRect( (HTREEITEM)lpNMCustomDraw->dwItemSpec, &m_rcItem, TRUE);
m_rcItem.right = max(lpNMCustomDraw->rc.right, m_cxHeader);
return CDRF_NOTIFYPOSTPAINT; // We need more notifications
}
DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
if( lpNMCustomDraw->hdr.hwndFrom != m_ctrlTree ) return CDRF_DODEFAULT;
T* pT = static_cast<T*>(this);
LPNMTVCUSTOMDRAW pCustomDraw = reinterpret_cast<LPNMTVCUSTOMDRAW>(lpNMCustomDraw);
pT->DrawTreeItem( pCustomDraw, m_iItemState, m_rcItem);
return CDRF_DODEFAULT;
}
// Overridables
void UpdateLayout()
{
ATLASSERT(::IsWindow(m_ctrlTree));
ATLASSERT(::IsWindow(m_ctrlHeader));
// FIX: Horizontal scrollbar fix by Nail Kaipov
m_nOffset = 0;
if( GetStyle() & WS_HSCROLL ) {
m_nOffset = -GetScrollPos(SB_HORZ); // read scrollbar position
}
// Reposition the header and tree control
RECT rcClient;
GetClientRect(&rcClient);
if( m_ctrlHeader.GetStyle() & WS_VISIBLE ) {
HDLAYOUT hdl = { 0 };
WINDOWPOS wpos;
hdl.prc = &rcClient;
hdl.pwpos = &wpos;
m_ctrlHeader.Layout(&hdl);
ATLASSERT(wpos.y==0);
ATLASSERT(rcClient.top!=0); // We assume that HDLAYOUT is updated (MSDN docs doesn't mention it)
m_ctrlTree.SetWindowPos(HWND_TOP, &rcClient, SWP_NOACTIVATE | SWP_NOZORDER);
m_ctrlHeader.SetWindowPos(HWND_TOP, wpos.x, wpos.y, wpos.cx, wpos.cy, SWP_NOACTIVATE);
}
else {
m_ctrlTree.SetWindowPos(HWND_TOP, &rcClient, SWP_NOACTIVATE | SWP_NOZORDER);
}
_CalcColumnSizes();
// FIX: Repaint entire control.
// NOTE:Cannot just use Invalidate() since WS_CLIPCHILDREN may not be there!!!
m_ctrlHeader.Invalidate();
m_ctrlTree.Invalidate();
}
void DrawTreeItem(LPNMTVCUSTOMDRAW lptvcd, UINT iState, const RECT& rcItem)
{
CDCHandle dc = lptvcd->nmcd.hdc;
HTREEITEM hItem = (HTREEITEM) lptvcd->nmcd.dwItemSpec;
tMapItem* pVal = m_mapItems.Lookup(hItem);
if( pVal == NULL ) return;
// NOTE: Having an ImageList attached to the TreeView control seems
// to produce some extra WM_ERASEBKGND msgs, which we can use to
// optimize the painting...
CImageList il = m_ctrlTree.GetImageList(TVSIL_NORMAL);
// If the item had focus then draw it
// NOTE: Only when images are used (see note above)
if( (iState & CDIS_FOCUS) != 0 && !il.IsNull() ) {
RECT rcFocus = rcItem;
rcFocus.left = 1;
dc.SetTextColor(::GetSysColor(COLOR_BTNTEXT));
dc.DrawFocusRect(&rcFocus);
}
// If it's selected, paint the selection background
if( iState & CDIS_SELECTED ) {
RECT rcHigh = rcItem;
dc.FillSolidRect(&rcHigh, ::GetSysColor(COLOR_HIGHLIGHT));
}
else if( il.IsNull() ) {
RECT rcHigh = rcItem;
dc.FillSolidRect(&rcHigh, lptvcd->clrTextBk);
}
// Always write text with background
dc.SetBkMode(OPAQUE);
dc.SetBkColor(::GetSysColor((iState & CDIS_SELECTED) != 0 ? COLOR_HIGHLIGHT : COLOR_WINDOW));
// Draw all columns of the item
RECT rc = rcItem;
int cnt = pVal->GetSize();
for( int i = 0; i < cnt; i++ ) {
LPTLVITEM pItem = (*pVal)[i];
ATLASSERT(pItem);
if( i != 0 ) rc.left = m_rcColumns[i].left;
rc.right = m_rcColumns[i].right;
if( pItem->mask & TLVIF_IMAGE ) {
ATLASSERT(!il.IsNull());
int cx, cy;
il.GetIconSize(cx, cy);
il.DrawEx(
pItem->iImage,
dc,
rc.left, rc.top,
min(cx, rc.right - rc.left), cy,
CLR_NONE, CLR_NONE,
ILD_TRANSPARENT);
rc.left += cx;
}
if( pItem->mask & TLVIF_TEXT ) {
rc.left += 2;
COLORREF clrText = lptvcd->clrText;
if( pItem->mask & TLVIF_TEXTCOLOR ) clrText = pItem->clrText;
if( iState & CDIS_SELECTED ) clrText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
dc.SetTextColor(clrText);
CFont font;
HFONT hOldFont = NULL;
if( pItem->mask & TLVIF_STATE ) {
LOGFONT lf;
::GetObject(m_ctrlTree.GetFont(), sizeof(LOGFONT), &lf);
if( pItem->state & TLVIS_BOLD ) lf.lfWeight += FW_BOLD - FW_NORMAL;
if( pItem->state & TLVIS_ITALIC ) lf.lfItalic = TRUE;
if( pItem->state & TLVIS_UNDERLINE ) lf.lfUnderline = TRUE;
if( pItem->state & TLVIS_STRIKEOUT ) lf.lfStrikeOut = TRUE;
font.CreateFontIndirect(&lf);
ATLASSERT(!font.IsNull());
hOldFont = dc.SelectFont(font);
}
UINT format = pItem->mask & TLVIF_FORMAT ? pItem->format : 0;
dc.DrawText(pItem->pszText,
-1,
&rc,
DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS | format);
if( pItem->mask & TLVIF_STATE ) dc.SelectFont(hOldFont);
}
}
}
};
class CTreeListViewCtrl : public CTreeListViewImpl<CTreeListViewCtrl>
{
public:
DECLARE_WND_SUPERCLASS(_T("WTL_TreeListView"), GetWndClassName())
};
#endif // !defined(AFX_TREELISTVIEW_H__20010510_B3AE_CB5E_0BEB_0080AD509054__INCLUDED_)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -