树状控件与列表控件交互以及XML的解析





自查笔记。
  
   

方法记录

MSXML相关

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MSXML2::IXMLDOMDocumentPtr pDoc;

HRESULT hr = pDoc.CreateInstance(_uuidof(MSXML2::DOMDocument60));
if(!pDoc)
{
ASSERT(FALSE);
return;
}
_variant_t varOut((bool)TRUE);
varOut = pDoc->load(xmlPath);
if ((bool)varOut == FALSE)
{
::MessageBox(NULL, "Wrong!", "提示", MB_ICONERROR);
}

获取根节点

1
2
MSXML2::IXMLDOMElementPtr pNode;
pNode = pDoc->GetdocumentElement();

获取属性(法一)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MSXML2::IXMLDOMNamedNodeMapPtr pAttrMap;
MSXML2::IXMLDOMNodePtr pAttrItem;
MSXML2::IXMLDOMNodeListPtr pNodeList = pNode->GetchildNodes();
long Len = pNodeList->Getlength();
for (int i = 0; i < Len; ++i)
{
MSXML2::IXMLDOMElementPtr pChildNode = (MSXML2::IXMLDOMElementPtr)pNodeList->Getitem(i);
pChildNode->get_attributes(&pAttrMap);
long count;
pAttrMap->get_length(&count);
if (count)
{
pAttrMap->get_item(0, &pAttrItem);
_variant_t variantItemName;
pAttrItem->get_nodeTypedValue(&variantItemName);
}
}

获取属性(法二)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MSXML2::IXMLDOMNodeListPtr pList = pRoot->GetchildNodes();
int iCount = pList->Getlength();
for (int i = 0; i < iCount; ++i)
{
MSXML2::IXMLDOMElementPtr pItem = (MSXML2::IXMLDOMElementPtr)pList->item[i];
MSXML2::IXMLDOMNamedNodeMapPtr pAttrMap = pItem->Getattributes();
MSXML2::IXMLDOMNodePtr pAttrItem;
for (int att = 0; att < pAttrMap->length; ++att)
{
pAttrItem = pAttrMap->Getitem(att);
strTemp = (LPCTSTR)pAttrItem->GetnodeName();
_variant_t value;
value = pAttrItem->GetnodeValue();
if (strTemp == "某属性名1")
{
//需要进行的操作
}
if (strTemp == "某属性名2")
{
//需要进行的操作
}
}
}

删除节点

连同子节点一起删 pChildNode->GetParentNode()->removeChild(pChildNode);

树控件相关

插入

HTREEITEM ht = m_tree.InsertItem((_bstr_t)variantValue, 1, 1, ht, TVI_LAST);
依次是:插入的数据,树前图标点击前,树前图标点击后,父节点,插在最后。

存入数据

m_tree.SetItemData(ht,(DWORD_PTR)数据);

获得根节点

HTREEITEM hRoot=m_tree.GetRootItem();

展开树

m_tree.Expand(ht,TVE_EXPAND);

获取选中树

HTREEITEM ChooseItem = m_tree.GetSelectedItem();
获取数据
数据类型animal pStructure = (数据类型animal)m_tree.GetitemData(ChooseItem);

列表控件相关

插入

m_list.InsertItem(i,(LPCTSTR)数据,i);

存入数据

m_list.SetItemData(i,(DWORD_PTR)数据);

删除全部项

m_list.DeleteAllItems();

在列表最后插入

int iRow = m_list.GetItemCount();
m_list.InsertItem(iRow, text, 0);
text 插入的文本 0表示imagelist中的第一个图片
imagelist是由一幅幅image拼起来的长图列表

其它C++相关

MessageBox

MessageBox分为win API 和 MFC封装两种。
win API:
::MessageBox(1, 2, 3, 4);
1为父窗口句柄,常用NULL
2为显示的字符串
3为标题栏显示的字符串
4为图标icon 有四种:MB_ICONERROR,MB_ICONINFORMATION,MB_ICONWARNING,MB_ICONQUESTION

父对话框给子对话框传值

如果子对话框是模态对话框,那么子对话框是无法在弹出后调用父对话框的控件变量的。只能在DoModal之前,通过一个子对话框的自定义成员变量传递过去。
eg.
CxxDlg dlg;
UpdateData(TRUE);
dlg.m_str=m_edit;
dlg.DoModal();

