Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1569131
  • 博文数量: 884
  • 博客积分: 52280
  • 博客等级: 大将
  • 技术积分: 13060
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-06 09:46
文章分类

全部博文(884)

文章存档

2008年(884)

我的朋友

分类: C/C++

2008-08-06 09:59:31

下载本文示例代码
下载源代码

  工作之余,用VC作了一个小程序,是VC SQL server模式的,程式内容比较简单,主要设及以下内容:

  1. 窗口如何分割;
  2. 多视图之间如何通信;
  3. 列表视图的操作及事件处理;
  4. 树形控件的操作及事件处理,利用递归根据数据库动态生成目录;
  5. 实现主从表的显示 以下对程序细节稍作一下说明;

一、窗口如何分割

  切分窗口既适用于SDI应用程序,也适宜于MDI应用程序。它通常驻是以类 CSplitterWnd来表示,对 Windows 来说,CSplitterWnd 是一个真正的窗口,它完全占用框架窗口的客户区域而视图占用切分窗口的窗格区域。 动态切分与静态切分 动态切分允许用户须任何时候对窗口进行切分,用户既可以通过选取菜单也可以通过拖动滚动条中的切分框来进行切分。动态切分窗口使用的是一个视图类。 静态切分,当窗口第一次被创建时,窗格就已经被切他好了,它们不能再被改变。用户可以移动窗格边框,但此时不能再对窗口进行合并或再划分。静态切分窗口允许使用多个视图类,并且可以创建时对这些视图类进行配置。在静态切分窗口中,每个窗格都有自己的滚动条。 动态切分比较简单,不实用,下面看静态切分。

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)

{

	CRect rect;

	GetClientRect(&rect);

	m_wndSplitter1.CreateStatic(this,1,2); 

	m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CClassTreeView),CSize(150,150),pContext); 

	m_wndSplitter2.CreateStatic(&m_wndSplitter1,

	                            2,1,WS_CHILD|WS_VISIBLE,

	                            m_wndSplitter1.IdFromRowCol(0,1));

	

	m_wndSplitter2.CreateView(1,0,RUNTIME_CLASS(CDagDetialView),CSize(0,0),pContext);

	m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CDagView),CSize(0,0),pContext);

	m_wndSplitter2.SetRowInfo(0,rect.Height()/2,0);



	return true;



         //重载时不能调用基类的OnCreateClient()

}      
以上是本程序DEMO使用的方法。关于切分详细信息,请查阅MSDN.

二、多视图之间的通信

  本程序demo使用了三个视图类,Winzard生成了一个,其余的是我加的。关于多视图之间的通信主要是茯得你要访问的视图类的pointer,如何茯得?有两种方法1,通过视图类(如果有多个视图共享一个文档类的话)2.通过主框架茯得。由于我的程式就一个文档类,其他视图类是独立的,所以就采用第二种方法了。
以下代码是茯得CDagDetialView类指针的示例,其余类同。
CMainFrame* pFrame=static_cast(AfxGetMainWnd());//茯得主框架

CDagDetialView* pDagDetialView = 

          static_cast(pFrame->m_wndSplitter2.GetPane(1,0));

  以上有一点,我觉得最好采用以上形式,虽说通过C的强制转换语法也行,但最好还是使用C 的语法比较好。
  茯得视图指针后你就可以do anything that you want to do
顺便说一下,通信细节有一些牵连到效率的地方,一定要妥善处理。上面的demo程式,没有考虑过多。因为我觉得这比较适合大家看明白意思,如果程式模块分散开来,程式看起来是简洁了不少,但不太方便看清程序流程。请见谅。

三、列表视图的操作及事件处理

  说句实话,列表的属性参数很多,标准的、扩展的风格参数一大堆,看看那些英文文档,在其中苦苦搜寻,找一条自己需要的特性,其中的滋味别提了。还好我很有耐心:)熟练了就好了,都记住了一大半,还怕什么。列表控件显示数据,要用LVS_REPORT风格,比较好看,就像DataGrid一样。设置窗口风格需要用SetWindowLong这个API函数。这个函数是通用的。参数也一大堆。处理列表的单击事件即是处理消息NM_CLICK,处理选择改变时的事件即是处理LVN_ITEMCHANGED,不同的消息可能参数不同,需区别对待。

void CDagView::OnItemchanged(NMHDR* pNMHDR, LRESULT* pResult) 

{

    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

    // TODO: Add your control notification handler code here

    CListCtrl& refListCtrl=GetListCtrl();

    if(pNMListView->uNewState&LVIS_SELECTED)

    …//选取列表项时,事件触发

    pResult=0;

}      
同样的方法处理单击,双击等事件,不再嗷述。

