场景
- 在开发
Win32
,WTL
,MFC
程序时,经常会用到ListView
这个表格控件,ListView
的数据是按照行来显示的,行与行之间没有并没有什么关系。但是如果行之间有父子关系,比如像树形控件TreeView
可以收起展开呢,如何实现?
图1
说明
-
这种具有列名的
TreeView
可以称为多列TreeView(ColumnTreeView)
, 也可以称为TreeListView
. 这种控件它的特点就是TreeView
有一个表头,拖动表头的分割线可以同步拖动TreeView
的对应的列数据。 -
在
WTL
开发的时候,当对窗口消息和通知不熟练的时候,我们实现自定义控件可以借助MFC
的自定义控件例子,改为WTL
的实现方式也很容易,因为MFC
的大多数控件本质上还是对Win32
控件的封装,操作的还是它的窗口句柄和消息。 -
在我的 使用WTL进行Windows桌面应用开发-第一部 里已经讲过自定义的
ListView
控件,知道它可以自定义一个表头, 也就是扩展类CHeaderCtrl
的子类,增加一个复选框。 要实现多列TreeView
,原理就是增加一个容器窗口来管理一个CHeaderCtrl
和CTreeCtrl
, 在拖动CHeaderCtrl
时CTreeCtrl
能响应表头的拖动操作,根据表头每列的横坐标变化来绘制CTreeCtrl
的文本。 -
我参考[4]了
Multi-Column Tree View
的MFC
实现,改为依赖WTL
库. 有两个关键的通知:- hdn-itemchanged:
CHeaderCtrl
的父窗口通知,当表头项属性改变时(如宽度变化)响应处理函数。 - NM_CUSTOMDRAW:
CTreeCtrl
在父窗口里实现TreeView
的自定义HTREEITEM
项绘制,这样可以根据表头进行列数据的坐标调整。
- hdn-itemchanged:
例子
ccolumn_tree_view.h
/*********************************************************************
* Multi-Column Tree View, version 1.4 (July 7, 2005)
* Copyright (C) 2003-2005 Michal Mecinski.
*
* You may freely use and modify this code, but don't remove
* this copyright note.
*
* THERE IS NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, FOR
* THIS CODE. THE AUTHOR DOES NOT TAKE THE RESPONSIBILITY
* FOR ANY DAMAGE RESULTING FROM THE USE OF IT.
*
* E-mail: mimec@mimec.org
* WWW: http://www.mimec.org
********************************************************************/
#pragma once
#include "ccolumn_tree_view_ctrl.h"
#include <Windows.h>
#include <atlbase.h>
#include <atlapp.h>
#include <vector>
#include <functional>
#include <utility>
#include <atlwin.h>
#include "atlframe.h"
#include "atlctrls.h"
#include "atlmisc.h"
#include "atlcrack.h"
#include <GdiPlus.h>
#include "ccolumn_tree_view_header.h"
class CColumnTreeView : public CWindowImpl<CColumnTreeView, CWindow>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CWindow::GetWndClassName())
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
BEGIN_MSG_MAP_EX(CColumnTreeView)
MSG_WM_PAINT(OnPaint)
MSG_WM_ERASEBKGND(OnEraseBkgnd)
MSG_WM_SIZE(OnSize)
MSG_WM_CREATE(OnCreate)
MSG_WM_HSCROLL(OnHScroll)
MSG_WM_SETFONT(OnSetFont)
MSG_WM_GETFONT(OnGetFont);
COMMAND_ID_HANDLER(HeaderID,OnListViewHeaderCheck)
NOTIFY_HANDLER_EX(HeaderID,HDN_ITEMCHANGED, OnHeaderItemChanged)
NOTIFY_HANDLER_EX(HeaderID,HDN_DIVIDERDBLCLICK, OnHeaderDividerDblClick)
NOTIFY_HANDLER_EX(TreeID,NM_CUSTOMDRAW, OnTreeCustomDraw)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
CColumnTreeView():dpi_scale_(1.0),font_normal_(NULL){}
enum ChildrenIDs { HeaderID = 1, TreeID = 2 };
void UpdateColumns();
void AdjustColumnWidth(int nColumn, BOOL bIgnoreCollapsed);
CTreeViewCtrl& GetTreeCtrl() { return m_Tree; }
CHeaderCtrl& GetHeaderCtrl() { return m_Header; }
BOOL isHeaderChecked();
void SetCheckImage(Gdiplus::Bitmap* checked,Gdiplus::Bitmap* uncheck);
void SetButtonsImage(Gdiplus::Bitmap* folder,Gdiplus::Bitmap* file);
// 设置DPI缩放因子
void SetDpiScale(float dpi_scale);
int DpiScale(int value);
void ReadyView();
void setFuncHeaderCheck(std::function<bool(bool)> func_check);
void setAllItemCheck(bool check);
void setHeaderCheck(bool check);
void setFuncRootCheckListener(std::function<void(BOOL)> funcRootCheckListener);
private:
std::function<bool(bool)> funcHeaderCheck_;
protected:
LRESULT OnListViewHeaderCheck(WORD wNotify,WORD wID,HWND hCtrl, BOOL &bHandeld);
int OnCreate(LPCREATESTRUCT lpCreateStruct);
virtual void OnDraw(CDC* pDC) {}
void OnSetFont(CFontHandle font, BOOL bRedraw);
HFONT OnGetFont();
protected:
void UpdateScroller();
void RepositionControls();
int GetMaxColumnWidth(HTREEITEM hItem, int nColumn, int nDepth, BOOL bIgnoreCollapsed);
protected:
CColumnTreeViewCtrl m_Tree;
CColumnTreeViewHeader m_Header;
int m_cyHeader;
int m_cxTotal;
int m_xPos;
int m_arrColWidths[16];
int m_xOffset;
Gdiplus::Bitmap* folder_;
Gdiplus::Bitmap* file_;
Gdiplus::Bitmap* image_checked_;
Gdiplus::Bitmap* image_uncheck_;
protected:
void OnPaint(CDCHandle dc);
BOOL OnEraseBkgnd(CDCHandle dc);
void OnSize(UINT nType, CSize size);
void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar);
LRESULT OnHeaderItemChanged(LPNMHDR pnmh);
LRESULT OnHeaderDividerDblClick(LPNMHDR pnmh);
LRESULT OnTreeCustomDraw(LPNMHDR pnmh);
float dpi_scale_;
HFONT font_normal_;
typedef std::pair<Gdiplus::Image*,Gdiplus::Rect> GpImageData;
std::vector<GpImageData> gpDeferImageDataCache;
};
ccolumn_tree_view.cpp
/*********************************************************************
* Multi-Column Tree View, version 1.4 (July 7, 2005)
* Copyright (C) 2003-2005 Michal Mecinski.
*
* You may freely use and modify this code, but don't remove
* this copyright note.
*
* THERE IS NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, FOR
* THIS CODE. THE AUTHOR DOES NOT TAKE THE RESPONSIBILITY
* FOR ANY DAMAGE RESULTING FROM THE USE OF IT.
*
* E-mail: mimec@mimec.org
* WWW: http://www.mimec.org
********************************************************************/
#include "stdafx.h"
#include "ccolumn_tree_view.h"
#include <shlwapi.h>
#include <string>
#include <assert.h>
#include "utils.h"
#include <iostream>
#include "ccolumn_tree_view_ctrl.h"
#ifndef TVS_NOHSCROLL
#define TVS_NOHSCROLL 0x8000 // IE 5.0 or higher required
#endif
void CColumnTreeView::SetCheckImage(Gdiplus::Bitmap* checked,Gdiplus::Bitmap* uncheck){
image_checked_ = checked;
image_uncheck_ = uncheck;
}
void CColumnTreeView::SetButtonsImage(Gdiplus::Bitmap* folder,Gdiplus::Bitmap* file){
folder_ = folder;
file_ = file;
}
void CColumnTreeView::SetDpiScale(float dpi_scale)
{
dpi_scale_ = dpi_scale;
}
int CColumnTreeView::DpiScale(int value)
{
return dpi_scale_*value;
}
BOOL CColumnTreeView::isHeaderChecked()
{
return m_Header.GetIfChecked();
}
int CColumnTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (m_Tree.m_hWnd)
return 0;
assert(folder_ && file_ && image_checked_ && image_uncheck_);
// create tree and header controls as children
m_Tree.SetDpiScale(dpi_scale_);
m_Tree.GetWndClassInfo().m_wc.hbrBackground = AtlGetStockBrush(WHITE_BRUSH);
m_Tree.Create(m_hWnd, CRect(),0,WS_CHILD | WS_VISIBLE | TVS_NOHSCROLL | TVS_NOTOOLTIPS | TVS_HASBUTTONS | TVS_HASLINES
| TVS_LINESATROOT | TVS_FULLROWSELECT | TVS_DISABLEDRAGDROP,0,TreeID);
m_Header.SetCheckBoxImage(image_checked_,image_uncheck_);
m_Header.SetDpiScale(dpi_scale_);
m_Header.Create(m_hWnd,CRect(),0,WS_CHILD | WS_VISIBLE | HDS_FULLDRAG,0, HeaderID);
return 0;
}
void CColumnTreeView::setFuncRootCheckListener(std::function<void(BOOL)> funcRootCheckListener)
{
m_Tree.setFuncRootCheckListener(funcRootCheckListener);
}
HFONT CColumnTreeView::OnGetFont()
{
return font_normal_;
}
void CColumnTreeView::OnSetFont(CFontHandle font, BOOL bRedraw)
{
font_normal_ = font;
}
void CColumnTreeView::ReadyView()
{
// set correct font for the header
HFONT pFont = GetFont();
m_Header.SetFont(pFont);
m_Tree.SetFont(pFont);
// check if the common controls library version 6.0 is available
BOOL bIsComCtl6 = FALSE;
HMODULE hComCtlDll = LoadLibrary(L"comctl32.dll");
if (hComCtlDll)
{
typedef HRESULT (CALLBACK *PFNDLLGETVERSION)(DLLVERSIONINFO*);
PFNDLLGETVERSION pfnDllGetVersion = (PFNDLLGETVERSION)GetProcAddress(hComCtlDll, "DllGetVersion");
if (pfnDllGetVersion)
{
DLLVERSIONINFO dvi;
ZeroMemory(&dvi, sizeof(dvi));
dvi.cbSize = sizeof(dvi);
HRESULT hRes = (*pfnDllGetVersion)(&dvi);
if (SUCCEEDED(hRes) && dvi.dwMajorVersion >= 6)
bIsComCtl6 = TRUE;
}
FreeLibrary(hComCtlDll);
}
// calculate correct header's height
auto hdc = GetDC();
CDCHandle pDC(hdc);
pDC.SelectFont(pFont);
CSize szExt;
const wchar_t* A = L"A";
pDC.GetTextExtent(A,wcslen(L"A"),&szExt);
m_cyHeader = szExt.cy + (bIsComCtl6 ? DpiScale(7) :DpiScale(4));
// offset from column start to text start
m_xOffset = bIsComCtl6 ? DpiScale(9) : DpiScale(6);
m_xPos = 0;
UpdateColumns();
ReleaseDC(hdc);
}
void CColumnTreeView::OnPaint(CDCHandle dc1)
{
// do nothing
CPaintDC dc(m_hWnd);
}
BOOL CColumnTreeView::OnEraseBkgnd(CDCHandle dc)
{
return TRUE;
}
void CColumnTreeView::OnSize(UINT nType, CSize size)
{
DefWindowProc(WM_SIZE, (WPARAM)nType,MAKELPARAM(size.cx,size.cy));
UpdateScroller();
RepositionControls();
}
void CColumnTreeView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar)
{
CRect rcClient;
GetClientRect(&rcClient);
int cx = rcClient.Width();
int xLast = m_xPos;
switch (nSBCode)
{
case SB_LINELEFT:
m_xPos -= 15;
break;
case SB_LINERIGHT:
m_xPos += 15;
break;
case SB_PAGELEFT:
m_xPos -= cx;
break;
case SB_PAGERIGHT:
m_xPos += cx;
break;
case SB_LEFT:
m_xPos = 0;
break;
case SB_RIGHT:
m_xPos = m_cxTotal - cx;
break;
case SB_THUMBTRACK:
m_xPos = nPos;
break;
}
if (m_xPos < 0)
m_xPos = 0;
else if (m_xPos > m_cxTotal - cx)
m_xPos = m_cxTotal - cx;
if (xLast == m_xPos)
return;
SetScrollPos(SB_HORZ, m_xPos);
RepositionControls();
}
LRESULT CColumnTreeView::OnHeaderItemChanged(LPNMHDR pNMHDR)
{
UpdateColumns();
m_Tree.Invalidate();
return 0;
}
LRESULT CColumnTreeView::OnHeaderDividerDblClick(LPNMHDR pNMHDR)
{
NMHEADER* pNMHeader = (NMHEADER*)pNMHDR;
AdjustColumnWidth(pNMHeader->iItem, TRUE);
return 0;
}
void CColumnTreeView::setFuncHeaderCheck(std::function<bool(bool)> func_check)
{
funcHeaderCheck_ = func_check;
}
void CColumnTreeView::setAllItemCheck(bool check)
{
auto hItem = m_Tree.GetRootItem();
while(hItem){
m_Tree.SetSelfCheckState(hItem,check);
if(m_Tree.ItemHasChildren(hItem)){
m_Tree.SetChildCheckState(hItem,check);
}
hItem = m_Tree.GetNextSiblingItem(hItem);
}
}
void CColumnTreeView::setHeaderCheck(bool check)
{
m_Header.SetCheckAll(check);
m_Header.RefreshCheckRect();
}
LRESULT CColumnTreeView::OnListViewHeaderCheck(WORD wNotify,WORD wID,HWND hCtrl, BOOL &bHandeld)
{
setAllItemCheck(wNotify);
if(funcHeaderCheck_)
funcHeaderCheck_(wNotify);
return 0;
}
LRESULT CColumnTreeView::OnTreeCustomDraw(LPNMHDR pNMHDR)
{
NMCUSTOMDRAW* pNMCustomDraw = (NMCUSTOMDRAW*)pNMHDR;
NMTVCUSTOMDRAW* pNMTVCustomDraw = (NMTVCUSTOMDRAW*)pNMHDR;
LRESULT result = 0;
LRESULT* pResult = &result;
switch (pNMCustomDraw->dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
break;
case CDDS_ITEMPOSTPAINT:
{
HTREEITEM hItem = (HTREEITEM)pNMCustomDraw->dwItemSpec;
CRect rcItem = pNMCustomDraw->rc;
if (rcItem.IsRectEmpty())
{
// nothing to paint
*pResult = CDRF_DODEFAULT;
break;
}
CDCHandle dc(pNMCustomDraw->hdc);
//CMemoryDC dc(pNMCustomDraw->hdc,rcItem);
//dc.SetBkMode(TRANSPARENT);
CRect rcLabel;
m_Tree.GetItemRect(hItem, &rcLabel, TRUE);
auto item_data = (CColumnTreeViewCtrlData*)m_Tree.GetItemData(hItem);
BOOL is_folder = item_data->is_folder;
COLORREF crTextBk = pNMTVCustomDraw->clrTextBk;
COLORREF crText = pNMTVCustomDraw->clrText;
//COLORREF crWnd = GetSysColor(COLOR_WINDOW);
COLORREF crWnd = RGB(255,255,255);
COLORREF color_selected = GetSysColor(COLOR_HIGHLIGHT);
// clear the original label rectangle
CRect rcClear = rcLabel;
if (rcClear.left > m_arrColWidths[0] - DpiScale(1))
rcClear.left = m_arrColWidths[0] - DpiScale(1);
if ((pNMCustomDraw->uItemState & CDIS_SELECTED) == 0){
dc.FillSolidRect(&rcClear, crWnd);
}
int nColsCnt = m_Header.GetItemCount();
// draw horizontal lines...
int xOffset = 0;
for (int i=0; i<nColsCnt; i++)
{
xOffset += m_arrColWidths[i];
rcItem.right = xOffset-DpiScale(1);
//dc.DrawEdge(&rcItem, BDR_SUNKENINNER, BF_RIGHT);
}
// ...and the vertical ones
dc.DrawEdge(&rcItem, BDR_SUNKENINNER, BF_BOTTOM);
std::wstring strSub;
TCHAR szBuf[MAX_PATH]={0};
memset(szBuf,0,sizeof(szBuf));
m_Tree.GetItemText(hItem,szBuf,MAX_PATH);
auto pos = wcschr(szBuf,L'\t');
if(pos)
strSub.append(szBuf,pos-szBuf);
else
strSub = szBuf;
// calculate main label's size
CRect rcText(0,0,0,0);
dc.DrawText(strSub.c_str(),strSub.size(), &rcText, DT_NOPREFIX | DT_CALCRECT);
rcLabel.right = min(rcLabel.left + rcText.right + DpiScale(4), m_arrColWidths[0] - DpiScale(4));
CRect rcBack = rcLabel;
if (::GetWindowLong(m_Tree.m_hWnd, GWL_STYLE) & TVS_FULLROWSELECT)
{
int nWidth = 0;
for (int i=0; i<nColsCnt; i++)
nWidth += m_arrColWidths[i];
rcBack.right = nWidth - DpiScale(1);
if (rcBack.left > m_arrColWidths[0] - DpiScale(1))
rcBack.left = m_arrColWidths[0] - DpiScale(1);
}
if (rcBack.Width() < 0)
crTextBk = crWnd;
// draw label's background
// crTextBk != crWnd &&
if ((pNMCustomDraw->uItemState & CDIS_SELECTED)){
dc.FillSolidRect(&rcBack, color_selected); // crTextBk
}
// draw focus rectangle if necessary
if (pNMCustomDraw->uItemState & CDIS_FOCUS){
dc.DrawFocusRect(&rcBack);
}
// draw main label
gpDeferImageDataCache.clear();
xOffset = m_arrColWidths[0];
rcText = rcLabel;
rcText.DeflateRect(DpiScale(2), DpiScale(1));
auto data = (CColumnTreeViewCtrlData*)m_Tree.GetItemData(hItem);
auto imageCheck = data->checked?image_checked_:image_uncheck_;
auto widthCheck = DpiScale(image_checked_->GetWidth());
auto heightCheck = DpiScale(image_checked_->GetHeight());
int image_checky = rcText.top+(rcText.Height()-heightCheck)/2;
gpDeferImageDataCache.push_back(std::make_pair(imageCheck,
Gdiplus::Rect(rcText.left,image_checky,widthCheck,heightCheck)));
auto imageFile = (is_folder)?folder_:file_;
auto widthFile = DpiScale(imageFile->GetWidth());
auto heightFile = DpiScale(imageFile->GetHeight());
int imagex = rcText.left+widthCheck+DpiScale(8);
int imagey = rcText.top+(rcText.Height()-heightFile)/2;
gpDeferImageDataCache.push_back(std::make_pair(imageFile,
Gdiplus::Rect(imagex,imagey,widthFile,heightFile)));
dc.SetBkMode(TRANSPARENT);
CSize size_strSub;
dc.GetTextExtent(strSub.c_str(),strSub.size(),&size_strSub);
rcText.top = (rcText.Height()- size_strSub.cy)/2+rcText.top;
rcText.bottom = rcText.top+size_strSub.cy;
rcText.right = rcText.left+xOffset;
rcText.left = imagex+widthFile+DpiScale(4);
dc.SetTextColor(crText); // crText
dc.DrawText(strSub.c_str(),strSub.size(), rcText, DT_NOPREFIX | DT_END_ELLIPSIS);
if (!(::GetWindowLong(m_Tree.m_hWnd, GWL_STYLE) & TVS_FULLROWSELECT))
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
// draw other columns text
wchar_t* start = pos;
for (int i=1; i<nColsCnt; i++)
{
if(start && (start+1)){
start += 1;
pos = wcschr(start,L'\t');
if(pos){
strSub.clear();
strSub.append(start,pos-start);
start = pos;
}else{
strSub.clear();
strSub = start;
start = pos;
}
rcText = rcLabel;
rcText.left = xOffset;
rcText.right = xOffset + m_arrColWidths[i];
rcText.DeflateRect(m_xOffset, DpiScale(1),DpiScale(2), DpiScale(1));
dc.GetTextExtent(strSub.c_str(),strSub.size(),&size_strSub);
rcText.top = (rcText.Height()- size_strSub.cy)/2+rcText.top;
rcText.bottom = rcText.top+size_strSub.cy;
dc.DrawText(strSub.c_str(),strSub.size(), &rcText, DT_NOPREFIX | DT_END_ELLIPSIS);
}
xOffset += m_arrColWidths[i];
}
// 1. 最后再使用Graphics,避免Gdi和Gdi+混合调用.
auto sizeCache = gpDeferImageDataCache.size();
if(sizeCache){
Gdiplus::Graphics graphics(dc);
graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
for(auto i = 0; i<gpDeferImageDataCache.size();++i){
auto& one = gpDeferImageDataCache[i];
Utils::DrawImage(graphics,one.first,one.second);
}
}
}
*pResult = CDRF_DODEFAULT;
break;
default:
*pResult = CDRF_DODEFAULT;
}
return result;
}
void CColumnTreeView::UpdateColumns()
{
m_cxTotal = 0;
HDITEM hditem;
hditem.mask = HDI_WIDTH;
int nCnt = m_Header.GetItemCount();
if (nCnt > 16)
nCnt = 16;
// get column widths from the header control
for (int i=0; i<nCnt; i++)
{
if (m_Header.GetItem(i, &hditem))
{
m_cxTotal += m_arrColWidths[i] = hditem.cxy;
if (i==0)
m_Tree.m_cxFirstCol = hditem.cxy;
}
}
m_Tree.m_cxTotal = m_cxTotal;
UpdateScroller();
RepositionControls();
}
void CColumnTreeView::UpdateScroller()
{
CRect rcClient;
GetClientRect(&rcClient);
int cx = rcClient.Width();
int lx = m_xPos;
if (m_xPos > m_cxTotal - cx)
m_xPos = m_cxTotal - cx;
if (m_xPos < 0)
m_xPos = 0;
SCROLLINFO scrinfo;
scrinfo.cbSize = sizeof(scrinfo);
scrinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
scrinfo.nPage = cx;
scrinfo.nMin = 0;
scrinfo.nMax = m_cxTotal;
scrinfo.nPos = m_xPos;
SetScrollInfo(SB_HORZ, &scrinfo);
}
void CColumnTreeView::RepositionControls()
{
// reposition child controls
if (m_Tree.m_hWnd)
{
CRect rcClient;
GetClientRect(&rcClient);
int cx = rcClient.Width();
int cy = rcClient.Height();
// move to a negative offset if scrolled horizontally
int x = 0;
if (cx < m_cxTotal)
{
x = GetScrollPos(SB_HORZ);
cx += x;
}
m_Header.MoveWindow(-x, 0, cx, m_cyHeader);
m_Tree.MoveWindow(-x, m_cyHeader, cx, cy-m_cyHeader);
}
}
void CColumnTreeView::AdjustColumnWidth(int nColumn, BOOL bIgnoreCollapsed)
{
int nMaxWidth = GetMaxColumnWidth(m_Tree.GetRootItem(), nColumn, 0, bIgnoreCollapsed);
HDITEM hditem;
hditem.mask = HDI_WIDTH;
m_Header.GetItem(nColumn, &hditem);
hditem.cxy = nMaxWidth + 20;
m_Header.SetItem(nColumn, &hditem);
}
int CColumnTreeView::GetMaxColumnWidth(HTREEITEM hItem, int nColumn, int nDepth, BOOL bIgnoreCollapsed)
{
int nMaxWidth = 0;
std::wstring strSub;
TCHAR szBuf[MAX_PATH]={0};
m_Tree.GetItemText(hItem,szBuf,MAX_PATH);
auto pos = wcschr(szBuf,L'\t');
if (pos)
{
strSub.append(szBuf,pos-szBuf);
CDCHandle dc;
dc.CreateCompatibleDC(NULL);
HFONT pOldFont = dc.SelectFont(m_Tree.GetFont());
// calculate text width
CSize size_strSub;
dc.GetTextExtent(strSub.c_str(),strSub.size(),&size_strSub);
nMaxWidth = size_strSub.cx;
dc.SelectFont(pOldFont);
}
// add indent and image space if first column
if (nColumn == 0)
{
int nIndent = nDepth;
if (::GetWindowLong(m_Tree.m_hWnd, GWL_STYLE) & TVS_LINESATROOT)
nIndent++;
int nImage, nSelImage;
m_Tree.GetItemImage(hItem, nImage, nSelImage);
if (nImage >= 0)
nIndent++;
nMaxWidth += nIndent * m_Tree.GetIndent();
}
if (!bIgnoreCollapsed || (m_Tree.GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED))
{
// process child items recursively
HTREEITEM hSubItem = m_Tree.GetChildItem(hItem);
while (hSubItem)
{
int nSubWidth = GetMaxColumnWidth(hSubItem, nColumn, nDepth + 1, bIgnoreCollapsed);
if (nSubWidth > nMaxWidth)
nMaxWidth = nSubWidth;
hSubItem = m_Tree.GetNextSiblingItem(hSubItem);
}
}
return nMaxWidth;
}
ccolumn_tree_view_ctrl.h
// test-document-viewView.h : interface of the CColumnTreeViewCtrl class
//
/
#pragma once
#include <stdint.h>
#include <functional>
#include <Windows.h>
#include <atlbase.h>
#include <atlapp.h>
#include <atlwin.h>
#include "atlframe.h"
#include "atlctrls.h"
#include "atlmisc.h"
#include "atlcrack.h"
#include <GdiPlus.h>
typedef struct CColumnTreeViewCtrlData1
{
BOOL checked;
void* userdata;
BOOL is_folder;
int totalChildren;
int checkedChildren;
}CColumnTreeViewCtrlData;
class CColumnTreeViewCtrl : public CWindowImpl<CColumnTreeViewCtrl, CTreeViewCtrl>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CTreeViewCtrl::GetWndClassName())
CColumnTreeViewCtrl():dpi_scale_(1.0){}
// 设置DPI缩放因子
void SetDpiScale(float dpi_scale);
int DpiScale(int value);
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
BEGIN_MSG_MAP_EX(CColumnTreeViewCtrl)
MSG_WM_PAINT(OnPaint)
MSG_WM_ERASEBKGND(OnEraseBkgnd)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
protected:
void HandleMouse(UINT message, UINT nFlags, CPoint point);
protected:
int m_cxFirstCol;
int m_cxTotal;
void refreshCheck(HTREEITEM hItem);
BOOL getRootCheckState();
std::function<void(BOOL)> funcRootCheckListener_;
public:
void setFuncRootCheckListener(std::function<void(BOOL)> funcRootCheckListener);
public:
void SetParentCheckState(HTREEITEM hItem,BOOL checked);
void SetSelfCheckState(HTREEITEM hItem,BOOL checked);
void SetChildCheckState(HTREEITEM hItem,BOOL checked);
void OnPaint(CDCHandle dc);
BOOL OnEraseBkgnd(CDCHandle dc);
void OnLButtonDown(UINT nFlags, CPoint point);
void OnLButtonDblClk(UINT nFlags, CPoint point);
friend class CColumnTreeView;
float dpi_scale_;
};
ccolumn_tree_view_ctrl.cpp
/*********************************************************************
* Multi-Column Tree View, version 1.4 (July 7, 2005)
* Copyright (C) 2003-2005 Michal Mecinski.
*
* You may freely use and modify this code, but don't remove
* this copyright note.
*
* THERE IS NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, FOR
* THIS CODE. THE AUTHOR DOES NOT TAKE THE RESPONSIBILITY
* FOR ANY DAMAGE RESULTING FROM THE USE OF IT.
*
* E-mail: mimec@mimec.org
* WWW: http://www.mimec.org
********************************************************************/
#include "stdafx.h"
#include "ccolumn_tree_view_ctrl.h"
#include <string>
void CColumnTreeViewCtrl::SetDpiScale(float dpi_scale)
{
dpi_scale_ = dpi_scale;
}
int CColumnTreeViewCtrl::DpiScale(int value)
{
return dpi_scale_*value;
}
void CColumnTreeViewCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// mask left click if outside the real item's label
HandleMouse(WM_LBUTTONDOWN, nFlags, point);
}
void CColumnTreeViewCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
HandleMouse(WM_LBUTTONDBLCLK, nFlags, point);
}
void CColumnTreeViewCtrl::OnPaint(CDCHandle dc1)
{
CPaintDC dc(m_hWnd);
CRect rcClient;
GetClientRect(&rcClient);
CDC dcMem;
CBitmap bmpMem;
// use temporary bitmap to avoid flickering
dcMem.CreateCompatibleDC(dc);
if (bmpMem.CreateCompatibleBitmap(dc, rcClient.Width(), rcClient.Height()))
{
auto pOldBmp = dcMem.SelectBitmap(bmpMem);
// paint the window onto the memory bitmap
DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, 0);
// copy it to the window's DC
dc.BitBlt(0, 0, rcClient.right, rcClient.bottom, dcMem, 0, 0, SRCCOPY);
dcMem.SelectBitmap(pOldBmp);
bmpMem.DeleteObject();
}
dcMem.DeleteDC();
}
BOOL CColumnTreeViewCtrl::OnEraseBkgnd(CDCHandle dc)
{
return TRUE;
}
void CColumnTreeViewCtrl::HandleMouse(UINT message, UINT nFlags, CPoint point)
{
UINT fFlags;
HTREEITEM hItem = HitTest(point, &fFlags);
// verify the hit result
if (fFlags & (TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))
{
CRect rcItem;
GetItemRect(hItem, &rcItem, TRUE);
if (::GetWindowLong(m_hWnd, GWL_STYLE) & TVS_FULLROWSELECT)
{
if (message == WM_LBUTTONDOWN)
SetFocus();
// ignore if outside all columns
rcItem.right = m_cxTotal;
if (!rcItem.PtInRect(point))
return;
// select or expand item
if (message == WM_LBUTTONDOWN)
{
Select(hItem, TVGN_CARET);
rcItem.right = rcItem.left+DpiScale(24);
if(rcItem.PtInRect(point)){
auto data = (CColumnTreeViewCtrlData*)GetItemData(hItem);
data->checked = !data->checked;
InvalidateRect(rcItem);
if(ItemHasChildren(hItem)){
SetChildCheckState(hItem,data->checked);
}
SetParentCheckState(hItem,data->checked);
}
}
else if (message == WM_LBUTTONDBLCLK)
{
// send the NM_DBLCLK notification
NMHDR nmhdr;
nmhdr.hwndFrom = m_hWnd;
nmhdr.idFrom = GetDlgCtrlID();
nmhdr.code = NM_DBLCLK;
::SendMessage(GetParent(),WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
Expand(hItem, TVE_TOGGLE);
}
return;
}
else
{
// ignore if outside the first column
rcItem.right = m_cxFirstCol;
if (!rcItem.PtInRect(point))
{
if (message == WM_LBUTTONDOWN)
SetFocus();
return;
}
std::wstring strSub;
TCHAR szBuf[MAX_PATH]={0};
GetItemText(hItem,szBuf,MAX_PATH);
auto pos = wcschr(szBuf,L'\t');
if(pos)
strSub.append(szBuf,pos-szBuf);
else
strSub = szBuf;
CClientDC pDC(m_hWnd);
pDC.SelectFont(GetFont());
CSize size_str_sub;
pDC.GetTextExtent(strSub.c_str(),strSub.size(),&size_str_sub);
rcItem.right = rcItem.left + size_str_sub.cx + DpiScale(6);
ReleaseDC(pDC);
// ignore if outside the label's rectangle
if (!rcItem.PtInRect(point))
{
if (message == WM_LBUTTONDOWN)
SetFocus();
return;
}
}
}
else
{
// check if the button or icon is hidden
if (point.x >= m_cxFirstCol)
{
if (message == WM_LBUTTONDOWN)
SetFocus();
// ignore if outside all columns
if (point.x > m_cxTotal)
return;
// select or expand item
if (message == WM_LBUTTONDOWN)
{
Select(hItem, TVGN_CARET);
}
else if (message == WM_LBUTTONDBLCLK)
{
// send the NM_DBLCLK notification
NMHDR nmhdr;
nmhdr.hwndFrom = m_hWnd;
nmhdr.idFrom = GetDlgCtrlID();
nmhdr.code = NM_DBLCLK;
::SendMessage(GetParent(),WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
Expand(hItem, TVE_TOGGLE);
}
return;
}
}
// pass message to the default procedure
DefWindowProc(message, nFlags, MAKELONG(point.x, point.y));
}
BOOL CColumnTreeViewCtrl::getRootCheckState()
{
int checkedCount = 0;
int count = 0;
auto hItem = GetRootItem();
while(hItem){
auto itemData = (CColumnTreeViewCtrlData*)GetItemData(hItem);
count++;
if(!itemData->checked)
break;
checkedCount++;
hItem = GetNextSiblingItem(hItem);
}
return (checkedCount == count);
}
void CColumnTreeViewCtrl::setFuncRootCheckListener(std::function<void(BOOL)> funcRootCheckListener)
{
funcRootCheckListener_ = funcRootCheckListener;
}
void CColumnTreeViewCtrl::SetParentCheckState(HTREEITEM hItem,BOOL checked)
{
if(!hItem) return;
auto item_parent = GetParentItem(hItem);
if(!item_parent){
auto checkedRoot = getRootCheckState();
if(funcRootCheckListener_)
funcRootCheckListener_(checkedRoot);
return;
}
auto data = (CColumnTreeViewCtrlData*)GetItemData(item_parent);
if(checked){
data->checkedChildren++;
if(data->checkedChildren == data->totalChildren){
data->checked = checked;
refreshCheck(item_parent);
SetParentCheckState(item_parent,checked);
}else{
;
}
}else{
data->checkedChildren--;
if(data->checked){
data->checked = checked;
refreshCheck(item_parent);
SetParentCheckState(item_parent,checked);
}else{
;
}
}
}
void CColumnTreeViewCtrl::refreshCheck(HTREEITEM hItem)
{
CRect rect_item;
GetItemRect(hItem,rect_item,TRUE);
rect_item.right = rect_item.left+DpiScale(24);
InvalidateRect(rect_item);
}
void CColumnTreeViewCtrl::SetSelfCheckState(HTREEITEM hItem,BOOL checked){
auto data = (CColumnTreeViewCtrlData*)GetItemData(hItem);
data->checked = checked;
CRect rect_item;
GetItemRect(hItem,rect_item,TRUE);
rect_item.right = rect_item.left+DpiScale(24);
InvalidateRect(rect_item);
}
void CColumnTreeViewCtrl::SetChildCheckState(HTREEITEM hItem,BOOL checked){
auto one = GetChildItem(hItem);
while(one){
auto dataParent = (CColumnTreeViewCtrlData*)GetItemData(hItem);
dataParent->checkedChildren = (checked)?dataParent->totalChildren:0;
if(ItemHasChildren(one))
SetChildCheckState(one,checked);
auto data = (CColumnTreeViewCtrlData*)GetItemData(one);
data->checked = checked;
CRect rect_item;
GetItemRect(one,rect_item,TRUE);
rect_item.right = rect_item.left+DpiScale(24);
InvalidateRect(rect_item);
one = GetNextSiblingItem(one);
}
}
ccolumn_tree_view_header.h
#ifndef __CCOLUMN_TREE_VIEW_HEADER_H
#define __CCOLUMN_TREE_VIEW_HEADER_H
#include <atlbase.h>
#include <atlapp.h>
#include <atlwin.h>
#include "atlframe.h"
#include "atlctrls.h"
#include "atlmisc.h"
#include <GdiPlus.h>
class CColumnTreeViewHeader:public CWindowImpl<CColumnTreeViewHeader,CHeaderCtrl>,public CCustomDraw<CColumnTreeViewHeader>
{
public:
CColumnTreeViewHeader();
~CColumnTreeViewHeader();
// 设置DPI缩放因子
void SetDpiScale(float dpi_scale);
int DpiScale(int value);
BEGIN_MSG_MAP(CColumnTreeViewHeader)
MESSAGE_HANDLER(WM_CREATE,OnCreate)
MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
CHAIN_MSG_MAP_ALT(CCustomDraw<CColumnTreeViewHeader>,1)
END_MSG_MAP()
void SetCheckAll(int bcheck);
BOOL GetIfChecked();
void SetCheckBoxImage(Gdiplus::Bitmap* m_checkbox_yes,
Gdiplus::Bitmap* m_checkbox_no);
void RefreshCheckRect();
DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw);
DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw);
protected:
LRESULT OnCreate(UINT umsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled );
LRESULT OnPaint(UINT umsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled );
LRESULT OnLButtonDown(UINT umsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled );
private:
Gdiplus::Bitmap* m_checkbox_yes_;
Gdiplus::Bitmap* m_checkbox_no_;
bool checked_;
DWORD bkg_color_;
int m_nHeadHight;
int m_nSortColumn;
BOOL m_bAscend;
COLORREF m_cr3DHighLight;
COLORREF m_cr3DShadow;
COLORREF m_cr3DFace;
int m_iSpacing;
BOOL m_bDividerLines;
float dpi_scale_;
};
#endif
ccolumn_tree_view_header.cpp
#include "stdafx.h"
#include "ccolumn_tree_view_header.h"
#include <string>
#include <iostream>
#include <assert.h>
#include "utils.h"
CColumnTreeViewHeader::CColumnTreeViewHeader():
m_nSortColumn(-1)
{
bkg_color_ = RGB(242,242,242);
checked_ = false;
m_checkbox_yes_ = NULL;
m_checkbox_no_ = NULL;
dpi_scale_ = 1.0;
m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
}
LRESULT CColumnTreeViewHeader::OnCreate(UINT umsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled )
{
m_nHeadHight = DpiScale(25);
bHandled = FALSE;
return 0;
}
void CColumnTreeViewHeader::SetDpiScale(float dpi_scale)
{
dpi_scale_ = dpi_scale;
}
int CColumnTreeViewHeader::DpiScale(int value)
{
return dpi_scale_*value;
}
CColumnTreeViewHeader::~CColumnTreeViewHeader()
{
Detach();
}
void CColumnTreeViewHeader::SetCheckBoxImage(Gdiplus::Bitmap* m_checkbox_yes,
Gdiplus::Bitmap* m_checkbox_no)
{
m_checkbox_yes_ = m_checkbox_yes;
m_checkbox_no_ = m_checkbox_no;
}
void CColumnTreeViewHeader::RefreshCheckRect()
{
CRect rect_client;
GetClientRect(&rect_client);
assert(m_checkbox_yes_);
CRect rect;
rect.left = 0;
rect.top = 0;
rect.right = rect.left+DpiScale(m_checkbox_yes_->GetWidth())+DpiScale(4);
rect.bottom = rect_client.bottom;
InvalidateRect(rect,0);
}
DWORD CColumnTreeViewHeader::OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
{
Gdiplus::Graphics graphics(lpNMCustomDraw->hdc);
if(m_checkbox_yes_){
CRect rect;
GetClientRect(&rect);
int width_image = DpiScale(m_checkbox_yes_->GetWidth());
int height_image = DpiScale(m_checkbox_yes_->GetHeight());
rect.top+= (rect.Height()-height_image)/2;
if(checked_){
Utils::DrawImage(graphics,m_checkbox_yes_,DpiScale(5),rect.top,
width_image,height_image);
}else{
Utils::DrawImage(graphics,m_checkbox_no_,DpiScale(5),rect.top,
width_image,height_image);
}
}
auto color1 = GetSysColor(COLOR_ACTIVEBORDER);
Gdiplus::Color color;
color.SetFromCOLORREF(color1);
Gdiplus::Pen pen(color);
CRect rect;
GetClientRect(&rect);
graphics.DrawLine(&pen,rect.left,rect.bottom-1,rect.right,rect.bottom-1);
return CDRF_DODEFAULT;
}
DWORD CColumnTreeViewHeader::OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
{
return CDRF_NOTIFYPOSTPAINT;
}
LRESULT CColumnTreeViewHeader::OnLButtonDown(UINT umsg,WPARAM wparam,LPARAM lparam,BOOL& bHandled )
{
if(!m_checkbox_yes_)
{
bHandled = FALSE;
return 0;
}
POINT pt={LOWORD(lparam),HIWORD(lparam)};
HDHITTESTINFO hdht;
hdht.pt=pt;
int nItem=HitTest(&hdht);
if (nItem!=-1 && (hdht.flags & HHT_ONHEADER) && !(hdht.flags & HHT_ONDIVIDER) )
{
//5: 是checkbox的x位置,时间关系暂时先固定.
auto five5 = DpiScale(5);
if( five5 <= pt.x && pt.x <= five5+DpiScale(m_checkbox_yes_->GetWidth()))
{
CWindow parent = GetParent();
checked_ = !checked_;
parent.SendMessage(WM_COMMAND,MAKEWPARAM(GetWindowLong(GWL_ID),checked_),(LPARAM)m_hWnd);
Invalidate(FALSE);
bHandled = TRUE;
return 0;
}
}
bHandled = FALSE;
//增加判断识别点击了CheckBox坐标.
return 0;
}
void CColumnTreeViewHeader::SetCheckAll(int bcheck)
{
checked_ = bcheck;
}
BOOL CColumnTreeViewHeader::GetIfChecked()
{
return checked_;
}
utils.h
#ifndef __UTILS_H
#define __UTILS_H
#include <Windows.h>
#include <GdiPlus.h>
class Utils
{
public:
static void DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
int x,int y);
static void DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
int x,int y,int dest_width,int dest_height);
static void DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
Gdiplus::Rect dest_rect);
static BOOL Is64BitWindows();
};
#endif
utils.cpp
#include "stdafx.h"
#include "utils.h"
void Utils::DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
int x,int y)
{
DrawImage(graphics,image,x,y,image->GetWidth(),image->GetHeight());
}
void Utils::DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
int x,int y,int dest_width,int dest_height)
{
Gdiplus::Rect dest_rect(x,y,dest_width,dest_height);
DrawImage(graphics,image,dest_rect);
}
void Utils::DrawImage(Gdiplus::Graphics& graphics,Gdiplus::Image* image,
Gdiplus::Rect dest_rect)
{
graphics.DrawImage(image,dest_rect,0,0,image->GetWidth(),image->GetHeight(),
Gdiplus::UnitPixel);
}
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
static LPFN_ISWOW64PROCESS fnIsWow64Process = NULL;
//
// FUNCTION: SafeIsWow64Process(HANDLE, PBOOL)
//
// PURPOSE: This is a wrapper of the IsWow64Process API. It determines
// whether the specified process is running under WOW64. IsWow64Process
// does not exist prior to Windows XP with SP2 and Window Server 2003 with
// SP1. For compatibility with operating systems that do not support
// IsWow64Process, call GetProcAddress to detect whether IsWow64Process is
/// implemented in Kernel32.dll. If GetProcAddress succeeds, it is safe to
// call IsWow64Process dynamically. Otherwise, WOW64 is not present.
//
// PARAMETERS:
// * hProcess - A handle to the process.
// * Wow64Process - A pointer to a value that is set to TRUE if the process
// is running under WOW64. If the process is running under 32-bit Windows,
// the value is set to FALSE. If the process is a 64-bit application
// running under 64-bit Windows, the value is also set to FALSE.
//
// RETURN VALUE: If the function succeeds, the return value is TRUE.If
// IsWow64Process does not exist in kernel32.dll, or the function fails,
// the return value is FALSE.
//
static BOOL WINAPI SafeIsWow64Process(HANDLE hProcess, PBOOL Wow64Process)
{
if (fnIsWow64Process == NULL)
{
// IsWow64Process is not available on all supported versions of
// Windows. Use GetModuleHandle to get a handle to the DLL that
// contains the function, and GetProcAddress to get a pointer to the
// function if available.
HMODULE hModule = GetModuleHandle(L"kernel32.dll");
if (hModule == NULL)
{
return FALSE;
}
fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>(
GetProcAddress(hModule, "IsWow64Process"));
if (fnIsWow64Process == NULL)
{
return FALSE;
}
}
return fnIsWow64Process(hProcess, Wow64Process);
}
//
// FUNCTION: Is64BitOS()
//
// PURPOSE: The function determines whether the current operating system is
// a 64-bit operating system.
//
// RETURN VALUE: The function returns TRUE if the operating system is
// 64-bit; otherwise, it returns FALSE.
//
static BOOL Is64BitOS()
{
#if defined(_WIN64)
return TRUE; // 64-bit programs run only on Win64
#elif defined(_WIN32)
// 32-bit programs run on both 32-bit and 64-bit Windows
BOOL f64bitOS = FALSE;
return (SafeIsWow64Process(GetCurrentProcess(), &f64bitOS) && f64bitOS);
#else
return FALSE; // 64-bit Windows does not support Win16
#endif
}
BOOL Utils::Is64BitWindows()
{
return Is64BitOS();
}
MainFrm.h
// MainFrm.h : interface of the CMainFrame class
//
/
#pragma once
#include <math.h>
#include <algorithm>
#include <string>
#include <stdint.h>
static Gdiplus::Bitmap* GetBitmap(const wchar_t* path,int xdpi = 92,int ydpi = 92)
{
Gdiplus::Bitmap* image = new Gdiplus::Bitmap(path);
if(image->GetLastStatus()!=Gdiplus::Ok)
{
return NULL;
}
if (floor(image->GetHorizontalResolution()) != xdpi ||
floor(image->GetVerticalResolution()) != ydpi)
{
image->SetResolution(xdpi,ydpi);
}
return image;
}
class CMainFrame :
public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CColumnTreeView* treeView_;
//CImageList m_ImgList;
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
return treeView_->PreTranslateMessage(pMsg);
}
virtual BOOL OnIdle()
{
return FALSE;
}
BEGIN_UPDATE_UI_MAP(CMainFrame)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
void AddBoolItemData(CTreeViewCtrl& tree,HTREEITEM item,BOOL check){
auto data = (CColumnTreeViewCtrlData*)malloc(sizeof(CColumnTreeViewCtrlData));
memset(data,0,sizeof(CColumnTreeViewCtrlData));
data->checked = check;
tree.SetItemData(item,(DWORD_PTR)data);
}
bool onHeaderCheckkAll(bool check)
{
return true;
}
void onTreeCtrlCheckAll(bool check)
{
}
void addOneTreeItem(const std::wstring& name,const std::wstring& url,HTREEITEM item_parent,
bool include_children,int childrenCount)
{
auto& tree = treeView_->GetTreeCtrl();
std::wstring value = name;
std::replace(value.begin(),value.end(),L'\t',L' ');
value.append(L"\t").append(url);
HTREEITEM hItem = tree.InsertItem(value.c_str(), 0, 0, item_parent,TVI_LAST);
addBoolItemData(tree,hItem,FALSE,NULL,include_children,childrenCount);
if(!include_children)
return;
for(int i = 0; i< childrenCount; ++i){
addOneTreeItem(name,url,hItem,false,0);
}
}
void addBoolItemData(CTreeViewCtrl& tree,HTREEITEM item,BOOL check,
void* userdata,bool isFolder,int childrenCount)
{
auto data = (CColumnTreeViewCtrlData*)malloc(sizeof(CColumnTreeViewCtrlData));
memset(data,0,sizeof(CColumnTreeViewCtrlData));
data->checked = check;
data->userdata = userdata;
data->is_folder = isFolder;
data->totalChildren = childrenCount;
tree.SetItemData(item,(DWORD_PTR)data);
}
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
auto folder_ = GetBitmap(L"res\\content-bookmarks-folder.png");
auto file_ = GetBitmap(L"res\\content-bookmarks-file.png");
auto image_checked_ = GetBitmap(L"res\\content-check-yes.png");
auto image_uncheck_ = GetBitmap(L"res\\content-check-no.png");
treeView_ = new CColumnTreeView();
treeView_->SetButtonsImage(folder_,file_);
treeView_->SetCheckImage(image_checked_,image_uncheck_);
treeView_->Create(m_hWnd, CRect(CPoint(0,0),CSize(600,400)), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,0);
auto func = std::bind(&CMainFrame::onHeaderCheckkAll,this,std::placeholders::_1);
treeView_->setFuncHeaderCheck(func);
auto func2 = std::bind(&CMainFrame::onTreeCtrlCheckAll,this,std::placeholders::_1);
treeView_->setFuncRootCheckListener(func2);
CTreeViewCtrl& tree = treeView_->GetTreeCtrl();
tree.SetBkColor(RGB(255,255,255));
CHeaderCtrl& header = treeView_->GetHeaderCtrl();
tree.SetItemHeight(32);
treeView_->ReadyView();
HDITEM hditem;
hditem.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT;
hditem.fmt = HDF_CENTER | HDF_STRING;
hditem.cxy = 300;
hditem.pszText = (wchar_t*)L"name";
header.InsertItem(0, &hditem);
hditem.cxy = 300;
hditem.pszText = (wchar_t*)L"url";
header.InsertItem(1, &hditem);
treeView_->UpdateColumns();
addMockData();
// register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return 0;
}
void addMockData()
{
for(auto i = 0; i< 10; ++i){
std::wstring name(L"Tobey-");
name.append(std::to_wstring((int64_t)i));
addOneTreeItem(name,L"https://blog.csdn.net/infoworld",NULL,true,4);
}
}
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
// unregister message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->RemoveMessageFilter(this);
pLoop->RemoveIdleHandler(this);
bHandled = FALSE;
return 1;
}
LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
PostMessage(WM_CLOSE);
return 0;
}
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
// TODO: add code to initialize document
return 0;
}
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CAboutDlg dlg;
dlg.DoModal();
return 0;
}
};
资源图片
输出
图2:
下载
https://download.csdn.net/download/infoworld/18781186