Win32 SDK TreeView 控件的基本用法,节点的编辑,拖动,添加,删除,弹出菜单

发布时间 2023-05-08 18:03:06作者: XGZ21

TreeView控件老是用的稀里糊涂的,写个测试程序总结一下基本用法

要注意的是控件是发通知给父窗口处理,因此消息中鼠标的坐标不是相对控件的,要从屏幕坐标转换来。

程序是WIN32 SDK向导生成的C语言框架。树的操作定义成一个类。 

 

 

// Tree.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"

//xgz 扩展控件用库
#include "Commctrl.h"
#pragma comment(lib,"comctl32.lib")

//xgz==========
#define IDC_EDIT                1001 
#define IDC_STATUSBAR           1002
#define IDC_TOOLSBAR            1003
#define IDC_VIEW_TREEVIEW1      1101 

HWND m_hWndEdit;
HWND m_hStatusbar;
HWND m_hToolsbar;

int OnCreate(HWND, UINT, WPARAM, LPARAM);
int OnSize(HWND, UINT, WPARAM, LPARAM);
int OnPaint(HWND, UINT, WPARAM, LPARAM);
int OnTimer(HWND, UINT, WPARAM, LPARAM);
int OnNotify(HWND, UINT, WPARAM, LPARAM); //控件的通知是发给父窗口处理
int OnLButtonDown(HWND, UINT, WPARAM, LPARAM);
int OnLButtonUp(HWND, UINT, WPARAM, LPARAM);
int OnMouseMove(HWND, UINT, WPARAM, LPARAM);
int OnFileNew(HWND, UINT, WPARAM, LPARAM);
int OnFileOpen(HWND, UINT, WPARAM, LPARAM);
int OnFileSave(HWND, UINT, WPARAM, LPARAM);
int OnTreeEdit(HWND, UINT, WPARAM, LPARAM);

int PRINT(const TCHAR* fmt, ...)
{
    TCHAR buffer[1024];
    va_list argptr;
    int cnt;
    int iEditTextLength;
    HWND hWnd = m_hWndEdit;
    if (NULL == hWnd) return 0;

    va_start(argptr, fmt);
    cnt = wvsprintf(buffer, fmt, argptr);  // not %f
    va_end(argptr);
    iEditTextLength = GetWindowTextLength(hWnd);
    if (iEditTextLength + cnt > 30000)       // edit text max length is 30000
    {
        SendMessage(hWnd, EM_SETSEL, 0, 10000);
        SendMessage(hWnd, WM_CLEAR, 0, 0);
        iEditTextLength = iEditTextLength - 10000;
    }
    SendMessage(hWnd, EM_SETSEL, iEditTextLength, iEditTextLength);
    SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)buffer);

    return(cnt);
}

//============

class CTree
{
public:
    CTree();
    ~CTree();

    HMENU        m_hMenu, m_hPopMenu;
    HINSTANCE    m_hInst;
    HWND        m_hWnd;
    HTREEITEM    m_hRoot;
    HWND        CreateTreeView(HWND hWndParent);
    int           InitTreetView();
    HTREEITEM    AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel);
    HTREEITEM    InsertTreeNode(HWND hWndTV, HTREEITEM hPrev, LPTSTR lpszItem);
    //int   DeleteTreetView(); //TreeView_DeleteAllItems(m_hWndTree);
    void        ExpandAll(HTREEITEM hItem);

    HTREEITEM        m_hBeginDrag;         //记录拖动起点的Item
    HIMAGELIST        m_hDragImageList;   //拖动的图像列表

    BOOL        m_bDrag;                   //用来判断是否进行拖动
    void        CopyNodes(HTREEITEM hDesItem, HTREEITEM hSrcItem);//复制节点函数
     
    int       OnTvnBegindrag(NMHDR* pNMHDR);
    int        OnLButtonUp(HWND, UINT, WPARAM, LPARAM);
    int        OnMouseMove(HWND, UINT, WPARAM, LPARAM);
    int        OnTvnBeginlabeledit(LPNMHDR lphr);
    int       OnTvnEndlabeledit(LPNMHDR lphr);
    
    int        OnLClkTree(LPNMHDR lphr);
    int        OnRClkTree(LPNMHDR lphr);
    int        OnDBLClkTree(LPNMHDR lphr);

    int        ItemLabelEdit();
    int        InsertChild();
    int        InsertSibling();
    int       DeleteItem();

};