四、树形控件的操作及事件处理,利用递归根据数据库动态生成目录

  树形控件,和列表控件一样,在VC数据开发中有着非常重要的地位。常用来显示分级,分类信息。其实,提到树,脑海里就出现一大堆树,什么二叉树,查找树,哈夫曼树等一大堆,也就想起了遍历树的常用算法—递归。虽然采用堆栈更有效率,但使用递归常使程序设计简化。
看以下程式片段:
	pRS->MoveFirst();

	_variant_t var;

	CString strTableName,strTableCode;

	HTREEITEM hChildItem;

	while(!pRS->adoEOF)

	{

		var=pRS->GetCollect("menucode");

		if(var.vt != VT_NULL)

			strTableCode = (LPCSTR)_bstr_t(var);

			

		//此即函数递归出口

		if(!strTableCode.Find(strParent,0) && 

                                            strTableCode.GetLength()==strParent.GetLength() 2)

		{

			hChildItem=refTree.InsertItem(strTableName,hTreeNode,TVI_LAST);

			InitTree(refTree,hChildItem,strTableCode);

		}

		pRS->MoveNext();

	}      
  递归时充分利用树形结构的特点。然后结合递归思想,融合在一起,就行了。这里就是理论和实际的结合点。不同的开发语言提供的操作接口是不同的,如Delphi、.dotnet。我呈在在.net也实现了一个类似的树,算法思想一样,只是具体操作不同,因为dotnet提供的接口不同。
树形控件的事件处理:
void CClassTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 

{

	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	// TODO: Add your control notification handler code here	

	CTreeCtrl& refTreeCtrl=GetTreeCtrl();

	HTREEITEM hSelected=pNMTreeView->itemNew.hItem;

	if(hSelected!=NULL)

	{

		m_strMenuName=refTreeCtrl.GetItemText(hSelected); 

	    if(AfxGetMainWnd()->IsWindowEnabled())

		UpdateDagView();

	

	}



	//-----------

	*pResult = 0;

}
五、实现主从表的显示

  由于我假设不知道数据库中有多少字段,字段名都是不知道的。所有都动态生成。所以首先要检索主键。把主键检索出来以后,然后得到列表当前选择行的对应于主键列的数据。然后以这些主键列的值当作检索条件检索从表的数据(从表的主键一定多于主表)最后将结果显示出来。看起来过程也挺简单的,如果用dotnet中DataGrid做的话,太简单了是吧,但VC操作起来,就有点麻烦了。就拿从列表取当前选择行的主键列的数据的话,和dotnet一比,就知道VC麻烦在哪儿了。(不过我还是比较喜欢VC,嘿嘿)。一个DataGrid,如果我知道了列名,又茯取了当前行的行号(CurrentRowIndex)
如果你后台是直接绑定的一个DataTable,就可以直接取得这个值:
myTable.Rows[CurrnetRowIndex][“pkey1”].ToString();
当然如果你后面不是直接绑定一个DataTable,而是一个结果经过虑后的结果,那么可能会稍烦琐一点。这里不对这种情况进行计讨论。下面看一下VC下是怎么处里的,(如果有更好的方法,请通知我,先谢了)
int CDagView::GetColumnIndex(CListCtrl &ref,LPCTSTR strCol)

{

	CHeaderCtrl * pHeader=ref.GetHeaderCtrl();

	int nCount=pHeader->GetItemCount();

	TCHAR  lpBuffer[256];

	bool   fFound = false;

	HDITEM hdi;

	hdi.mask = HDI_TEXT;

	hdi.pszText = lpBuffer;

	hdi.cchTextMax = 256;

	int index=-1;

	for (int i=0;!fFound && (i < nCount);i  )

	{

		pHeader->GetItem(i, &hdi);

		

		if (strcmp(hdi.pszText, strCol) == 0)

		{

			index=i;

			fFound = true;

		}

	}

	return index;

}

iSubItem=GetColumnIndex(refListCtrl);

CString strValue=refListCtrl.GetItemText(iCurrent,iSubItem);      
先取得列号然后取得行号,最后取这个值......好了,就写这么多吧,具体细节再看代码。

六、结束语

  其实这个程式比较简单,在Windows 2k下调试通过!就写到这里吧,不足之处,见谅!有时候,再好的技术,如果缺乏有效的示例,也会变得晦涩难懂。像本文中的列表、树形控件参数很多,使用VC 虽然说可以发挥出他们最强劲的功能,但是有一部分参数,在MSDN中只是简要介绍,没有详细的示例说明(我猜:微软内部肯定有更加详细的示例及文档,或者说叫引擎,不对外公布)这就要靠广大程序员的努力探索了,知识共享,大家共同进步。

sambios 撰于 2004-9-2

下载本文示例代码
阅读(177) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~