MFC CTreeCtrl 树节点是在插入时确定位置,用hParent, hInsertAfter 这两个HTREEITEM 可以唯一确定节点位置。
这样增,减,移动节点后,只有两个数据变化,涉及的其他节点数据的变化也很少,比较适合存储。随机存储的节点只要有两个相关节点数据就能还原出树。
实际还原很麻烦,因为存储的是两个节点的id,相当于索引,但是还原需要的是两个插入时才生成的句柄,一边要遍历树,另一边要查表,还得按顺序,脑袋嗡嗡的,不搞了。
简单起见,用一个层数据,一个遍历时的顺序数,这两个数据,配合一个节点的标识id,来存储和还原树结构。
id 是唯一标识节点,在数据库中是自动增长的唯一非空整数。每插入一个节点,数据库自动产生唯一的增加的id。这个值始终不变,除非节点删掉。
lev,idno 一个是层,一个是遍历序号,在保存时计算出来,并存入数据库,还原时用这两个数据来还原树结构,树节点中要保存id,用来对应数据库中相关业务数据。保存是遍历树,update 节点数据。
这样插入节点,移动节点不用改变其他节点的位置数据,因为id不变,唯一确定树节点,树的结构在保存时用遍历重新计算。
MFC 默认的MDI框架,主要的存取,删除,插入程序段在File View中,用POP菜单做测试。
FileView.h
#pragma once #include "ViewTree.h" #include "re2.h" #include "CSQLite.h" class CFileViewToolBar : public CMFCToolBar { virtual void OnUpdateCmdUI(CFrameWnd* /*pTarget*/, BOOL bDisableIfNoHndler) { CMFCToolBar::OnUpdateCmdUI((CFrameWnd*) GetOwner(), bDisableIfNoHndler); } virtual BOOL AllowShowOnList() const { return FALSE; } }; class CFileView : public CDockablePane { // Construction public: CFileView(); void AdjustLayout(); void OnChangeVisualStyle(); CSQLite m_SQLite; HTREEITEM m_hNodeSrc; HTREEITEM m_hNodeFile; FileTable* m_pFileTable; //返回值是中间过程 void FindNode(HTREEITEM hItem, int id, HTREEITEM& hPItem); //XGZ 这2个变量在递归中全程累计 int m_NodeNo; int m_NodeLev; void SaveNode(HTREEITEM hItem); void UpdateNode(HTREEITEM hItem); void ShowAllNodes(HTREEITEM hItem); void InsertNode(HTREEITEM hItem); void DeleteNode(HTREEITEM hItem); void DeleteAllNodes(HTREEITEM hItem); CString m_strSave; HTREEITEM AddFileName(LPCTSTR strFileName, DWORD_PTR dwData); BOOL DelFileName(HTREEITEM& hFileNode); HTREEITEM AddNote(LPCTSTR Name, DWORD_PTR dwData); // Attributes protected: CViewTree m_wndFileView; CImageList m_FileViewImages; CFileViewToolBar m_wndToolBar; protected: void FillFileView(); // Implementation public: virtual ~CFileView(); protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); afx_msg void OnProperties(); afx_msg void OnFileOpen(); afx_msg void OnFileOpenWith(); afx_msg void OnDummyCompile(); afx_msg void OnEditCut(); afx_msg void OnEditCopy(); afx_msg void OnEditClear(); afx_msg void OnPaint(); afx_msg void OnSetFocus(CWnd* pOldWnd); DECLARE_MESSAGE_MAP() public: afx_msg void OnPopExpTest(); afx_msg void OnPopExpTest2(); afx_msg void OnPopExpInsertchild(); afx_msg void OnPopExpInsertsibling(); afx_msg void OnPopExpDelete(); afx_msg void OnPopExpSave(); afx_msg void OnPopExpUpdate(); afx_msg void OnPopExpShowall(); };
FileView.cpp
#include "stdafx.h" #include "mainfrm.h" #include "FileView.h" #include "Resource.h" #include "RE2.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ///////////////////////////////////////////////////////////////////////////// // CFileView CFileView::CFileView() { m_pFileTable = NULL; } CFileView::~CFileView() { if (NULL != m_pFileTable) { delete[] m_pFileTable; m_pFileTable = NULL; } } BEGIN_MESSAGE_MAP(CFileView, CDockablePane) ON_WM_CREATE() ON_WM_SIZE() ON_WM_CONTEXTMENU() ON_COMMAND(ID_PROPERTIES, OnProperties) ON_COMMAND(ID_OPEN, OnFileOpen) ON_COMMAND(ID_OPEN_WITH, OnFileOpenWith) ON_COMMAND(ID_DUMMY_COMPILE, OnDummyCompile) ON_COMMAND(ID_EDIT_CUT, OnEditCut) ON_COMMAND(ID_EDIT_COPY, OnEditCopy) ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) ON_WM_PAINT() ON_WM_SETFOCUS() ON_COMMAND(ID_POP_EXP_TEST, &CFileView::OnPopExpTest) ON_COMMAND(ID_POP_EXP_TEST2, &CFileView::OnPopExpTest2) ON_COMMAND(ID_POP_EXP_INSERTCHILD, &CFileView::OnPopExpInsertchild) ON_COMMAND(ID_POP_EXP_INSERTSIBLING, &CFileView::OnPopExpInsertsibling) ON_COMMAND(ID_POP_EXP_DELETE, &CFileView::OnPopExpDelete) ON_COMMAND(ID_POP_EXP_SAVE, &CFileView::OnPopExpSave) ON_COMMAND(ID_POP_EXP_UPDATE, &CFileView::OnPopExpUpdate) ON_COMMAND(ID_POP_EXP_SHOWALL, &CFileView::OnPopExpShowall) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CWorkspaceBar message handlers int CFileView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDockablePane::OnCreate(lpCreateStruct) == -1) return -1; CRect rectDummy; rectDummy.SetRectEmpty(); // Create view: const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS; if (!m_wndFileView.Create(dwViewStyle, rectDummy, this, 4)) { TRACE0("Failed to create file view\n"); return -1; // fail to create } // Load view images: m_FileViewImages.Create(IDB_FILE_VIEW, 16, 0, RGB(255, 0, 255)); m_wndFileView.SetImageList(&m_FileViewImages, TVSIL_NORMAL); m_wndToolBar.Create(this, AFX_DEFAULT_TOOLBAR_STYLE, IDR_EXPLORER); m_wndToolBar.LoadToolBar(IDR_EXPLORER, 0, 0, TRUE /* Is locked */); OnChangeVisualStyle(); m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() | CBRS_TOOLTIPS | CBRS_FLYBY); m_wndToolBar.SetPaneStyle(m_wndToolBar.GetPaneStyle() & ~(CBRS_GRIPPER | CBRS_SIZE_DYNAMIC | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT)); m_wndToolBar.SetOwner(this); // All commands will be routed via this control , not via the parent frame: m_wndToolBar.SetRouteCommandsViaFrame(FALSE); // Fill in some static tree view data (dummy code, nothing magic here) FillFileView(); AdjustLayout(); m_wndFileView.ModifyStyle(0, TVS_HASLINES | TVS_HASBUTTONS | TVS_EDITLABELS); return 0; } void CFileView::OnSize(UINT nType, int cx, int cy) { CDockablePane::OnSize(nType, cx, cy); AdjustLayout(); } void CFileView::FillFileView() { HTREEITEM hRoot = m_wndFileView.InsertItem(_T("FakeApp files"), 0, 0); m_wndFileView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD); HTREEITEM hSrc = m_wndFileView.InsertItem(_T("FakeApp Source Files"), 0, 0, hRoot); m_wndFileView.InsertItem(_T("FakeApp.cpp"), 1, 1, hSrc); HTREEITEM hSrc1 = m_wndFileView.InsertItem(_T("FakeApp.rc"), 1, 1, hSrc); m_wndFileView.InsertItem(_T("FakeAppDoc.cpp"), 1, 1, hSrc); m_wndFileView.InsertItem(_T("FakeAppView.cpp"), 1, 1, hSrc); m_wndFileView.InsertItem(_T("MainFrm.cpp"), 1, 1, hSrc); m_wndFileView.InsertItem(_T("StdAfx.cpp"), 1, 1, hSrc, hSrc1); //插在hSrc1后面 HTREEITEM hInc = m_wndFileView.InsertItem(_T("FakeApp Header Files"), 0, 0, hRoot); m_wndFileView.InsertItem(_T("FakeApp.h"), 2, 2, hInc); m_wndFileView.InsertItem(_T("FakeAppDoc.h"), 2, 2, hInc); m_wndFileView.InsertItem(_T("FakeAppView.h"), 2, 2, hInc); m_wndFileView.InsertItem(_T("Resource.h"), 2, 2, hInc); m_wndFileView.InsertItem(_T("MainFrm.h"), 2, 2, hInc); m_wndFileView.InsertItem(_T("StdAfx.h"), 2, 2, hInc); HTREEITEM hRes = m_wndFileView.InsertItem(_T("FakeApp Resource Files"), 0, 0, hRoot); m_wndFileView.InsertItem(_T("FakeApp.ico"), 2, 2, hRes); m_wndFileView.InsertItem(_T("FakeApp.rc2"), 2, 2, hRes); m_wndFileView.InsertItem(_T("FakeAppDoc.ico"), 2, 2, hRes); m_wndFileView.InsertItem(_T("FakeToolbar.bmp"), 2, 2, hRes); m_wndFileView.Expand(hRoot, TVE_EXPAND); m_wndFileView.Expand(hSrc, TVE_EXPAND); m_wndFileView.Expand(hInc, TVE_EXPAND); m_hNodeSrc = hRoot; } void CFileView::OnContextMenu(CWnd* pWnd, CPoint point) { CTreeCtrl* pWndTree = (CTreeCtrl*) &m_wndFileView; ASSERT_VALID(pWndTree); if (pWnd != pWndTree) { CDockablePane::OnContextMenu(pWnd, point); return; } if (point != CPoint(-1, -1)) { // Select clicked item: CPoint ptTree = point; pWndTree->ScreenToClient(&ptTree); UINT flags = 0; HTREEITEM hTreeItem = pWndTree->HitTest(ptTree, &flags); if (hTreeItem != NULL) { pWndTree->SelectItem(hTreeItem); } } pWndTree->SetFocus(); theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EXPLORER, point.x, point.y, this, TRUE); } void CFileView::AdjustLayout() { if (GetSafeHwnd() == NULL) { return; } CRect rectClient; GetClientRect(rectClient); int cyTlb = m_wndToolBar.CalcFixedLayout(FALSE, TRUE).cy; m_wndToolBar.SetWindowPos(NULL, rectClient.left, rectClient.top, rectClient.Width(), cyTlb, SWP_NOACTIVATE | SWP_NOZORDER); m_wndFileView.SetWindowPos(NULL, rectClient.left + 1, rectClient.top + cyTlb + 1, rectClient.Width() - 2, rectClient.Height() - cyTlb - 2, SWP_NOACTIVATE | SWP_NOZORDER); } void CFileView::OnProperties() { AfxMessageBox(_T("Properties....")); } //1.树是遍历存储的,实际时update了一个序号idno,读出是按存储的顺序(idno) //2.创建树是通过层(lev)的变化数量来寻找父节点,sibling顺序遍历存储时就确定了。 // lev不变时是连续插入sibling节点。父节点不变 // lev增加(只可能增加1)是在当前节点下插入子节点,父节点就是变成当前节点。 // lev减小(按差值循环上溯父节点),在找到的父节点下插入子节点,父节点更新。 // void CFileView::OnFileOpen() { CString fileName; CString sql; int nrow, ncolum; int i, j; HTREEITEM hItem; HTREEITEM hPItem; HTREEITEM hRoot; CFileDialog dlgFile(TRUE, "DataBase File(*.db)|*.DB", "Test.db", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "DataBase File(*.db)|*.db|RTF Files (*.rtf)|*.rtf|All Files (*.*)|*.*||", NULL); if (IDOK == dlgFile.DoModal()) { fileName = dlgFile.GetFileName(); if (!m_SQLite.Open(fileName.GetBuffer())) { PRINT("OPEN FAILED"); return; } PRINT("OPEN %s OK", fileName.GetBuffer()); m_wndFileView.DeleteAllItems(); sql = "select id,lev,idno,name from notesid order by idno";//lev asc, levno asc; "; m_SQLite.Query(sql.GetBuffer(), nrow, ncolum); PRINT("OPEN OK nrow=%d,ncloum=%d", nrow, ncolum); if (NULL != m_pFileTable) { delete[] m_pFileTable; m_pFileTable = NULL; } m_pFileTable = new FileTable[nrow]; for (i = 1; i < nrow + 1; i++) //第一行是字段名称 { //wSQLTable.Cell[i] = new SQLCell[ncolum]; m_pFileTable[i - 1].id = atoi(m_SQLite.m_sresult[i * ncolum + 0]); m_pFileTable[i - 1].lev = atoi(m_SQLite.m_sresult[i * ncolum + 1]); m_pFileTable[i - 1].idno = atoi(m_SQLite.m_sresult[i * ncolum + 2]); memset(m_pFileTable[i - 1].name, 0, 256); memcpy(m_pFileTable[i - 1].name, m_SQLite.m_sresult[i * ncolum + 3], 255); PRINT(m_pFileTable[i - 1].name); } int levNow = 0; HTREEITEM hParent = NULL; HTREEITEM hCurrent = NULL; for (i = 0; i < nrow; i++) { if(m_pFileTable[i].lev == levNow) { hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent); m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i])); m_pFileTable[i].hItem = hCurrent; if(i == 0) hRoot = hCurrent; //第一个节点肯定时根节点 } if(m_pFileTable[i].lev > levNow) //子节点优先 { hParent = hCurrent; //上个节点 hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent); m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i])); m_pFileTable[i].hItem = hCurrent; levNow = m_pFileTable[i].lev; } if(m_pFileTable[i].lev < levNow) { int gap = levNow - m_pFileTable[i].lev; for (int j = 0; j < gap; j++) { hParent = m_wndFileView.GetParentItem(hParent); //上个节点 } hCurrent = m_wndFileView.InsertItem(m_pFileTable[i].name, 0, 0, hParent); m_wndFileView.SetItemData(hCurrent, (DWORD_PTR) & (m_pFileTable[i])); m_pFileTable[i].hItem = hCurrent; levNow = m_pFileTable[i].lev; } } m_wndFileView.Expand(hRoot, TVE_EXPAND); } } void CFileView::FindNode(HTREEITEM hItem, int id, HTREEITEM& hFindItem) { if (!hItem) return ; FileTable* pFileTable; pFileTable = (FileTable*)m_wndFileView.GetItemData(hItem); if (pFileTable == NULL) { PRINT(_T("<ERR> data fail! item = %s"), m_wndFileView.GetItemText(hItem)); return; } if (pFileTable->id == id) { PRINT( _T("Find id=%d,Name=%s"), pFileTable->id, m_wndFileView.GetItemText(hItem).GetBuffer()); hFindItem = hItem; return ; } HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); FindNode(hChild, id, hFindItem); HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem); FindNode(hSibling, id, hFindItem); return ; } void CFileView::OnFileOpenWith() { // TODO: Add your command handler code here HTREEITEM htItem = m_wndFileView.GetSelectedItem(); FileTable* pFileTable; pFileTable = (FileTable*)m_wndFileView.GetItemData(htItem); PRINT("id:%d-pid:%d-sid:%d-lev:%d-lno:%d", pFileTable->id, pFileTable->pid, pFileTable->sid, pFileTable->lev, pFileTable->levno); } void CFileView::OnDummyCompile() { // TODO: Add your command handler code here } void CFileView::OnEditCut() { // TODO: Add your command handler code here } void CFileView::OnEditCopy() { // TODO: Add your command handler code here } void CFileView::OnEditClear() { m_wndFileView.DeleteAllItems(); } void CFileView::OnPaint() { CPaintDC dc(this); // device context for painting CRect rectTree; m_wndFileView.GetWindowRect(rectTree); ScreenToClient(rectTree); rectTree.InflateRect(1, 1); dc.Draw3dRect(rectTree, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DSHADOW)); } void CFileView::OnSetFocus(CWnd* pOldWnd) { CDockablePane::OnSetFocus(pOldWnd); m_wndFileView.SetFocus(); } void CFileView::OnChangeVisualStyle() { m_wndToolBar.CleanUpLockedImages(); m_wndToolBar.LoadBitmap(theApp.m_bHiColorIcons ? IDB_EXPLORER_24 : IDR_EXPLORER, 0, 0, TRUE /* Locked */); m_FileViewImages.DeleteImageList(); UINT uiBmpId = theApp.m_bHiColorIcons ? IDB_FILE_VIEW_24 : IDB_FILE_VIEW; CBitmap bmp; if (!bmp.LoadBitmap(uiBmpId)) { TRACE(_T("Can't load bitmap: %x\n"), uiBmpId); ASSERT(FALSE); return; } BITMAP bmpObj; bmp.GetBitmap(&bmpObj); UINT nFlags = ILC_MASK; nFlags |= (theApp.m_bHiColorIcons) ? ILC_COLOR24 : ILC_COLOR4; m_FileViewImages.Create(16, bmpObj.bmHeight, nFlags, 0, 0); m_FileViewImages.Add(&bmp, RGB(255, 0, 255)); m_wndFileView.SetImageList(&m_FileViewImages, TVSIL_NORMAL); } HTREEITEM CFileView::AddFileName(LPCTSTR strFileName, DWORD_PTR dwData) { HTREEITEM hFileNode; hFileNode = m_wndFileView.InsertItem(strFileName, 0, 0, m_hNodeSrc); m_wndFileView.SetItemData(hFileNode, dwData); m_hNodeFile = hFileNode; return hFileNode; } HTREEITEM CFileView::AddNote(LPCTSTR Name, DWORD_PTR dwData) { HTREEITEM hNode; hNode = m_wndFileView.InsertItem(Name, 2, 2, m_hNodeFile); m_wndFileView.SetItemData(hNode, dwData); return hNode; } BOOL CFileView::DelFileName(HTREEITEM& hFileNode) { return m_wndFileView.DeleteItem(hFileNode); } void CFileView::OnPopExpTest() { // TODO: Add your command handler code here m_wndFileView.Test(); } void CFileView::OnPopExpTest2() { // TODO: Add your command handler code here m_wndFileView.Test2(); } //子节点的插入 void CFileView::OnPopExpInsertchild() { // TODO: Add your command handler code here HTREEITEM hItem = m_wndFileView.GetSelectedItem(); HTREEITEM hItemNew = NULL; if (NULL == hItem) { PRINT("No selected"); hItemNew = m_wndFileView.InsertItem("NewChild", 0, 0, TVI_ROOT, TVI_FIRST); } else { PRINT("Select = %s", m_wndFileView.GetItemText(hItem).GetBuffer()); hItemNew = m_wndFileView.InsertItem("NewChild", 0, 0, hItem, TVI_FIRST); m_wndFileView.Expand(hItem, TVE_EXPAND); } InsertNode(hItemNew); } //sibling节点插入时先找到父节点,在父节点下插入子节点。 void CFileView::OnPopExpInsertsibling() { // TODO: Add your command handler code here HTREEITEM hItem = m_wndFileView.GetSelectedItem(); HTREEITEM hItemNew = NULL; HTREEITEM hPItem = NULL; if (NULL == hItem) { hItemNew = m_wndFileView.InsertItem("NewSibling", 0, 0, TVI_ROOT, TVI_FIRST); } else { hPItem = m_wndFileView.GetParentItem(hItem); hItemNew = m_wndFileView.InsertItem("NewSibling", 0, 0, hPItem, hItem); m_wndFileView.Expand(hPItem, TVE_EXPAND); } InsertNode(hItemNew); } //插入节点,直接在数据库中插入一个节点的name, //id的类型是唯一的自动增长型,由数据自动生成。 //因为是自动增长的,所以查找最大id,读出来放在节点id数据中,用来更新 //id是唯一标识这个节点的。不能用name,name是可以相同的。 void CFileView::InsertNode(HTREEITEM hItem) { if (!hItem) return; CString sql; int nrow, ncolum; sql.Format(_T("INSERT INTO notesid(name) values('%s');"), m_wndFileView.GetItemText(hItem).GetBuffer()); if (!m_SQLite.OnSqlExec(sql.GetBuffer())) { PRINT("OnSqlExec FAILED"); return; } sql.Format(_T("SELECT max(id) FROM notesid;")); if (!m_SQLite.Query(sql.GetBuffer(), nrow, ncolum)) { PRINT("Query FAILED"); return; } FileTable* pData = new FileTable; int i, j; for (i = 1; i < nrow + 1; i++) //第一行是字段名称 { pData->id = atoi(m_SQLite.m_sresult[i * ncolum + 0]); } m_wndFileView.SetItemData(hItem, (DWORD_PTR)pData); pData->hItem = hItem; return; } //先删除子节点再删除当前节点,避免删掉当前节点的sibling节点 void CFileView::OnPopExpDelete() { // TODO: Add your command handler code here PRINT("OnPopExpDelete"); HTREEITEM hItem = m_wndFileView.GetSelectedItem(); HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); DeleteAllNodes(hChild); DeleteNode(hItem); } //通过id删除数据库中的节点 void CFileView::DeleteNode(HTREEITEM hItem) { if (!hItem) return; FileTable* pData = (FileTable * )m_wndFileView.GetItemData(hItem); CString sql; sql.Format(_T("DELETE FROM notesid WHERE id = %d;"), pData->id); if (!m_SQLite.OnSqlExec(sql.GetBuffer())) { PRINT("OnSqlExec FAILED"); return; } m_wndFileView.DeleteItem(hItem); return; } //遍历时不能在遍历sibling节点前就删除当前节点了。 void CFileView::DeleteAllNodes(HTREEITEM hItem) { if (!hItem) return; HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); DeleteAllNodes(hChild); //DeleteNode(hChild); HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem); DeleteAllNodes(hSibling); DeleteNode(hItem); PRINT(m_wndFileView.GetItemText(hItem).GetBuffer()); return; } //保存前先备份和删除旧表,然后新建新表并批量插入 void CFileView::OnPopExpSave() { if (NULL == m_SQLite.m_db) { PRINT("Fail no datebase"); return; } CString sql; sql = "PRAGMA foreign_keys = 0;"; sql += "DROP TABLE notesidbak;"; sql += "CREATE TABLE notesidbak AS SELECT * FROM notesid;"; sql += "DROP TABLE notesid;"; sql += "CREATE TABLE notesid (id INTEGER,lev INTEGER,levno INTEGER,name CHAR(256));"; m_strSave = ""; HTREEITEM hItem = m_wndFileView.GetRootItem(); m_NodeNo = 0; m_NodeLev = 0; SaveNode(hItem); //遍历创建插入语句 sql += m_strSave; sql += "PRAGMA foreign_keys = 1;"; if (!m_SQLite.OnSqlExec(sql.GetBuffer())) { PRINT("OnSqlExec FAILED"); } PRINT(m_strSave.GetBuffer()); } //然后遍历插入节点,id是唯一不变的,其他都是读取树的结构数据 void CFileView::SaveNode(HTREEITEM hItem) { if (!hItem) return; m_NodeNo++; CString str; FileTable* pdata = (FileTable*)m_wndFileView.GetItemData(hItem); str.Format(_T("INSERT INTO notesid(id,lev,idno,name) values(%d,%d,%d,'%s');"), pdata->id, m_NodeLev, m_NodeNo, m_wndFileView.GetItemText(hItem).GetBuffer()); m_strSave += str; HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); if (hChild) { m_NodeLev++; if (m_NodeLev > 255) { PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev); return; } } SaveNode(hChild); HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem); if (!hSibling) m_NodeLev--; SaveNode(hSibling); return; } //更新只是批量更新旧表,不删除 void CFileView::OnPopExpUpdate() { CString sql; if (NULL == m_SQLite.m_db) { PRINT("Fail no datebase"); return; } sql = ""; m_strSave = ""; HTREEITEM hItem = m_wndFileView.GetRootItem(); m_NodeNo = 0; m_NodeLev = 0; UpdateNode(hItem); sql += m_strSave; if (!m_SQLite.OnSqlExec(sql.GetBuffer())) { PRINT("OnSqlExec FAILED"); } PRINT(m_strSave.GetBuffer()); } // //更新数据库时,m_NodeNo是节点的遍历顺序,也就是idno字段。 //确定了sibling的顺序,可按顺序还原。确保父节点存在。 // void CFileView::UpdateNode(HTREEITEM hItem) { if (!hItem) return; m_NodeNo++; CString str; FileTable* pdata = (FileTable*)m_wndFileView.GetItemData(hItem); str.Format(_T("update notesid set lev=%d,idno=%d, name='%s' where id=%d;"), m_NodeLev, m_NodeNo, m_wndFileView.GetItemText(hItem).GetBuffer(), pdata->id); m_strSave += str; HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); if (hChild) { m_NodeLev++; if (m_NodeLev > 255) { PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev); return; } } UpdateNode(hChild); HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem); if (!hSibling) m_NodeLev--; UpdateNode(hSibling); return; } void CFileView::ShowAllNodes(HTREEITEM hItem) { if (!hItem) return; m_NodeNo++; PRINT(_T("%d:%d-%s "), m_NodeNo, m_NodeLev, m_wndFileView.GetItemText(hItem).GetBuffer()); HTREEITEM hChild = m_wndFileView.GetChildItem(hItem); if (hChild) { m_NodeLev++; if (m_NodeLev > 255) { PRINT(_T("<ERR> m_NodeLev>255(%d)!"), m_NodeLev); return; } } ShowAllNodes(hChild); HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem); if (hSibling) m_NodeLev--; ShowAllNodes(hSibling); return; } void CFileView::OnPopExpShowall() { m_NodeNo = 0; m_NodeLev = 0; HTREEITEM hItem = m_wndFileView.GetRootItem(); ShowAllNodes(hItem); }