CTree::CTree()
{
}
CTree::~CTree()
{
}

CTree m_Tree1;

HWND CTree::CreateTreeView(HWND hWndParent)
{
    InitCommonControls();

    m_hInst = (HINSTANCE)GetWindowLong(hWndParent, GWL_HINSTANCE);

    HWND hWndTreeView = CreateWindow(WC_TREEVIEW, _T("Tree"),
        WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS | TVS_LINESATROOT | WS_BORDER | TVS_EDITLABELS,
        0, 0, 0, 0,
        hWndParent,
        (HMENU)IDC_VIEW_TREEVIEW1,
        m_hInst,
        NULL);

    HIMAGELIST hImageList;
    hImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 15, 15);             //size 16x16  X15 
    for (unsigned int i = 0; i < 15; i++)
    {
        HICON  hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_ICON1 + i));
        ImageList_AddIcon(hImageList, hIcon);
        DestroyIcon(hIcon);
    }
    SendMessage(hWndTreeView, TVM_SETIMAGELIST, 0, (LPARAM)hImageList);

    m_hWnd = hWndTreeView; //xgz 直接保存
    
    m_hMenu = LoadMenu(m_hInst, (LPCSTR)IDC_TREE);  
    m_hPopMenu = GetSubMenu(m_hMenu, 2);    //XGZ 用主菜单的第三列菜单作为弹出菜单

    return (hWndTreeView);

}

int CTree::InitTreetView()
{

    HTREEITEM hti;

    m_hRoot = InsertTreeNode(m_hWnd, NULL, _T("Root"));
    hti = InsertTreeNode(m_hWnd, m_hRoot, _T("Node1"));
    hti = InsertTreeNode(m_hWnd, m_hRoot, _T("Node2"));
    hti = InsertTreeNode(m_hWnd, m_hRoot, _T("Node3"));
    hti = InsertTreeNode(m_hWnd, hti, _T("Node3-1"));
    hti = InsertTreeNode(m_hWnd, hti, _T("Node3-1-1"));
   
    //TreeView_Expand(m_hWnd, m_hRoot, TVE_EXPAND);//只展开一个节点
    ExpandAll(m_hRoot);   //递归展开所有节点

    return 1;
}

HTREEITEM CTree::AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
{
    TVITEM tvi;
    TVINSERTSTRUCT tvins;
    static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
    static HTREEITEM hPrevRootItem = NULL;
    static HTREEITEM hPrevLev2Item = NULL;
    HTREEITEM hti;

    tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATE;

    // Set the text of the item. 
    tvi.pszText = lpszItem;
    tvi.cchTextMax = sizeof(tvi.pszText) / sizeof(tvi.pszText[0]);

    // Assume the item is not a parent item, so give it a 
    // document image. 
    tvi.iImage = 0;// g_nDocument; 
    tvi.iSelectedImage = 0;// g_nDocument; 

    // Save the heading level in the item's application-defined 
    // data area. 
    tvi.lParam = (LPARAM)nLevel;
    tvins.item = tvi;
    tvins.hInsertAfter = hPrev;

    // Set the parent item based on the specified level. 
    if (nLevel == 1)
        tvins.hParent = TVI_ROOT;
    else if (nLevel == 2)
        tvins.hParent = hPrevRootItem;
    else
        tvins.hParent = hPrevLev2Item;

    // Add the item to the tree-view control. 
    hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM,
        0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);

    if (hPrev == NULL)
        return NULL;

    // Save the handle to the item. 
    if (nLevel == 1)
        hPrevRootItem = hPrev;
    else if (nLevel == 2)
        hPrevLev2Item = hPrev;

    // The new item is a child item. Give the parent item a 
    // closed folder bitmap to indicate it now has child items. 
    if (nLevel > 1)
    {
        hti = TreeView_GetParent(hwndTV, hPrev);
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
        tvi.hItem = hti;
        tvi.iImage = 2;//g_nClosed; 
        tvi.iSelectedImage = 2;// g_nClosed; 
        TreeView_SetItem(hwndTV, &tvi);
    }

    return hPrev;
}

