📄 ctreedemodlg.cpp
字号:
//================================================================================
// 这是一个例子程序,主要用于演示ADO和CTREE控件的使用
// 在写代码期间,改成了VCHELP精选问题的简易离线浏览
// 我们在这个小程序中将会用到很多小技巧
// 1、 ADO操作数据库
// 2、 树形控件的使用
// 3、 控件大小随着窗口变化
// 4、 对话框之间的通讯
//
//参考资料:VCHELP网站的部分文章
// VC知识库网站的部分文章
//程序编写:雷神
//日期: 200211
//修改计划:也不能算修改了,因为有时间我会重写,主要会尝试将ADO和CTREE封装起来
// 另外不会再使用对话框,在界面也会有较大的改变
// 还有需要增加更多的功能,例如树的拖放、搜索等等。
// 当然这不知道是什么时候的事了。呵呵。
//关于版权:此程序没有版权,你可以随意拷贝修改。
// 如果你愿意并保留了以上信息,雷神对此表示感谢!
//关于问题:此程序已知的问题是,当数据库记录的ID字段超过255时会溢出
// 因为树中的节点(包括字节点)的数量限制在255个
// 雷神还未想到好的办法解决。
// 如果发现程序有其他的BUG请和我联系
//个人主页:http://www.ai361.com
//电子邮件:lsmodel@ai361.com
//
// *************************
// * 最后祝大家编程愉快。*
// *************************
//
//================================================================================
// CTreeDemoDlg.cpp : implementation file
#include "stdafx.h"
#include "CTreeDemo.h"
#include "CTreeDemoDlg.h"
#include "AddNodeDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CCTreeDemoDlg dialog
CCTreeDemoDlg::CCTreeDemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCTreeDemoDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CCTreeDemoDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CCTreeDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCTreeDemoDlg)
DDX_Control(pDX, IDC_TREE1, m_tree);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CCTreeDemoDlg, CDialog)
//{{AFX_MSG_MAP(CCTreeDemoDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_SIZE()
ON_NOTIFY(NM_DBLCLK, IDC_TREE1, OnDblclkTree1)
ON_NOTIFY(NM_RCLICK, IDC_TREE1, OnRclickTree1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CCTreeDemoDlg message handlers
BOOL CCTreeDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
HRESULT hr;
try
{
hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
if(SUCCEEDED(hr))
{
hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=HandBook.mdb","","",adModeUnknown);///连接数据库
///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51;
}
ShowTree();
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
}
///在这里可以改变窗口的标题
AfxGetMainWnd()->SetWindowText(_T("VCHELP问题集(这是一个ADO和CTREECTRL的应用的演示程序)"));
CDialog::OnInitDialog();
return TRUE; // return TRUE unless you set the focus to a control
}
void CCTreeDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CCTreeDemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CCTreeDemoDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
///显示目录树的函数
void CCTreeDemoDlg::ShowTree()
{
m_pRecordset.CreateInstance("ADODB.Recordset");
//====================================
// 定义一个HTREEITEM 数组变量
// 用来存放所有树项目的句柄这里暂定255
// 这个问题比较麻烦,它限制了我们树的项目不能超过255
// 雷神有一个办法,但是在这个程序中我并没有实现它
// 可以用SQL语句返回记录的ID号最大的数值
// (不能返回记录集的个数,因为有被删除过的记录,
// 记录个数作为数组下标会产生溢出)。
// 然后在定义数组的大小。这样可以从表面上解决这个问题
// 不过这样做的效率和资源浪费情况不尽人意。
// 所以我也正在找更好的方法,如果你有好的办法也请告诉我。
//====================================
HTREEITEM hItem,hSubItem[255];
m_pRecordset->Open("SELECT * FROM NodeTree",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
m_pRecordset->MoveFirst();
m_tree.DeleteAllItems();
while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗?
{
vID = m_pRecordset->GetCollect("ID");
vNodeName = m_pRecordset->GetCollect("NodeName");
vParentID = m_pRecordset->GetCollect("ParentID");
//父级项句柄是 hSubItem[vParentID.lVal]
hItem= hSubItem[vParentID.lVal];
item=vID.lVal;
hSubItem[item]=m_tree.InsertItem((LPCTSTR)(_bstr_t)vNodeName,hItem);
m_pRecordset->MoveNext();///移到下一条记录
}
}
///树控件可以随着窗口变化而跟着变化大小
void CCTreeDemoDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(m_tree.m_hWnd)
m_tree.SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER|SWP_NOMOVE);
}
void CCTreeDemoDlg::OnOK()
{
// TODO: Add extra validation here
///关闭打开的记录集
if(m_pRecordset->State)
m_pRecordset->Close();
///关闭数据库连接
if(m_pConnection->State)
m_pConnection->Close();
CDialog::OnOK();
}
///显示选中项的详细信息
void CCTreeDemoDlg::OnDblclkTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
CString strItem;
_variant_t vName;
CAddNodeDlg dlg;
HTREEITEM m_hTreeItem; ///定一个用来存放当前项的句柄
m_hTreeItem = m_tree.GetSelectedItem(); ///获得当前项的句柄,然后你就可以为所欲为了,嘿嘿
m_tree.SelectItem(m_hTreeItem); ///设定当前项
CString S1 = m_tree.GetItemText(m_hTreeItem); ///用GetItemText()函数取得选定项的文本:
///获得选中项的文本赋给m_NodeName
dlg.m_NodeName=m_tree.GetItemText(m_hTreeItem);
dlg.m_Pname=m_tree.GetItemText(m_tree.GetParentItem(m_hTreeItem));
///下面演示了用ADO建立一个数据库的记录集的方法。
///注意_variant_t,_bstr_t 两个类型和其它类型的转换
S1="SELECT * FROM NodeTree where NodeName='"+S1+"'";
m_pRecordset->Close();
m_pRecordset->Open((_variant_t)S1,_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
m_pRecordset->MoveFirst();
vName=m_pRecordset->GetCollect("Detail");
dlg.m_Detail=(LPCTSTR)(_bstr_t)vName;
dlg.m_id=m_pRecordset->GetCollect("id").lVal;
dlg.m_pid=m_pRecordset->GetCollect("ParentID").lVal;
dlg.DoModal(); ///显示详细信息的对话框
*pResult = 0;
}
void CCTreeDemoDlg::OnRclickTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
//===============================================================
//这里是本程序的精髓,演示添加,删除,修改。同级节点、和子节点
//另外同时使用了ADO的两种不同的方法添加记录
//并且这次的树选中判断用的是鼠标范围
//================================================================
CString S1,strItem,strSql; ///定义一个用来存放SQL语句的字符串
_variant_t vName,vNULL;
CPoint point;
GetCursorPos(&point); ///获得当前鼠标的坐标
CPoint PinT=point;
m_tree.ScreenToClient(&PinT);
HTREEITEM m_hTreeItem;///定一个用来存放当前项的句柄
UINT nFlag=TVHT_ONITEM; ///在一个项目上点击鼠标右键时
m_hTreeItem=m_tree.HitTest(PinT,&nFlag);
if(m_hTreeItem!=NULL)
{
m_tree.SelectItem(m_hTreeItem); ///选中当前项目
CString S1 = m_tree.GetItemText(m_hTreeItem); ///用GetItemText()函数取得选定项的文本:
S1="SELECT * FROM NodeTree where NodeName='"+S1+"'";
m_pRecordset->Close();
m_pRecordset->Open((_variant_t)S1,_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
m_pRecordset->MoveFirst();
CAddNodeDlg dlg;
dlg.m_pid=m_pRecordset->GetCollect("ParentID").lVal;
dlg.m_id=m_pRecordset->GetCollect("ID").lVal;
dlg.m_Pname=m_tree.GetItemText(m_tree.GetParentItem(m_hTreeItem));
if(dlg.DoModal()==IDOK)
{
if(dlg.m_bDel)
{
strItem.Format("%d",dlg.m_id);
strSql="DELETE * FROM NodeTree WHERE ParentID="+strItem+" or id="+strItem;
MessageBox("删除成功!");
m_pConnection->Execute((_bstr_t)strSql,&vNULL,adCmdText);
CCTreeDemoDlg::ShowTree();
}
else
{
///判断信息是否填写完整
if(dlg.m_NodeName=="")
AfxMessageBox("节点(问题)和节点描述(问题回复)均不能为空!\r\n你的信息填写不完整!\r\n数据库没有更新!!!!");
///判断是添加子节点还是同级节点,
else if(dlg.m_PorC)
{
//========================================================
//以下演示用Recordset的AddNew方法添加记录
//添加的是子节点,也就是选中项的下级子项
//=========================================================
m_pRecordset->AddNew();///添加新记录
m_pRecordset->PutCollect("NodeName",_variant_t(dlg.m_NodeName));
m_pRecordset->PutCollect("ParentID",_variant_t((long)dlg.m_id));
m_pRecordset->PutCollect("Detail",_variant_t(dlg.m_Detail));
m_pRecordset->Update();
CCTreeDemoDlg::ShowTree();
}
else
{
//========================================================
//以下演示用Connection的Execute方法添加记录
//Command比较适合存储过程,这里就不做演示了
//添加的是同级节点,也就和是选中项同属一个父级节点的子项
//=========================================================
strItem.Format("%d",dlg.m_pid);
strSql="INSERT INTO NodeTree(NodeName,ParentID,Detail) VALUES('"+dlg.m_NodeName+"',"+strItem+",'"+dlg.m_Detail+"')";
m_pConnection->Execute((_bstr_t)strSql,&vNULL,adCmdText);
CCTreeDemoDlg::ShowTree();
}
}
}
}
*pResult = 0;
}
///此函数作用为当单击帮助按钮时弹出帮助对话框
void CCTreeDemoDlg::ShowHelp()
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
///此函数作用是当在关于对话框单击时对话框关闭。
///因为为了好看些把关闭和取消按钮都砍掉了。(雷神认为好看一点)
void CAboutDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CDialog::OnOK();
CDialog::OnLButtonDown(nFlags, point);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -