⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ctreedemodlg.cpp

📁 数据库的递归查找程序
💻 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 + -