HTREEITEM CTree::InsertTreeNode(HWND hWndTV, HTREEITEM hPrev, LPTSTR lpszItem)
{
    TVINSERTSTRUCT tvins;
    TVITEM tvi;

    tvins.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; //TVIF_TEXT| TVIF_PARAM; 
    tvins.item.pszText = lpszItem;

    if (NULL != hPrev)   //把上级图标改为文件夹
    {
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;   //只处理 IMAGE
        tvi.hItem = hPrev;
        tvi.iImage = 10;
        tvi.iSelectedImage = 10;
        TreeView_SetItem(hWndTV, &tvi);
    }

    tvins.item.iImage = 0;          //初始图标都是文件 
    tvins.item.iSelectedImage = 0;

    tvins.hParent = hPrev;
    tvins.hInsertAfter = TVI_LAST;

    return (HTREEITEM)SendMessage(hWndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
}

void CTree::ExpandAll(HTREEITEM hItem)
{
    if (!hItem)    return;

    HTREEITEM hChild = TreeView_GetChild(m_hWnd, hItem);

    ExpandAll(hChild);

    if (hChild) TreeView_Expand(m_hWnd, hItem, TVE_EXPAND);//XGZ 有子节点的展开父节点,上个返回可能是NULL

    HTREEITEM hSibling = TreeView_GetNextSibling(m_hWnd, hItem);

    ExpandAll(hSibling);
    //XGZ  sibling节点无需展开
    return;
}

//复制节点,将hSrcItem及其子项目复制到下hDesItem节点下
void CTree::CopyNodes(HTREEITEM hDesItem, HTREEITEM hSrcItem)
{
    if (hDesItem == NULL || hSrcItem == NULL)                //Item为空则不进行操作
    {
        return;
    }
    TVITEM tvItem;                            //定义项目信息结构体
    tvItem.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM;    //xgz 必须要copy param
    tvItem.hItem = hSrcItem;
    char chText[MAX_PATH] = { 0 };
    tvItem.pszText = chText;
    tvItem.cchTextMax = MAX_PATH;
    //GetItem(&tvItem);                    //获取项目信息
    TreeView_GetItem(m_hWnd, &tvItem);

    TVINSERTSTRUCT tvInsert;                //定义插入操作的数据结构
    tvInsert.hParent = hDesItem;                        //定义插入的父节点
    tvInsert.item = tvItem;                             //定义插入的子节点

    HTREEITEM hInsert = TreeView_InsertItem(m_hWnd, &tvInsert);//插入Item,并获取插入完之后的Item新的句柄

    //Item里的所有子Item也插过来
    HTREEITEM hChild = TreeView_GetChild(m_hWnd, hSrcItem);//获取子节点

    while (hChild != NULL)
    {
        CopyNodes(hInsert, hChild);                //递归复制子节点
        hChild = TreeView_GetNextSibling(m_hWnd, hChild); //获取下一个兄弟节点
    }
}

//WM_NOTIFY 消息在 wParam 中包含发送消息的控件的 ID,在 lParam 中包含指向结构的指针。
//该结构要么是 NMHDR 结构,要么是具有 NMHDR 结构作为其第一个成员的较大结构。 
//由于 NMHDR 是第一个成员,因此指向此结构的指针可用作指向 NMHDR 的指针,也可以用作指向较大结构的指针,具体取决于转换方式。
//开始拖动时控件发通知给父窗口,父窗口须记录源树节点,捕获鼠标以便处理移动消息
int CTree::OnTvnBegindrag(NMHDR* pNMHDR)
{
  
    //LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    //// TODO: Add your control notification handler code here
    //*pResult = 0;

    NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
    HTREEITEM hItem = pNMTreeView->itemNew.hItem;        //获取开始拖动的Item的句柄
    if (hItem == TreeView_GetRoot(m_hWnd))         //根节点不能拖动
    {
        return 0;
    }

    m_hBeginDrag = hItem;                       //保存拖动的树节点

    POINT dragPt;                               //拖动位置坐标,相对树控件
    dragPt.x = pNMTreeView->ptDrag.x;
    dragPt.y = pNMTreeView->ptDrag.y;

    m_hDragImageList = (HIMAGELIST)SendMessage(m_hWnd, TVM_CREATEDRAGIMAGE, 0, (LPARAM)pNMTreeView->itemNew.hItem); //创建拖动的图像列表

    if (m_hDragImageList != NULL)
    {
        ImageList_BeginDrag(m_hDragImageList, 0, 8, 8); //xgz 拖动图标加个(8,8)位置偏移,避免光标遮挡
        ClientToScreen(m_hWnd, &(dragPt));                  //转换客户坐标到屏幕坐标
        ImageList_DragEnter(m_hWnd, dragPt.x, dragPt.y); //在拖动的过程中显示拖动的图像 
        SetCapture(GetParent(m_hWnd));                             //树的父窗口开始鼠标捕捉
        m_bDrag = TRUE;                           //这里表示当前状态是拖动操作
    }
    return 0;
}

//父窗口的鼠标消息,和控件可能有位移,所以不用lParam中的坐标,而是从屏幕坐标转换
int CTree::OnMouseMove(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (m_bDrag)                        //处于拖动状态
    {
        HTREEITEM hItem;
        UINT nHitFlags;
            
        POINT point;
        GetCursorPos(&point);
        ScreenToClient(m_hWnd, &point); //控件坐标

        ImageList_DragMove(point.x, point.y);  //设置拖动的图像位置为鼠标位置,相对于控件显示

        //设置鼠标经过时高亮显示选中
        TVHITTESTINFO tvhit;
        tvhit.pt = point;
        hItem = TreeView_HitTest(m_hWnd, &tvhit);
        if (hItem != NULL)
        {
            ImageList_DragShowNolock(FALSE);            //隐藏拖动的图像
            TreeView_SelectDropTarget(m_hWnd, hItem);//设置选中的项目

            //若有子节点就展开
            HTREEITEM hChild = TreeView_GetChild(m_hWnd, hItem);
            if (hChild) TreeView_Expand(m_hWnd, hItem, TVE_EXPAND);//XGZ 有子节点的展开父节点,上个返回可能是NULL

            ImageList_DragShowNolock(TRUE);  //显示拖动的图像,展开子节点后再显示,避免残影
        }
        return 1;
    }
    else
    {
        //CTreeCtrl::OnMouseMove(nFlags, point);//MFC如果不是拖动,就交父类默认处理
        return 1; //xgz 自己封装SDK API,父类没处理,直接返回
    }
}

//拖放完成可以用UP,也可以用Down,习惯上可能多用UP
int CTree::OnLButtonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //CTreeCtrl::OnLButtonUp(nFlags, point);  //FMC的基本调用格式,nFlags可以是键盘状态等,在WPARAM中,
    POINT point;
    point.x = LOWORD(lParam);
    point.y = HIWORD(lParam);

    if (m_bDrag == TRUE)   //正在拖动
    {
        m_bDrag = FALSE;                 //结束拖动状态
        ImageList_DragLeave(m_hWnd);  //隐藏拖动图像,允许窗口进行更新            (3)
        ImageList_EndDrag();              //结束图像的拖动                          (4)
        ReleaseCapture();                     //释放鼠标捕捉

        ImageList_Destroy(m_hDragImageList);  //释放图像列表
        m_hDragImageList = NULL;

        HTREEITEM hItemDest;  //拖放目的地的Item

        TVHITTESTINFO tvhit;
        GetCursorPos(&point);
        ScreenToClient(m_hWnd, &point);
        tvhit.pt = point;
        tvhit.flags = TVHT_TORIGHT;
        hItemDest = TreeView_HitTest(m_hWnd, &tvhit);

        if (hItemDest != NULL)  //目的地有效
        {
            BOOL blMove = TRUE; //可以移动的条件:目的节点不是源节点的父节点,目的不是源也不是源的子孙

            if (hItemDest == TreeView_GetParent(m_hWnd, m_hBeginDrag)) blMove = FALSE; //目标和源的父相同

            HTREEITEM hItem = hItemDest;
            while (hItem != NULL)
            {
                if (hItem == m_hBeginDrag)  //目标或目标的父节点和源节点相同则不能移动
                {
                    blMove = FALSE;
                    break;
                }
                hItem = TreeView_GetParent(m_hWnd, hItem); //向上查找
            }

            if (blMove) //满足移动条件
            {
                HTREEITEM hItemScrParent = TreeView_GetParent(m_hWnd, m_hBeginDrag);

                CopyNodes(hItemDest, m_hBeginDrag);            //完成节点的复制
                
                TreeView_DeleteItem(m_hWnd, m_hBeginDrag); //删除原Item,其子节点也会自动删除。


                TVITEM tvi;
                tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;   //只处理 IMAGE
                tvi.hItem = hItemDest;                        //有了子节点图标应该是文件夹
                tvi.iImage = 10;
                tvi.iSelectedImage = 10;
                TreeView_SetItem(m_hWnd, &tvi);

                if (TreeView_GetChild(m_hWnd, hItemScrParent) == NULL)  //如果源的父没有子了
                {
                    tvi.hItem = hItemScrParent;             
                    tvi.iImage = 0;                        //图标改成文件
                    tvi.iSelectedImage = 0;
                    TreeView_SetItem(m_hWnd, &tvi);
                }
            }
            TreeView_SelectDropTarget(m_hWnd, NULL);   //取消移入Item的选中状态
            m_hBeginDrag = NULL;
        }
    }
    return 1;
}