一种不良的方式:
父对话框 BookDlg 子对话框TitleDlg
在父对话框中 TitleDlg dlg(this);
在子对话框中 CBookDlg parent = (CBookDlg)GetParent();
然后就可以使用了:
CString text=pParent->m_sentence;
m_sentence是父对话框类的成员变量

父窗口引用子窗口变量
在父窗口中 xxxDlg dlg;
if(dlg.DoModal() == IDOK)
{
//操作
}

在子对话框显示图片

给自对话框添加OnInitDlg()
添加OnPaint()
(记得加WM_ONPAINT消息)
在OnInitDlg中加载图片,在OnPanint()中重绘图片。(在初始化里重绘是不行的)

vector

添加头文件#include
vector容器,std::vector<类型*> name; push_back添加元素。
遍历元素使用迭代器iterator,for循环
vector<数据类型>::iterator it;
for(it=数据.begin();it!=数据.end();it++)
{
//操作
Break;
}

map

添加头文件#include
std::map>
一个CString对应一个vector。查询时也是先遍历(迭代器)再比较,it->first指代返回关键字,it->second指代数据。

父子循环

父子循环遍历时避免使用相同的变量名。

几个函数

检查路径合法性 PathFileExists(path),使用它需要加上头文件#include “shlwapi.h”
字符连接 CString str;
str.Format((“%s%s\%s\“),path,”第三层”,”第四层”));
复制文件CopyFile(原完整带后缀路径,复制后的带后缀完整目的路径,TRUE); 该函数有返回值
比较

1
2
3
4
if(strTemp.Compare("相比较的字符串")==0)
{
//操作
}

创建临时文件夹

1
2
3
4
5
6
7
8
9
10
11
12
TCHAR Temp[512];
GetTempPath(512, Temp);
CString strTempPath;
strTempPath.Format("%s\\Mydata", Temp);
if (!PathFileExists(strTempPath))
{
if (!CreateDirectory(strTempPath, NULL))
{
ASSERT(FALSE);
return;
}
}

复制文件夹及子文件

写一个函数CopyDiretory(CString source, CString target)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
CFileFind finder;
bool bWorking = finder.FindFile(source);
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDirectory() && !finder.IsDots())
{
copyDiretory(finder.GetFilePath(), target + "\\" + finder.GetFileName();
}
else
{
Bool temp = CopyFile(finder.GetFilePath(), target + "\\" + finder.GetFileName(), FALSE);
if (temp == 0)
{
MessageBox("复制失败!");
return;
}
}
}

}

删除文件目录及子文件夹

写一个DeleteDirectory函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CFileFind finder;
CString path;
path.Format("%s\\*.*", directorypath);
BOOL bWorking = finder.FindFile(path);
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDirectory() && !finder.IsDots())
{
DeleteDirectory(finder.GetFilePath());
RemoveDirectory(finder.GetFilePath();)
}
else
{
DeleteFile(finder.GetFilePath());
}
finder.Close();
RemoveDirectory(directoryPath);
return 0;
}

文件遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CFileFind finder;
CString strName;
BOOL bWorking = finder.FindFile(path);
std::vector<CString>a;
while (bWorking)
{
bWorking = finder.FindNextFile();
strName = finder.GetFileName();
a.push_back(strName);//获取全部文件名
vector<CString>::iterator it;
for (it = a.begin(); it != a.end(); it++)
{
if ((*it) == strFileName)
{
//需要的处理代码
break;
}
}

}

截取字符串

1
2
3
4
5
6
7
8
9
10
char* temp = new char[260];
::GetCurrentDirectory(260, temp);//得到的是DWord型
CString strTempDir = temp;
char* str = strTempDir.GetBuffer(strTempDir.GetLength() + 1);
char* delim = "\\";
char* buf;
char* p1 = strtok_s(str, delim, &buf);
char*p2 = strtok_s(NULL, delim, &buf);
CString strp1 = (_bstr_t)p1;
CString strp2 = (_bstr_t)p2;

