MFC模型树控件TreeCtrl实现按下Ctrl键多选,按下Shift键连选
MFC的List Box只需要将控件属性中的Selection项设置为Extended,即可实现Ctrl键多选和Shift键连选;然而MFC的Tree Contrl(TreeCtrl)没有像List Box那样设置某个属性就实现Ctrl键多选和Shift键连选的功能,网上大多数MFC的TreeCtrl多选功能是用复选框实现的,也有前辈想通过继承类CTreeCtrl来重写多选功能,其中实现该功能较好的示例为以下链接:http://www.pudn.com/Download/item/id/640887.html,经过试验,发现该示例按住Ctrl只能选中两个项目。于是在此示例的基础上,我重新写了按下Ctrl键的代码,实现了真正的多选。
上述链接中包含一个基于对话框的MFC应用程序示例,该示例演示了CTreeCtrEx的用法,要将CTreeCtrlEx合并到自己的项目中,请按照以下步骤操作:
(1)将TreeCtrlEx.cpp和TreeCtrlEx.h两个文件复制到项目目录;
(2)将它们添加到项目中;
(3)在需要的地方包含TreeCtrlEx.h(包含在StdAfx.h中非常方便);
(4)在需要多选的地方,将对象的类型由CTreeCtrl更改为CTreeCtrlEx。
TreeCtrlEx.cpp和TreeCtrlEx.h中只有以下6个函数是多选所需要的,其他的可根据自己的需要保留:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
BOOL SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem);
void ClearSelection(BOOL bMultiOnly = FALSE);
void SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point);
下面直接上我修改后TreeCtrlEx.cpp和TreeCtrlEx.h中的代码,有什么不对的地方,还请读者留言,大家一起学习。代码仅供参考,转载请标明出处。
TreeCtrlEx.h
#pragma once
#include "afxcmn.h"
#include "vector"
using namespace std;
class CTreeCtrlEx :
public CTreeCtrl
{
public:
CTreeCtrlEx();
~CTreeCtrlEx();
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
BOOL SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem);
void ClearSelection(BOOL bMultiOnly = FALSE);
protected:
void SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point);
public:
BOOL m_bEditLabelPending;
UINT m_idTimer;
HTREEITEM m_hClickedItem;
BOOL m_bSelectPending;
CPoint m_ptClick;
HTREEITEM m_hFirstSelectedItem;
BOOL m_bSelectionComplete;
bool bIsSelectedItem[100];
};
TreeCtrlEx.cpp
#include "stdafx.h"
#include "TreeCtrlEx.h"
#define TCEX_EDITLABEL 1
CTreeCtrlEx::CTreeCtrlEx()
{
m_bSelectPending = FALSE;
m_hClickedItem = NULL;
m_hFirstSelectedItem = NULL;
m_bSelectionComplete = TRUE;
m_bEditLabelPending = FALSE;
for (int i = 0; i < 100; i++)
{
bIsSelectedItem[i] = false;
}
}
CTreeCtrlEx::~CTreeCtrlEx()
{
}
BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
void CTreeCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
UINT nHitFlags = 0;
HTREEITEM hClickedItem = HitTest(point, &nHitFlags);
if (!(nFlags&(MK_CONTROL | MK_SHIFT)) && (GetStyle() & TVS_EDITLABELS) && (nHitFlags & TVHT_ONITEMLABEL))
if (hClickedItem == GetSelectedItem())
{
ClearSelection();
SelectItem(hClickedItem);
m_bEditLabelPending = TRUE;
m_idTimer = SetTimer(TCEX_EDITLABEL, GetDoubleClickTime(), NULL);
return;
}
m_bEditLabelPending = FALSE;
if (nHitFlags & TVHT_ONITEM)
{
SetFocus();
m_hClickedItem = hClickedItem;
BOOL bIsClickedItemSelected = GetItemState(hClickedItem, TVIS_SELECTED) & TVIS_SELECTED;
if (bIsClickedItemSelected)
{
m_bSelectPending = TRUE;
}
else
{
SelectMultiple(hClickedItem, nFlags, point);
m_bSelectPending = FALSE;
}
m_ptClick = point;
}
else
CTreeCtrl::OnLButtonDown(nFlags, point);
}
void CTreeCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bSelectPending)
{
SelectMultiple(m_hClickedItem, nFlags, point);
m_bSelectPending = FALSE;
}
m_hClickedItem = NULL;
CTreeCtrl::OnLButtonUp(nFlags, point);
}
void CTreeCtrlEx::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CWnd* pWnd = GetParent();
if (nChar == VK_NEXT || nChar == VK_PRIOR)
{
if (!(GetKeyState(VK_SHIFT) & 0x8000))
{
if (GetSelectedCount()>1)
ClearSelection(TRUE);
CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
m_hFirstSelectedItem = GetSelectedItem();
return;
}
m_bSelectionComplete = FALSE;
CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
HTREEITEM hSelectedItem = GetSelectedItem();
SelectItems(m_hFirstSelectedItem, hSelectedItem);
m_bSelectionComplete = TRUE;
if (pWnd)
{
NM_TREEVIEW tv;
memset(&tv.itemOld, 0, sizeof(tv.itemOld));
tv.hdr.hwndFrom = GetSafeHwnd();
tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
tv.hdr.code = TVN_SELCHANGED;
tv.itemNew.hItem = hSelectedItem;
tv.itemNew.state = GetItemState(hSelectedItem, 0xffffffff);
tv.itemNew.lParam = GetItemData(hSelectedItem);
tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
tv.action = TVC_UNKNOWN;
pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
}
}
else if (nChar == VK_UP || nChar == VK_DOWN)
{
HTREEITEM hSelectedItem = GetSelectedItem();
HTREEITEM hNextItem;
if (nChar == VK_UP)
hNextItem = GetPrevVisibleItem(hSelectedItem);
else
hNextItem = GetNextVisibleItem(hSelectedItem);
if (!(GetKeyState(VK_SHIFT) & 0x8000))
{
if (GetSelectedCount()>1)
ClearSelection(TRUE);
if (hNextItem)
CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
m_hFirstSelectedItem = GetSelectedItem();
return;
}
if (hNextItem)
{
m_bSelectionComplete = FALSE;
BOOL bSelect = !(GetItemState(hNextItem, TVIS_SELECTED) & TVIS_SELECTED);
SelectItem(hNextItem);
if (bSelect || (!(GetItemState(hSelectedItem, TVIS_SELECTED) & TVIS_SELECTED)))
SelectItems(m_hFirstSelectedItem, hNextItem);
m_bSelectionComplete = TRUE;
if (pWnd)
{
NM_TREEVIEW tv;
memset(&tv.itemOld, 0, sizeof(tv.itemOld));
tv.hdr.hwndFrom = GetSafeHwnd();
tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
tv.hdr.code = TVN_SELCHANGED;
tv.itemNew.hItem = hNextItem;
tv.itemNew.state = GetItemState(hNextItem, 0xffffffff);
tv.itemNew.lParam = GetItemData(hNextItem);
tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
tv.action = TVC_UNKNOWN;
pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
}
}
CWnd* pWnd = GetParent();
if (pWnd)
{
NMTVKEYDOWN tvk;
tvk.hdr.hwndFrom = GetSafeHwnd();
tvk.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
tvk.hdr.code = TVN_KEYDOWN;
tvk.wVKey = nChar;
tvk.flags = 0;
pWnd->SendMessage(WM_NOTIFY, tvk.hdr.idFrom, (LPARAM)&tvk);
}
}
else
CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CTreeCtrlEx::SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point)
{
NM_TREEVIEW tv;
memset(&tv.itemOld, 0, sizeof(tv.itemOld));
CWnd* pWnd = GetParent();
HTREEITEM hOldItem = GetSelectedItem();
if (hOldItem)
{
tv.itemOld.hItem = hOldItem;
tv.itemOld.state = GetItemState(hOldItem, 0xffffffff);
tv.itemOld.lParam = GetItemData(hOldItem);
tv.itemOld.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
}
m_bSelectionComplete = FALSE;
if (nFlags & MK_SHIFT)
{
if (!m_hFirstSelectedItem)
m_hFirstSelectedItem = GetSelectedItem();
SelectItems(m_hFirstSelectedItem, hClickedItem);
}
else if (nFlags & MK_CONTROL)
{
vector<HTREEITEM>vecItem;
HTREEITEM root = GetRootItem();
HTREEITEM child = GetChildItem(root);
while (child != NULL)
{
vecItem.push_back(child);
child = GetNextItem(child, TVGN_NEXT);
}
for (int i = 0; i < vecItem.size(); i++)
{
bIsSelectedItem[i] = GetItemState(vecItem[i], TVIS_SELECTED) & TVIS_SELECTED;
}
BOOL bIsClickedItemSelected = GetItemState(hClickedItem, TVIS_SELECTED) & TVIS_SELECTED;
if (pWnd)
{
tv.hdr.hwndFrom = GetSafeHwnd();
tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
tv.hdr.code = TVN_SELCHANGING;
tv.itemNew.hItem = hClickedItem;
tv.itemNew.state = GetItemState(hClickedItem, 0xffffffff);
tv.itemNew.lParam = GetItemData(hClickedItem);
tv.itemOld.hItem = NULL;
tv.itemOld.mask = 0;
tv.action = TVC_BYMOUSE;
tv.ptDrag.x = point.x;
tv.ptDrag.y = point.y;
pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
}
for (int i = 0; i < vecItem.size(); i++)
{
if (bIsSelectedItem[i] && vecItem[i] == hClickedItem)
SetItemState(vecItem[i], 0, TVIS_SELECTED);
else if (bIsSelectedItem[i] && vecItem[i] != hClickedItem)
SetItemState(vecItem[i], TVIS_SELECTED, TVIS_SELECTED);
else if (!bIsSelectedItem[i] && vecItem[i] == hClickedItem)
SetItemState(vecItem[i], TVIS_SELECTED, TVIS_SELECTED);
else if (!bIsSelectedItem[i] && vecItem[i] != hClickedItem)
SetItemState(vecItem[i], 0, TVIS_SELECTED);
}
}
else
{
ClearSelection();
SelectItem(hClickedItem);
SetItemState(hClickedItem, TVIS_SELECTED, TVIS_SELECTED);
m_hFirstSelectedItem = hClickedItem;
}
m_bSelectionComplete = TRUE;
if (pWnd)
{
tv.hdr.hwndFrom = GetSafeHwnd();
tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
tv.hdr.code = TVN_SELCHANGED;
tv.itemNew.hItem = m_hClickedItem;
tv.itemNew.state = GetItemState(m_hClickedItem, 0xffffffff);
tv.itemNew.lParam = GetItemData(m_hClickedItem);
tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
tv.action = TVC_UNKNOWN;
pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
}
}
BOOL CTreeCtrlEx::SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem)
{
HTREEITEM hItem = GetRootItem();
while (hItem && hItem != hFromItem && hItem != hToItem)
hItem = GetNextVisibleItem(hItem);
if (!hItem)
return FALSE;
BOOL bReverse = hItem == hToItem;
SelectItem(hToItem);
hItem = GetRootItem();
BOOL bSelect = FALSE;
while (hItem)
{
if (hItem == (bReverse ? hToItem : hFromItem))
bSelect = TRUE;
if (bSelect)
{
if (!(GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED))
SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
}
else
{
if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
SetItemState(hItem, 0, TVIS_SELECTED);
}
if (hItem == (bReverse ? hFromItem : hToItem))
bSelect = FALSE;
hItem = GetNextVisibleItem(hItem);
}
return TRUE;
}
void CTreeCtrlEx::ClearSelection(BOOL bMultiOnly)
{
for (HTREEITEM hItem = GetRootItem(); hItem != NULL; hItem = GetNextVisibleItem(hItem))
if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
SetItemState(hItem, 0, TVIS_SELECTED);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)