int CTree::OnTvnBeginlabeledit(LPNMHDR lphr)
{
    TV_DISPINFO* pTVDispinfo = (TV_DISPINFO*)lphr;
    HWND hWndEdit = TreeView_GetEditControl(m_hWnd); //xgz 这是EDIT控件,可以发消息
    if (pTVDispinfo->item.lParam == 0)  //0 是取消了编辑
        return 1;  //阻止编辑
    else
        return 0;  //允许编辑
}

int CTree::OnTvnEndlabeledit(NMHDR* pNMHDR)
{
//    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

    TV_DISPINFO* pTVDispinfo = (TV_DISPINFO*)pNMHDR;
    if (pTVDispinfo->item.pszText != 0)  //xgz 修改了才更新,0是取消,若取消编辑保持原来的字符串
    {
        //SetItemText(pTVDispinfo->item.hItem, pTVDispinfo->item.pszText);
        //TreeView_SetItem(m_hWnd, &(pTVDispinfo->item));
        
        TVITEM tvi;
        
        tvi.mask = TVIF_TEXT;   //只处理 TEXT
        tvi.hItem = pTVDispinfo->item.hItem;
        tvi.pszText = pTVDispinfo->item.pszText;
        TreeView_SetItem(m_hWnd, &tvi);
    }
    return 0;
}