循环截取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char* str = strName.GetBuffer(strName.GetLength() + 1);
char* delim = "\\";
char* buf;
int count = 0;
for (int i = 0; str[i]; i++)
{
if (str[i] == *delim)
{
count++;
}
char* p = strtok_s(str, delim, *buf);
for (int j = 0; j < count; j++)
{
p = strtok_s(NULL, delim, &buf);
}
CString strp = (_bstr_t)p;

}

禁用

GetDlgItem(IDC_Text)->EnableWindow(FALSE);

调用exe

法一:
UINT temp=WinExec(strPath,SW_SHOWNORMAL);
法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

STARTUPINFO si;
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
PROCESS_INFORMATION pi;
BOOL bDemoPro = CreateProcess(path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (!bDemoPro)
{
ASSERT(FALSE);
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

窗口缩放

.h文件中:
protected:
afx_msg void OnSize(UINT nType,int cx, int cy);
POINT old;
CRect m_rect;

.cpp文件中:
ON_WM_SIZE()

在OnInitDialog()中:

1
2
3
GetClientRect(&m_rect);//初始大小
old.x = m_rect.right - m_rect.left;
old.y = m_rect.bottom - m_rect.top;

在OnSize函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
CDialog::OnSize(nTpye, cx, cy);
//TODO
float fsp[2];
POINT Newp;
CRect rectNow;
GetClientRect(&rectNow);
Newp.x = rectNow.right - rectNow.left;
Newp.y = rectNow.bottom - rectNow.top;
fsp[0] = (float)Newp.x / old.x;
fsp[1] = (float)Newp.y / old.y;
CRect Rect;
int woc;
CPoint OldTLPoint, TLPoint;//左上
CPoint OldBRPoint, BRPoint;//右下
HWND hwndChild = ::GetWindow(m_hWnd, GW_CHILD);//列出所有控件
while (hwndChild)
{
woc = ::GetDlgCtrlID(hwndChild);
GetDlgItem(woc)->GetWindowRect(Rect);
ScreenToClient(Rect);
OldTLPoint = Rect.TopLeft();
TLPoint.x = long(OldTLPoint.x*fsp[0]);
TLPoint.y = long(OldTLPoint.y*fsp[1]);
OldBRPoint = Rect.BottomRight();
BRPoint.x = long(OldBRPoint.x*fsp[0]);
BRPoint.y = long(OldBRPoint.y*fsp[1]);
Rect.SetRect(TLPoint, BRPoint);
GetDlgItem(woc)->MoveWindow(Rect, TRUE);
hwndChild = ::GetWindow(hwndChild, GW_HWNDNEXT);
}
old = Newp;
}

更新数据

UpdateData(TRUE);//更新值到界面
UpdateData(FALSE);//更新界面到值

数据类型转换

  1. int转CString
    iRow是int型,number是CString型。
    CString number;
    number.Format(“%d”, iRow);

2._variant_t转int
_variant_t a;
int b;
b=_ttoi((LPCTSTR)(_bstr_t)a);


MSDN参考

MSXML

MSXML API 历史记录 https://msdn.microsoft.com/zh-cn/library/ms762314(v=vs.85).aspx

MSXML SDK 概述 https://msdn.microsoft.com/zh-cn/library/ms760399(v=vs.85).aspx

TreeControl控件

使用 CTreeCtrl https://msdn.microsoft.com/zh-cn/library/8ws6dh1y(v=vs.120).aspx

ListControl控件

使用 CListCtrl https://msdn.microsoft.com/zh-cn/library/cc468276(v=vs.71).aspx
创建列表控件 (List Control) https://msdn.microsoft.com/zh-cn/library/cc451545(v=vs.71).aspx


现有一个XML文档如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<root>
<Node world = "生命">
<Node animal = “动物">
<Node aniname = "熊猫">
</Node>
<Node aniname = "兔子">
</Node>
</Node>
<Node plant = "植物">
<Node planame = "仙人掌">
</Node>
<Node planame = "玫瑰">
</Node>
</Node>
</Node>
</root>

要求在树状控件上显示如下:
–root
—-动物
—-植物

在树状控件上点击“动物”,在列表控件上出现
1 熊猫
2 兔子

同理,点击“植物”,在列表控件上出现
1 仙人掌
2 玫瑰

思维碰撞

之前的思路:先SelectedSingleNode(“/Node/Node”),定位到动物这一层。然后获得该结点的属性即aniname,插入树状控件。接着指向其兄弟结点,将兄弟结点(植物)的属性同样插入树状控件。如果新插入结点,可能需递归(但都是在动物同层面)。
因为并没有将“熊猫”“兔子”插入树状控件,因此无法使用m_tree.GetChildItem(ht)获取。所以我想通过“动物”植物“的aniname和planame来反过来查找对应xml里的结点pNode(这里存在问题how to find it?遍历xml找同样的名字?Item能和node存在对应关系么)。找到后,只需GetChildNode之类获取孩子结点,再插入到列表控件中(也就是和列表控件交互实际是列表控件在和xml直接交互,树控件只起到选择某个item,对应到xml中)。
简单说来:选择树结点->获得该xml对应结点->获得每一个孩子结点->获得孩子结点的文本和其他路径属性->通过列表插入

就算是为树状控件和列表控件设置一个类或者一个结构,先要把xml所需的信息全部放入。毕竟投射到树状控件的结点并非全部结点,最重要的是列表控件所需结点不在树状控件中。那么添加删除时,对应的类或结构也应相应操作,是否繁琐。但感觉之前用树控件用错了,只是获取了文本,数据并没传入。

又过了一天。思路变为:为不直接操作XML,应创建一个结构体,利用MSXML解析XML将信息放入结构体中,在后续树状控件和列表控件操作时都调用该结构体而非XML。

前面那么多废话,其实说白了就是要自己生成一棵树,数据来源是xml,通过MSXML获得每个结点信息,自己生成的树中只包含所需要的xml片段信息,之后树控件根据指针来获得选择结点的孩子结点。
xml解析的最关键之处,就是如何将xml文件内容解析成内存中的可用、易用的程序数据—DOM(Document Object Model)树。DOM其实就是多叉树,每个节点只需知道自己的第一个子节点(first child)和下一个兄弟节点(next sibling),即可实现元素数据的解析。

理一理六层结构:
map->分类节点->vector->n个小节点->结构体->xml

如果要求分类节点就一层,是完全OK的。但如果要求分类下面还需要小分类,这就很不ok了。换为
两个结构体:1)小节点结构体->xml
2)分类节点结构体->vector->1)

