8.5字段操作
在前面的示例中,虽然可以通过CRecordSet对象中的字段关联变量可以直接访问当前记录的相关字段值,但有时在处理多个字段时就不太方便了。CRecordSet类中的成员变量m_nFields(用于保存数据表的字段个数)和成员函数GetODBCFieldInfo及GetFieldValue可以简化多字段的访问操作。
GetODBCFieldInfo函数用于数据表中的字段信息,其函数原型如下:
voidGetODBCFieldInfo(short nIndex,CODBCFieldInfo &fieldinfo);
参数:nIndex用于指定字段索引号,0表示第1个字段,1表示第2个字段,…
Fieldinfo是CODBCFieldInfo结构参数,用于表示字段信息
CODBCFieldInfo结构体原型如下:
StructCODBCFieldInfo
{ CString m_strName; //字段名
SWORD m_nSQLType; //字段的SQL数组类型(SWORD表示short int短整形)
UDWORD m_nPrecision;//字段的文本大小或数据大小(UDWORD表示
//unsigned long int无符号长整型)
SWORD m_nScale; //字段的小数点位数
SWORD m_nNullability; //字段接受空值(NULL)能力
};
GetFieldValue函数用于获取数据表当前记录中指定字段的值,其常用的函数原型如下:
voidGetFieldValue(short nIndex,CString &strValue);
参数:nIndex用于指定字段索引号
strValue用于返回字段的内容
除了上述字段操作外,CRecordSet类的成员函数GetRecordCount和GetStatus还可分别用于获得表中的记录总数和当前记录的索引,其原型如下:
longGetRecordCount()const;
voidGetStatus(CRecordsetStatus &rStatus)count;
参数:rStatus是指向下列的CRecordsetStatus结构的对象:
StructCRecordsetStatus
{ long m_lCurrentRecord;//当前记录的索引,0表示第1个记录,1表示第2个记录,
//依次类推。-1表示在第1个记录之前,-2表示不确定
BOOL m_bRecordCountFinal;//记录总数是否是最终结果
};
GetRecordCount函数所返回的记录总数在表打开时或调用Requery函数后是不确定的,必须经过下列代码才能获得最终有效的记录总数:
while(!m_pSet->IsEOF())
{ m_pSet->MoveNext();
m_pSet->GetRecordCount();
}
例:字段的编程操作
用列表视图来显示一个课程信息表(见349表8.5)的全部记录内容,并在状态栏中显示记录总数。
1、为数据库Studentf.mdb添加一个数据表course,
下面我们开始用Access2003设计数据库:
(1)开机正常屏幕状态à开始à程序àMicrosoft OfficeàMicrosoft Office Access 2003à新建文件à空数据库à数据库名处写:studentfà点右边的创建à新建à设计视图à确定à出现表,你按书349页表8.5的下部分敲入数据即可。
(2)关闭此表à提问:你是否保存对表1的设计更改?à是à表名写courseà是à便在数据库中建立了表course
(3)你双击course表出现设计视图表,之后你按书349页表8.5上面部分敲进数据即可
2、为文档应用程序添加ODBC的支持
(1)建一个单文档应用程序名为:字段操作,在向导的第6步将View的基类由默认的CView选择为CListView类
(2)将项目工作区窗口切换到FileView页面,展开Header Files所有项,双击stdafx.h打开该文件
(3)在stdafx.h中添加ODBC数据库支持的头文件包含#include <afxdb.h>如下:
#endif//_AFX_NO_AFXCMN_SUPPORT
#include<afxdb.h>
3、创建数据表score的CRecordSet派生类
(1)View->ClassWizard->Add Class…下拉->New(加新类)
(2)在弹出的“Add Class”对话框中定义新的CRecordSet的派生类CCourseSet,如书350页图8.23所示。
(3)(4)
(5) cs.style &=~LVS_TYPEMASK;
cs.style|=LVS_REPORT;
(6) CListCtrl&m_ListCtrl=GetListCtrl();//获取内嵌在列表视图中的列表控件
CCourseSet cSet;
cSet.Open();//打开记录
CODBCFieldInfo field;
//创建列表头
for(UINT i=0;i<cSet.m_nFields;i++)
{
cSet.GetODBCFieldInfo(i,field);
m_ListCtrl.InsertColumn(i,field.m_strName,LVCFMT_LEFT,100);
}
//添加列表项
int nItem=0;
CString str;
while(!cSet.IsEOF())
{
for(UINTi=0;i<cSet.m_nFields;i++)
{
cSet.GetFieldValue(i,str);
if(i==0)
m_ListCtrl.InsertItem(nItem,str);
else
m_ListCtrl.SetItemText(nItem,i,str);
}
nItem++;
cSet.MoveNext();
}
cSet.Close();//关闭记录集
(7)在CMyView.cpp文件的前面加:#include “CourseSet.h”
说明:当为数据源中的某个数据表映射一个CRecordSet类时,该类对象一定先要调用CRecordSet::Open成员函数,才能访问该数据表的记录集,访问后还须调用CRecordSet::Close成员函数关闭记录集。
(8)编译运行,结果见351页图8.24
4、在状态栏中显示当前记录号和记录总数
(1)
(2)在CMyView.cpp文件前面的#endif后加:
voidDispRecNum(CCourseSet *pSet)
{
CStringstr;
CMainFrame *pFrame= (CMainFrame *)AfxGetApp()->m_pMainWnd;//获得主框
//架窗口的指针
CStatusBar*pStatus = &pFrame->m_wndStatusBar;//获得主框架窗口中的状态栏指针
if(pStatus)
{
CRecordsetStatus rStatus;
pSet->GetStatus(rStatus);//获得当前记录信息
str.Format("当前记录:%d 总记录:%d",1+rStatus.m_lCurrentRecord,
pSet->GetRecordCount());
pStatus->SetPaneText(1,str);
}
}
该函数先获得状态栏对象的指针,然后调用SetPaneText函数更新第2个窗格的文本。(3)在前面CMyView.cpp的OnInitialUpdate函数中刚才已经加好的代码最后一
条语句前面加: ::DispRecNum(&cSet);
(4)在CMyView.cpp文件的开始处添加:”MainFrm.h”
(5)将MainFrm.h文件中的保护型变量m_wndStatusBar剪切到public:里
(6)编译运行,结果见353页图8.25
8.6多表处理(此例是以树控件形式显示学生成绩)
数据库中表与表之间往往存在着一定的关系,例如:要显示一个学生的课程成绩信息,包括学号、姓名、课程号、课程所属专业、课程名称、课程类别、开课学期、课时数、学分、成绩,则要涉及前面的学生课程成绩表(score)、课程表以及学生基本信息表。以下示例是在一个对话框中用2个控件来进行学生课程成绩信息的相关操作,如353页图8.26所示,其左边是树视图,用于显示学生成绩、专业和班级号3个层次信息,单击班级号,所有该班级的学生课程成绩信息将在右边的列表视图中显示出来。
例:多表处理
1、为数据库Student.mdb添加一个数据表: student和score
双击Microsoft Office Access 2003在右边“打开”(已有的数据库名)的下面双击studentf(我们以上课里建好的数据库),便出现一个对话框,你点“打开”又出现一个“studentf:数据库”对话框->你点新建->设计视图->确定->开始输入数据,你将书354页表8.6的下表数据敲入到数据库中,表的名字为:student
这时在studentf数据库中右出现一个student表(原来有个course表),你双击它便开始敲入354页表8.6的上表数据。
以同样的方法再建一个score表,该表的内容为336页表8.1(studentf里共有
三个表,即:course、student、score)
2、创建并设计对话框应用程序
(1)创建一个基于对话框的应用程序名为:处理多个表
(2)(3)(4)(5)(6)
3、添加对MFC ODBC的支持及记录集
(1)#include <afxdb.h>
(2)
4、完善左边树控件的代码
(1)为CMyDlg类添加一个成员函数:ClassView->右键对准CMyDlg单击->
AddMember Function->Function Type处写:HTREEITEM->Function Declaration处写:FindTreeItem(HTREEITEM hParent, CString str)用于查找指定结点下是否有指定结点文本的子结点,函数代码如下:
并加如下代码:
HTREEITEM CMyDlg::FindTreeItem(HTREEITEMhParent, CString str)
{
HTREEITEM hNext;
CString strItem;
hNext=m_treeCtrl.GetChildItem(hParent);
while(hNext!=NULL)
{
strItem=m_treeCtrl.GetItemText(hNext);
if(strItem==str)
return hNext;
else
hNext=m_treeCtrl.GetNextItem(hNext,TVGN_NEXT);
}
return NULL;
}
(2)用右键对准CMyDlg类,为CMyDlg类加一个CImageList类的成员变量m_ImageList
(3)m_ImageList.Create(16,16,ILC_COLOR8|ILC_MASK,2,1);
m_treeCtrl.SetImageList(&m_ImageList,TVSIL_NORMAL);
SHFILEINFO fi; //定义一个文件信息结构变量
SHGetFileInfo("C:\\Windows",0,&fi,sizeof(SHFILEINFO),
SHGFI_ICON|SHGFI_SMALLICON);//获取文件夹图标
m_ImageList.Add(fi.hIcon);
SHGetFileInfo("C:\\Windows",0,&fi,sizeof(SHFILEINFO),
SHGFI_ICON|SHGFI_SMALLICON|SHGFI_OPENICON);//获取打开文件夹图标
m_ImageList.Add(fi.hIcon);
HTREEITEM hRoot,hSpec,hClass;
hRoot=m_treeCtrl.InsertItem("学生成绩",0,1);
CStudentSet sSet;
sSet.m_strSort="special";//按专业排序
sSet.Open();
while(!sSet.IsEOF())
{
hSpec=FindTreeItem(hRoot,sSet.m_special);
//查找是否有重复的专业结点
if(hSpec==NULL)//若没有重复的专业结点
hSpec=m_treeCtrl.InsertItem(sSet.m_special,0,1,hRoot);
hClass=FindTreeItem(hSpec,sSet.m_studentno.Left(6));
//查找是否有重复的班级结点
if(hClass==NULL)//若没有重复的班级结点
hClass=m_treeCtrl.InsertItem(sSet.m_studentno.Left(6),0,1,hSpec);
sSet.MoveNext();
}
sSet.Close();
(4)#include"StudentSet.h"
#include "ScoreSet.h"
#include "CourseSet.h"
(5)编译运行,第1次结果与356页图8.27一样
5、完善右边列表控件的代码
(1) //设置列表头
CString strHeader[]={"学号","姓名","课程号","课程所属专业","课程名称",
"课程类别","开课学期","课时数","学分","成绩"};
intnLong[]={80,80,80,180,180,80,80,80,80,80};
for(intnCol=0;nCol<sizeof(strHeader)/sizeof(CString);nCol++)
m_listCtrl.InsertColumn(nCol,strHeader[nCol],LVCFMT_LEFT,nLong[nCol]);
(2)为CMyDlg类添加一个成员函数:ClassView->右键对准CMyDlg单击->
AddMember Function->Function Type处写:void->Function Declaration处写:DispScoreAndCourseInfo(CString strFilter)用于根据指定的条件在列表控件中用报表形式显示学生成绩的所有信息,代码如下:
voidCMyDlg::DispScoreAndCourseInfo(CString strFilter)
{
m_listCtrl.DeleteAllItems();//删除所有的列表项
CScoreSet sSet;
sSet.m_strFilter=strFilter;//设置过滤条件
sSet.Open();//打开score表
int nItem=0;
CString str;
while(!sSet.IsEOF())
{
m_listCtrl.InsertItem(nItem,sSet.m_studentno);//插入学号
//根据score表中的studentno(学号)获取student表中的“姓名”
CStudentSet uSet;
uSet.m_strFilter.Format("studentno='%s'",sSet.m_studentno);
uSet.Open();
if(!uSet.IsEOF())
m_listCtrl.SetItemText(nItem,1,uSet.m_studentname);
uSet.Close();
m_listCtrl.SetItemText(nItem,2,sSet.m_course);
//根据score表中的course(课程号)获取course表中的课程信息
CCourseSet cSet;
cSet.m_strFilter.Format("courseno='%s'",sSet.m_course);
cSet.Open();
UINT i=10;
if(!cSet.IsEOF())
{
for(i=0;i<cSet.m_nFields;i++)
{
cSet.GetFieldValue(i,str);//获取指定字段值
m_listCtrl.SetItemText(nItem,i+2,str);
}
}
cSet.Close();
str.Format("%0.1f",sSet.m_score);
m_listCtrl.SetItemText(nItem,i+2,str);
sSet.MoveNext();
nItem++;
}
if(sSet.IsOpen())
sSet.Close();
}
(3)编译运行,第2次结果与358页图8.28一样
6、完善两控件的关联代码
上面作完,学生成绩还没有显示出来,下面就实现单击左边的班级号,在右边的视图中显示该班级的所有学生成绩信息。
(1)用MFC ClassWizard为CMyDlg类添加树控件(选中IDC_TREE1,右边找到TVN_SELCHANGED消息映射)TVN_SELCHANGED消息处理,并加下列代码:
voidCMyDlg::OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView =(NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notificationhandler code here
HTREEITEMhSelItem=pNMTreeView->itemNew.hItem;//获取当前选择的结点
//如果当前的结点没有子结点,那说明该结点是班级号结点
if(m_treeCtrl.GetChildItem(hSelItem)==NULL)
{
CString strSelItem,str;
strSelItem=m_treeCtrl.GetItemText(hSelItem);
str.Format("studentnoLIKE'%s%%'",strSelItem.Left(6));
DispScoreAndCourseInfo(str);
}
*pResult = 0;
}
代码中,调用DispScoreAndCourseInfo函数的参数是用于设置数据表(记录集)打开的过滤条件。str是类似内容:”studentno LIKE 210101%”,它使所有学号前面是210101的记录被打开。%是SQL使用的通配符,由于%也是Visual C++格式前导符,因此在代码中需要2个%号。
(2)编译运行,达到预想效果
第8章结束
ADO数据库编程这里不做介绍,大家可以自己看一看,当做自学。
!!!!!!!!!!!!!!!!!!