int CTree::OnLClkTree(LPNMHDR lphr)
{
    TVHITTESTINFO thti;
    HTREEITEM htItem;
    TVITEM tvi;
    POINT point;
        
    GetCursorPos(&point);
    ScreenToClient(m_hWnd, &point);
    thti.pt = point;
    thti.flags = TVHT_TORIGHT;
    htItem = TreeView_HitTest(m_hWnd, &thti);

    if (htItem == NULL)
        return 0;

    TCHAR szText[20];
    memset(&tvi, 0, sizeof(tvi));
    tvi.mask = TVIF_TEXT | TVIF_PARAM;
    tvi.hItem = htItem;
    tvi.pszText = szText;
    tvi.cchTextMax = sizeof(szText);
    TreeView_GetItem(m_hWnd, &tvi);

    PRINT(_T("\r\n<INFO>CTree::OnLClkTree= %s"), szText);

    return 1;
}
int CTree::OnRClkTree(LPNMHDR lphr)
{
    TVHITTESTINFO thti;
    HTREEITEM htItem;
    TVITEM tvi;
    POINT point;

    GetCursorPos(&point);
    ScreenToClient(m_hWnd, &point);
    thti.pt = point;
    thti.flags = TVHT_TORIGHT;
    htItem = TreeView_HitTest(m_hWnd, &thti);

    if (htItem == NULL)
        return 0;

    TCHAR szText[20];
    memset(&tvi, 0, sizeof(tvi));
    tvi.mask = TVIF_TEXT | TVIF_PARAM;
    tvi.hItem = htItem;
    tvi.pszText = szText;
    tvi.cchTextMax = sizeof(szText);
    TreeView_GetItem(m_hWnd, &tvi);

    PRINT(_T("\r\n<INFO>CTree::OnRClkTree = %s"), szText);

    GetCursorPos(&point);

    TrackPopupMenu(m_hPopMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, 0, GetParent(m_hWnd), NULL);  //通知是控件的父窗口处理

    return 1;
}
int CTree::OnDBLClkTree(LPNMHDR lphr) { HTREEITEM htItem; TVITEM tvi; htItem = TreeView_GetSelection(m_hWnd); if (htItem == NULL) return 0;
// HWND m_hWndClient = ((CXMDIFrameEx*)m_pMainFrame)->m_hWndClient; TCHAR szText[20]; memset(&tvi, 0, sizeof(tvi)); tvi.mask = TVIF_TEXT | TVIF_PARAM; tvi.hItem = htItem; tvi.pszText = szText; tvi.cchTextMax = sizeof(szText); TreeView_GetItem(m_hWnd, &tvi); PRINT(_T("\r\n<INFO>CTree::OnDBLClkTree= %s"), szText); return 1; } int CTree::ItemLabelEdit() { HTREEITEM htItem; TVITEM tvi; htItem = TreeView_GetSelection(m_hWnd); if (htItem == NULL) return 0; TreeView_EditLabel(m_hWnd, htItem); return 1; } int CTree::InsertChild() { // TODO: Add your command handler code here HTREEITEM hItem = TreeView_GetSelection(m_hWnd); HTREEITEM hItemNew = NULL; if (NULL == hItem) { PRINT(_T("\r\n<FAIL>No selected")); return 0; } else { hItemNew = InsertTreeNode(m_hWnd, hItem, _T("NewChild")); TreeView_Expand(m_hWnd, hItem, TVE_EXPAND); TreeView_SelectItem(m_hWnd, hItemNew); TreeView_EditLabel(m_hWnd, hItemNew); //插入后就编辑 return 1; } } int CTree::DeleteItem() { HTREEITEM hItem = TreeView_GetSelection(m_hWnd); HTREEITEM hItemNew = NULL; if (NULL == hItem) { PRINT(_T("\r\n<FAIL>No selected")); return 0; } else { TreeView_DeleteItem(m_hWnd, hItem); return 1; } } int CTree::InsertSibling() { HTREEITEM hItem = TreeView_GetSelection(m_hWnd); HTREEITEM hItemNew = NULL; hItem = TreeView_GetParent(m_hWnd, hItem); if (NULL == hItem) { PRINT(_T("\r\n<FAIL>No selected")); return 0; } else { hItemNew = InsertTreeNode(m_hWnd, hItem, _T("NewSibling")); TreeView_Expand(m_hWnd, hItem, TVE_EXPAND); TreeView_SelectItem(m_hWnd, hItemNew); TreeView_EditLabel(m_hWnd, hItemNew); //插入后就编辑 return 1; } } //========工具条和状态条========================== HWND CreateStatusbar(HWND hWnd) { int Rightend[3]; RECT rt; GetClientRect(hWnd, &rt); HWND hWndStatusBar = CreateWindowEx(0, STATUSCLASSNAME, _T(""), WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP, 0, 0, 0, 0, hWnd, (HMENU)IDC_STATUSBAR, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL); if (!hWndStatusBar) return NULL; Rightend[0] = rt.right / 2; Rightend[1] = rt.right * 3 / 4; Rightend[2] = rt.right; //set statusbar to three segment SendMessage(hWndStatusBar, SB_SETPARTS, (WPARAM)3, (LPARAM)Rightend); MoveWindow(hWndStatusBar, 0, 0, 0, 0, TRUE); //TextOut in statusbar SendMessage(hWndStatusBar, SB_SETTEXT, 0, (long)TEXT("Status 0")); SendMessage(hWndStatusBar, SB_SETTEXT, 1, (long)TEXT("========test=====")); SendMessage(hWndStatusBar, SB_SETTEXT, 2, (long)TEXT("Step 2")); return hWndStatusBar; } HWND CreateToolbar(HWND hWnd) { // image,COMMAND, STATUS, STYLE, //用系统标准图标 TBBUTTON tbButton[] = { { STD_FILENEW, IDM_FILE_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FILEOPEN, IDM_FILE_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FILESAVE, IDM_TEST_TEST1, TBSTATE_ENABLED , TBSTYLE_BUTTON , 0L, 0}, { STD_HELP, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, -1}, { STD_FIND, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_UNDO, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_REDOW, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0L, -1}, { STD_COPY, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_PASTE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_CUT, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_DELETE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FILESAVE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_PROPERTIES, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FIND, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_REPLACE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_PRINT, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FILESAVE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_FILESAVE, IDM_TEST_TEST3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0}, { STD_PRINTPRE, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0L, 0} }; HINSTANCE hLib = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); //It can be other DLL,here same as m_hInst HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, _T(""), //WS_VISIBLE|WS_CHILD|TBSTYLE_TOOLTIPS, //| WS_BORDER|TBSTYLE_FLAT, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | CCS_NORESIZE | CCS_ADJUSTABLE | CCS_NODIVIDER | CCS_NOPARENTALIGN, //for rebar 0, 0, 0, 0, hWnd, (HMENU)IDC_TOOLSBAR, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL); if (!hWndToolbar) return NULL; SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); SendMessage(hWndToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)(DWORD)(TBSTYLE_EX_DRAWDDARROWS)); TBADDBITMAP tbBitmap1; tbBitmap1.hInst = HINST_COMMCTRL; tbBitmap1.nID = IDB_STD_SMALL_COLOR; SendMessage(hWndToolbar, TB_ADDBITMAP, 0, (LPARAM)&tbBitmap1); SendMessage(hWndToolbar, TB_SETBITMAPSIZE, 0, (LPARAM)MAKELONG(16, 16)); //size 16x16 SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0); SendMessage(hWndToolbar, TB_ADDBUTTONS, 15, (LONG)&tbButton); //add 6 button return hWndToolbar; } //=====模板生成的WIN32 程序框架 ========================= #define MAX_LOADSTRING 100 // Global Variables: HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text // Foward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg; HACCEL hAccelTable; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TREE, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TREE); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_TREE); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDC_TREE; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // Store instance handle in our global variable hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } InitCommonControls(); m_hToolsbar = CreateToolbar(hWnd); m_hStatusbar = CreateStatusbar(hWnd); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; TCHAR szHello[MAX_LOADSTRING]; LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message) { case WM_CREATE: OnCreate(hWnd, message, wParam, lParam); break; case WM_SIZE: OnSize(hWnd, message, wParam, lParam); break; case WM_PAINT: OnPaint(hWnd, message, wParam, lParam); break; case WM_TIMER: OnTimer(hWnd, message, wParam, lParam); break; case WM_LBUTTONDOWN: OnLButtonDown(hWnd, message, wParam, lParam); break; case WM_LBUTTONUP: OnLButtonUp(hWnd, message, wParam, lParam); break; case WM_MOUSEMOVE: OnMouseMove(hWnd, message, wParam, lParam); break; case WM_NOTIFY: OnNotify(hWnd, message, wParam, lParam); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_FILE_NEW: OnFileNew(hWnd, message, wParam, lParam); break; case IDM_FILE_OPEN: OnFileOpen(hWnd, message, wParam, lParam); break; case IDM_FILE_SAVE: OnFileSave(hWnd, message, wParam, lParam); break; case IDM_TREE_EDIT: OnTreeEdit(hWnd, message, wParam, lParam); break; case IDM_TREE_INSERTCHILD: m_Tree1.InsertChild(); break; case IDM_TREE_INSERTSIBLING: m_Tree1.InsertSibling(); break; case IDM_ABOUT: MessageBox(hWnd, "Test", "Test",MB_OK); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } int OnCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { m_hWndEdit = CreateWindow(_T("edit"), NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)IDC_EDIT, hInst, NULL); SetTimer(hWnd, 1, 10000, NULL); //10s m_Tree1.CreateTreeView(hWnd); m_Tree1.InitTreetView(); return 1; } //留一个状态条的位置 int OnSize(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int cxClient, cyClient; cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); MoveWindow(m_hToolsbar, 0, 0, cyClient, 25, TRUE); MoveWindow(m_hStatusbar, 0, 0, 0, 0, TRUE); MoveWindow(m_hWndEdit, 0, cyClient - 220, cxClient, 200, TRUE); MoveWindow(m_Tree1.m_hWnd, 0, 25, 220, cyClient - 200 - 50, TRUE); return DefWindowProc(hWnd, message, wParam, lParam); } int OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; RECT rt; hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt); //DrawText(hdc, StatusText, strlen(StatusText), &rt, DT_LEFT|DT_BOTTOM|DT_SINGLELINE); EndPaint(hWnd, &ps); return 1; } int OnTimer(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnFileNew(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PRINT("\r\nOnFileNew"); return 1; } int OnFileOpen(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnFileSave(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnTreeEdit(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PRINT("\r\nOnTreeEdit"); m_Tree1.ItemLabelEdit(); return 1; } int OnLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return 1; } int OnLButtonUp(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { m_Tree1.OnLButtonUp(hWnd, message, wParam, lParam); return 1; } int OnMouseMove(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { m_Tree1.OnMouseMove(hWnd, message, wParam, lParam); return 1; } int OnNotify(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int idCtrl = (int)wParam; //控件的 ID LPNMHDR lphr = (LPNMHDR)lParam; //先看是哪个控件,再决定结构 //m_hWndTree = GetDlgItem(hWnd, IDM_VIEW_TREEVIEW1); if (lphr->hwndFrom == m_Tree1.m_hWnd) { NM_TREEVIEW* pTree = (NM_TREEVIEW*)lParam; //是树控件,所以用树控件通知结构 switch (pTree->hdr.code) { case NM_CLICK: m_Tree1.OnLClkTree(lphr); break; case NM_RCLICK: m_Tree1.OnRClkTree(lphr); break; case NM_DBLCLK: m_Tree1.OnDBLClkTree(lphr); break; case TVN_BEGINDRAG: return m_Tree1.OnTvnBegindrag(lphr); case TVN_BEGINLABELEDIT: return m_Tree1.OnTvnBeginlabeledit(lphr); //是否允许编辑,是通知的返回值 case TVN_ENDLABELEDIT: return m_Tree1.OnTvnEndlabeledit(lphr); break; default: //PRINT("\r\nm_hWndTree=%d %d", m_hWndTree, pTree->hdr.code); break; } } return 0; //返回消息是给控件处理 }