发现六层结构办不到时,仿佛多米诺骨牌立到最后几块,突然看到最开始的一段里有一块距离放远了……
但换成双结构体其实改动也没太多,又好像把那块出差错的给换了块适合长度的,整个链条还是能补完整的。
思维过山车 跌宕起伏

文章目录
  1. 1. 方法记录
    1. 1.1. MSXML相关
      1. 1.1.1. 创建对象
      2. 1.1.2. 获取根节点
      3. 1.1.3. 获取属性(法一)
      4. 1.1.4. 获取属性(法二)
      5. 1.1.5. 删除节点
    2. 1.2. 树控件相关
      1. 1.2.1. 插入
      2. 1.2.2. 存入数据
      3. 1.2.3. 获得根节点
      4. 1.2.4. 展开树
      5. 1.2.5. 获取选中树
    3. 1.3. 列表控件相关
      1. 1.3.1. 插入
      2. 1.3.2. 存入数据
      3. 1.3.3. 删除全部项
      4. 1.3.4. 在列表最后插入
    4. 1.4. 其它C++相关
      1. 1.4.1. MessageBox
      2. 1.4.2. 父对话框给子对话框传值
      3. 1.4.3. 在子对话框显示图片
      4. 1.4.4. vector
      5. 1.4.5. map
      6. 1.4.6. 父子循环
      7. 1.4.7. 几个函数
      8. 1.4.8. 创建临时文件夹
      9. 1.4.9. 复制文件夹及子文件
      10. 1.4.10. 删除文件目录及子文件夹
      11. 1.4.11. 文件遍历
      12. 1.4.12. 截取字符串
      13. 1.4.13. 禁用
      14. 1.4.14. 调用exe
      15. 1.4.15. 窗口缩放
      16. 1.4.16. 更新数据
    5. 1.5. 数据类型转换
  2. 2. MSDN参考
    1. 2.1. MSXML
    2. 2.2. TreeControl控件
    3. 2.3. ListControl控件
  3. 3. 思维碰撞
|