报表向导操作包括三个步骤:报表字段的选择、格式的设置以及设置信息的确认,因此制作向导时需要三个对话框。6.3.2节已经完成了报表向导的界面设计,下面我们首先介绍PropertySheet和PropertyPage的创建方法,然后分别介绍三个步骤的实现代码。
· 创建报表向导的CPropertySheet类
创建CPropertySheet类的操作步骤:
(1) 执行菜单命令“Insert>New Class…”,VC++弹出“New Class” 对话框,如图6-18所示。
图6-18 New Class对话框
(2) 在“New Class” 对话框里保持Class Type项为MFC Class,在Name编辑框里输入“CReportWizard”,然后在Base Class下拉列表框里选择派生的父类CPropertySheet,如图6-19所示。
(3) 单击对话框的OK按钮,VC++将CReportWizard类添加到工程里。此后,我们就可以在这个类里添加PropertyPage了。在后面我们完成了所有的PropertyPage创建以后,我们将在该类的实现代码里添加PropertyPage插入操作。
· 创建报表向导的CPropertyPage类
图6-19 设置派生的类名称与父类
这里需要创建三个CPropertyPage类,分别代表IDD_WZDFIELD,IDD_WZDFORMAT和IDD_WZDPREVIEW对话框。这三个类的名称分别是CReportWzdField、CReportWzdFormat和CReportWzdPreview,它们的创建方法是相同的,这里只介绍CReportWzdField类的创建方法。
操作步骤:
(1) 执行菜单命令“Insert>New Class…”,VC++弹出“New Class” 对话框,如图6-18所示。
(2) 在“New Class” 对话框里保持Class Type项为MFC Class,在Name编辑框里输入“CReportWzdField”,然后在Base Class下拉列表框里选择派生的父类CPropertyPage,在Dialog ID组合框里选择IDD_WZDFIELDS项,如图6-20所示。注意,在创建CReportWzdFormat类时需要选择IDD_WZDFORMAT,在 创建CReportWzdPreview类时需要选择IDD_WZDPREVIEW。
图6-20 设置派生的类名称与父类
(3) 单击对话框的OK按钮,VC++将CReportWzdField类添加到工程里。
· 编写报表向导CPropertySheet类的实现代码
CPropertySheet类的实现代码主要是在类的构造函数里将三个PropertyPage插入到PropertySheet里,同时设置PropertySheet为向导方式。我们首先在ReportWizard.h文件里添加下面的头文件引用代码:
#include "ReportWzdField.h"
#include "PreviewWnd.h"
#include "ReportWzdPreview.h"
然后在类的声明代码里添加如下声明代码:
public:
CReportWzdField m_Page1;
CReportWzdFormat m_Page2;
CReportWzdPreview m_Page3;
在构造函数里,我们添加如下实现代码:
AddPage(&m_Page1);
AddPage(&m_Page2);
AddPage(&m_Page3);
SetWizardMode();
前三个函数将三个PropertyPage插入到PropertySheet,SetWizardMode()函数设置PropertySheet为向导方式。
· 编写CReportWzdField类的实现代码
(1) 首先需要创建类里与对话框中控件相关联的变量,这些变量可以通过ClassWizard工具添加。通过ClassWizard工具添加变量的方法,本书已经在6.3.2节作了介绍,这里将添加变量的名称、类型、关联的控件ID及其意义罗列出来,如表6-14所示。
表6-14 CReportWzdField类的变量
名称 |
类型 |
关联的控件ID |
意义 |
m_LBFieldCandi |
ClistBox |
IDC_LISTPRE |
候选的报表字段列表 |
m_LBSelected |
CListBox |
IDC_LISTSEL |
选用的报表字段列表 |
m_strReportTitle |
CString |
IDC_MAINTITLE |
报表标题 |
m_strFieldTitle |
CString |
IDC_FIELDTITLE |
报表字段标题 |
m_strReportFooter |
CString |
IDC_FOOTER |
报表注脚标题 |
m_CBPageType |
CComboBox |
IDC_CBPNFORMAT |
页码类型列表 |
m_fUsePageNum |
BOOL |
IDC_CHKPAGENUM |
是否使用页码复选框 |
(2) 编写OnSetActive()消息响应函数
该函数在属性页被激活时执行,将该属性页的向导按钮只显示出“下一步”,需要在该函数里添加如下代码:
CPropertySheet* psheet = (CPropertySheet*) GetParent();
psheet->SetWizardButtons(PSWIZB_NEXT);
SetWizardButtons()的作用即是令本属性页的向导按钮只显示“下一步”。
(3) 编写属性页的OnInitDialog()函数
该函数实现本属性页的初始化操作。初始化时,需要将当前显示在视图里的报表信息字段名称罗列在候选列表框里,实现代码如下:
if(!m_pCommonRS->IsOpen()) return TRUE;
int nFieldCount = m_pCommonRS->GetODBCFieldCount();
m_LBFieldCandi.ResetContent();
CODBCFieldInfo fieldinfo;
for(int n=0;n
m_pCommonRS->GetODBCFieldInfo(n, fieldinfo);
m_LBFieldCandi.InsertString(-1, fieldinfo.m_strName);
}
UpdateData(FALSE);
代码通过m_pCommonRS实例的GetODBCFieldInfo函数获取记录集的字段信息。
(4) 编写选择按钮“>”的响应函数OnSelect()
可以通过ClassWizard工具为ID_SELECT按钮添加消息响应函数,使用ClassWizard工具为按钮添加消息响应函数的方法,本书在6.3.2的第5节已经作了介绍,这里不再赘述。该函数实现将候选列表里的字段送到选用字段列表里,它的实现代码如下:
UpdateData();
CString strFieldName;
UINT nSel = m_LBFieldCandi.GetCurSel();
if(nSel == LB_ERR) return;
int nSelected;
m_LBFieldCandi.GetText(nSel, strFieldName);
nSelected = m_LBSelected.AddString(strFieldName);
m_LBFieldCandi.DeleteString(nSel);
m_saFieldTitle.Add(strFieldName);
UpdateData(FALSE);
代码首先使用UpdateData()函数将界面上输入的变量内容保存到变量里,然后取m_LBFieldCandi控件的当前选择项索引,并通过该索引将项的文本保存到strFieldName变量里,接下来将该文本作为LBSelected`控件的一个项添加进去,并删除m_LBFieldCandi控件的选择项。变量m_saFieldTitle是存放字段标题的CstringArray类型变量,这里需要为该变量添加一个新元素。最后再次调用UpdateData()函数将变量内容显示到界面上。
(5) 编写选择按钮“<”的响应函数OnDeselect()
同样使用ClassWizard工具为ID_DESELECT按钮添加消息响应函数OnDeselect(),该函数将选用字段列表里的选择项“删除”并送回候选字段列表里,它的实现代码如下:
UpdateData();
CString strFieldName;
UINT nSel = m_LBSelected.GetCurSel();
if(nSel == LB_ERR) return;
int nDeselected;
m_LBSelected.GetText(nSel, strFieldName);
nDeselected = m_LBFieldCandi.AddString(strFieldName);
m_LBSelected.DeleteString(nSel);
int nArrayCount = m_saFieldTitle.GetSize();
if(nDeselected < nArrayCount) m_saFieldTitle.RemoveAt(nDeselected);
m_strFieldTitle.Empty();
UpdateData(FALSE);
代码同OnSelect()函数大致相似,不同之处是,将m_LBSelected列表里的选择项删除,并将该项恢复到m_LBFieldCandi列表里,同时需要将m_saFieldTitle变量里相应的项删除。
(6) 编写选择按钮“>|”的响应函数OnSelectall ()
该函数将候选字段里的所有字段送到选用字段列表里,它的实现代码如下:
UpdateData();
CString strFieldName;
UINT nCount = m_LBFieldCandi.GetCount();
if(nCount <= 0) return;
for(UINT iIdx = 0; iIdx
strFieldName.Empty();
m_LBFieldCandi.GetText(0, strFieldName);
m_LBSelected.AddString(strFieldName);
m_LBFieldCandi.DeleteString(0);
m_saFieldTitle.Add(strFieldName);
}
UpdateData(FALSE);
代码同OnSelect()函数大致相似,不同之处是,使用循环将所有候选字段送到选用字段列表里。
(7) 编写选择按钮“|<”的响应函数OnDeselectall ()
该函数将选用字段里的所有字段恢复到候选字段列表里,它的实现代码如下:
UpdateData();
CString strFieldName;
UINT nCount = m_LBSelected.GetCount();
if(nCount <= 0) return;
for(UINT iIdx = 0; iIdx
strFieldName.Empty();
m_LBSelected.GetText(0, strFieldName);
m_LBFieldCandi.AddString(strFieldName);
m_LBSelected.DeleteString(0);
}
m_saFieldTitle.RemoveAll();
m_strFieldTitle.Empty();
UpdateData(FALSE);
代码同OnDeselect()函数大致相似,不同之处是,使用循环将所有选用字段恢复到候选字段列表里。
(8) 为IDC_LISTSEL列表控件编写LBN_SELCHANGE消息响应函数OnSelchangeListsel(),该函数在IDC_LISTSEL列表控件选择项发生改变时执行,它的执行代码如下:
UpdateData();
UINT nSel = m_LBSelected.GetCurSel();
if(nSel == LB_ERR) return;
UINT nArrayCount = m_saFieldTitle.GetSize();
if(nSel >= nArrayCount) return;
CString strFieldTitle;
strFieldTitle = m_saFieldTitle.GetAt(nSel);
m_strFieldTitle = strFieldTitle;
m_uSelIndex = nSel;
UpdateData(FALSE);
代码通过取得选用字段的索引,从字段标题变量m_saFieldTitle里读取文本,并显示在IDC_FIELDTITLE编辑控件里。
(9) 为IDC_FIELDTITLE编辑控件编写EN_KILLFOCUS消息响应函数OnKillfocusFieldtitle (),该函数在IDC_FIELDTITLE编辑控件失去输入焦点时执行,它的执行代码如下:
UpdateData();
UINT nArrayCount = m_saFieldTitle.GetSize();
if(m_uSelIndex < nArrayCount)
m_saFieldTitle.SetAt(m_uSelIndex, m_strFieldTitle);
代码将控件当前内容保存到字段标题变量m_saFieldTitle里。
(10) 为IDC_CHKPAGENUM复选控件编写BN_CLICKED消息响应函数OnChkpagenum (),该函数在IDC_CHKPAGENUM复选控件被单击时执行,它的执行代码如下:
UpdateData();
if(m_fUsePageNum){
GetDlgItem(IDC_PAGECAP)->EnableWindow(TRUE);
GetDlgItem(IDC_CBPNFORMAT)->EnableWindow(TRUE);
}
else{
GetDlgItem(IDC_PAGECAP)->EnableWindow(FALSE);
GetDlgItem(IDC_CBPNFORMAT)->EnableWindow(FALSE);
}
代码在控件被选中时将IDC_PAGECAP标签和IDC_CBPNFORMAT列表框设置为有效,否则相反。
(11) 编写“下一步”按钮的消息响应函数OnWizardNext()
该函数在单击了“下一步”按钮时执行,它的执行代码如下:
UpdateData();
CReportWizard *psheet = (CReportWizard *) GetParent();
psheet->m_strReportTitle = m_strReportTitle;
psheet->m_strReportFooter = m_strReportFooter;
psheet->m_fHavePage = m_fUsePageNum;
psheet->m_uPageType = m_CBPageType.GetCurSel();
CString strFieldName;
for(int i=0;i
m_LBSelected.GetText(i, strFieldName);
psheet->m_saSelectedFields.Add(strFieldName);
}
for(i=0;i
psheet->m_saSelectedFieldsTitle.Add(m_saFieldTitle.GetAt(i));
代码将当前属性页里的设置保存到CReportWizard类的相应变量里。
· 编写CReportWzdFormat类的实现代码
(1) 首先创建类里与对话框中控件相关联的变量,这些变量可以通过ClassWizard工具添加, 如表6-15所示,为添加变量的名称、类型、关联的控件ID及其意义。
表6-15 CReportWzdFormat类的变量
名称 |
类型 |
关联的控件ID |
意义 |
m_CBFontTitle |
CFontCombo |
IDC_CBTITLEFONT |
标题字体列表 |
m_CBFontContentHead |
CFontCombo |
IDC_CBHEADFONT |
字段标题字体列表 |
m_CBFontContent |
CFontCombo |
IDC_CBNORMALFONT |
正文字体列表 |
m_CBFontFooter |
CFontCombo |
IDC_CBFOOTERFONT |
注脚字体列表 |
CFontCombo类是一个第三方开发类,它的声明代码以及实现代码分别在文件FontCombo.h和FontCombo.cpp里,我们只要将这两个文件添加到工程里就可以使用。使用时,需要在CFontCombo类使用的地方加入头文件,有如下代码:
#include "FontCombo.h"
(2) 编写OnSetActive()消息响应函数
该函数在属性页被激活时执行,需要该属性页的向导按钮显示出“上一步”和“下一步”,在该函数里添加如下代码:
CPropertySheet* psheet = (CPropertySheet*) GetParent();
psheet->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);
(3) 编写属性页的OnInitDialog()函数
该函数实现本属性页的初始化操作。初始化操作包括:四个字体列表框的初始化,四个字号大小组合框的初始化,实现代码如下:
m_CBFontTitle.SubclassDlgItem(IDC_CBTITLEFONT, this);
m_CBFontContentHead.SubclassDlgItem(IDC_CBHEADFONT, this);
m_CBFontContent.SubclassDlgItem(IDC_CBNORMALFONT, this);
m_CBFontFooter.SubclassDlgItem(IDC_CBFOOTERFONT, this);
m_CBFontFooter.Initialize();
m_CBFontContentHead.Initialize();
m_CBFontContent.Initialize();
m_CBFontTitle.Initialize();
int nIndex = 0;
nIndex = m_CBFontFooter.FindStringExact(0, "宋体");
m_CBFontFooter.SetCurSel(nIndex);
nIndex = m_CBFontContentHead.FindStringExact(0, "宋体");
m_CBFontContentHead.SetCurSel(nIndex);
nIndex = m_CBFontContent.FindStringExact(0, "宋体");
m_CBFontContent.SetCurSel(nIndex);
nIndex = m_CBFontTitle.FindStringExact(0, "宋体");
m_CBFontTitle.SetCurSel(nIndex);
((CComboBox *)GetDlgItem(IDC_CBTITLESIZE))->SetCurSel(0);
((CComboBox *)GetDlgItem(IDC_CBHEADSIZE))->SetCurSel(0);
((CComboBox *)GetDlgItem(IDC_CBNORMALSIZE))->SetCurSel(0);
((CComboBox *)GetDlgItem(IDC_CBFOOTERSIZE))->SetCurSel(0);
(4) 为IDC_CBTITLEFONT,(IDC_CBNORMALFONT,IDC_CBHEADFONT,IDC_CBFOOTERFONT,IDC_CBTITLESIZE,IDC_CBNORMALSIZE,IDC_ CBFOOTERSIZE和IDC_CBHEADSIZE组合控件编写CBN_SELCHANGE消息响应函数OnSelchangeCbTitlefont(),OnSelchangeCbNormalfont (),OnSelchangeCbheadfont (),OnSelchangeCbFooterfont (),OnSelchangeCbTitlesize (),OnSelchangeCbNormalsize (),OnSelchangeCbFootersize (),OnSelchangeCbheadsize (),这些函数在组合控件选择项发生改变时执行,都是调用RefreshDisplay()函数刷新预览区域的显示,执行代码相同,如下:
UpdateData();
RefreshDisplay();
(5) 编写RefreshDisplay()函数
该函数根据八个列表框里字体和字体大小的选择,重新显示预览内容,执行代码如下:
// 取显示设备环境DC
CDC *pDC = GetDlgItem(IDC_PREVIEWAREA)->GetDC();
if(!pDC) return;
// 填充区域
CRect rect;
GetDlgItem(IDC_PREVIEWAREA)->GetClientRect(rect);
CBrush brush;
brush.CreateSolidBrush(RGB(255,255,255));
pDC->FillRect(rect, &brush);
// 显示
CString line; // 显示内容
TEXTMETRIC metrics; //Font measurements
int y = 0; //Current y position on report
// 创建字体
CFont TitleFont; //Font for Title
CFont HeadingFont; //Font for headings
CFont DetailFont; //Font for detail lines
CFont FooterFont; //Font for footer lines
// 设置TAB间隔
int TabStops[] = {100, 275, 650};
CString strFontName;
CString strSize;
UINT uFontSize;
CComboBox *pSizeCB;
// 为标题设置粗体
m_CBFontTitle.GetLBText(m_CBFontTitle.GetCurSel(), strFontName);
pSizeCB = (CComboBox *)GetDlgItem(IDC_CBTITLESIZE);
pSizeCB->GetLBText(pSizeCB->GetCurSel(), strSize);
uFontSize = atoi(strSize);
TitleFont.CreateFont(uFontSize, 0, 0, 0, FW_BOLD, FALSE, TRUE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
strFontName);
// 为字段标题设置粗体和下划线
m_CBFontContentHead.GetLBText(m_CBFontContentHead.GetCurSel(),
strFontName);
pSizeCB = (CComboBox *)GetDlgItem(IDC_CBHEADSIZE);
pSizeCB->GetLBText(pSizeCB->GetCurSel(), strSize);
uFontSize = atoi(strSize);
HeadingFont.CreateFont(uFontSize, 0, 0, 0, FW_BOLD, FALSE, TRUE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
strFontName);
// 正文字体
m_CBFontContent.GetLBText(m_CBFontContent.GetCurSel(), strFontName);
pSizeCB = (CComboBox *)GetDlgItem(IDC_CBNORMALSIZE);
pSizeCB->GetLBText(pSizeCB->GetCurSel(), strSize);
uFontSize = atoi(strSize);
DetailFont.CreateFont(uFontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
strFontName);
// 注脚字体
m_CBFontFooter.GetLBText(m_CBFontFooter.GetCurSel(), strFontName);
pSizeCB = (CComboBox *)GetDlgItem(IDC_CBFOOTERSIZE);
pSizeCB->GetLBText(pSizeCB->GetCurSel(), strSize);
uFontSize = atoi(strSize);
FooterFont.CreateFont(uFontSize, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
strFontName);
// 开始显示
CFont* OldFont = pDC->SelectObject(&TitleFont);
// 取显示文本尺寸
pDC->GetTextMetrics(&metrics);
// 计算高度
int LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
// 显示标题
y = 2;
pDC->TextOut(rect.Width()/2 - 70, y, "标 题");
// 显示列标题
pDC->GetTextMetrics(&metrics);
LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
y += LineHeight + 1; //Adjust y position
pDC->SelectObject(&HeadingFont);
line.Format("%s\t%s\t%s","列1","列2","列3");
pDC->TabbedTextOut(10, y, line, 2, TabStops, 0);
// 显示正文
pDC->GetTextMetrics(&metrics);
LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
y += LineHeight + 1; //Adjust y position
pDC->SelectObject(&DetailFont);
line.Format("%s\t%s\t%s", "值1","值2","值3");
pDC->TabbedTextOut(10, y, line, 2, TabStops, 0);
// 显示注脚
pDC->GetTextMetrics(&metrics);
LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
y += LineHeight + 1; //Adjust y position
pDC->SelectObject(&FooterFont);
line = _T("注脚");
pDC->TextOut(rect.Width() - 40, y, line);
// 恢复设备环境里的字体
pDC->SelectObject(OldFont);
上述代码首先取得显示的设备环境的显示尺寸,然后,用白色将显示区域填充;接着创建四种字体,并分别用四种字体显示与预览文本,最后恢复显示设备环境里的字体信息。这里要注意的是,TextOut()函数值将文本简单显示在指定区域,而TabbedTextOut ()函数可以显示TAB文本,即在文本里插入了TAB字符。GetTextMetrics()函数用于取得当前显示设备环境的文字尺寸。
(6) 编写“下一步”按钮的消息响应函数OnWizardNext()
该函数在单击“下一步”按钮时执行,它的执行代码如下:
UpdateData();
CReportWizard *psheet = (CReportWizard *) GetParent();
// 保存字体信息
CString strFontName;
m_CBFontTitle.GetLBText(m_CBFontTitle.GetCurSel(), strFontName) ;
psheet->m_strFontTitle = strFontName;
m_CBFontContentHead.GetLBText(m_CBFontContentHead.GetCurSel(),
strFontName) ;
psheet->m_strFontContentHead = strFontName;
m_CBFontContent.GetLBText(m_CBFontContent.GetCurSel(), strFontName) ;
psheet->m_strFontContent = strFontName;
m_CBFontFooter.GetLBText(m_CBFontFooter.GetCurSel(), strFontName) ;
psheet->m_strFontFooter = strFontName;
// 保存字体大小信息
UINT uFontSize;
CString strSize;
CComboBox *pCombo;
pCombo = (CComboBox *)GetDlgItem(IDC_CBTITLESIZE);
pCombo->GetLBText(pCombo->GetCurSel(), strSize);
uFontSize = atoi(strSize);
psheet->m_uSizeTitle = uFontSize;
pCombo = (CComboBox *)GetDlgItem(IDC_CBHEADSIZE);
pCombo->GetLBText(pCombo->GetCurSel(), strSize);
uFontSize = atoi(strSize);
psheet->m_uSizeContentHead = uFontSize;
pCombo = (CComboBox *)GetDlgItem(IDC_CBNORMALSIZE);
pCombo->GetLBText(pCombo->GetCurSel(), strSize);
uFontSize = atoi(strSize);
psheet->m_uSizeContent = uFontSize;
pCombo = (CComboBox *)GetDlgItem(IDC_CBFOOTERSIZE);
pCombo->GetLBText(pCombo->GetCurSel(), strSize);
uFontSize = atoi(strSize);
psheet->m_uSizeFooter = uFontSize;
该函数用于保存设定的字体信息。
· 编写CReportWzdPreview类的实现代码
(1) 首先创建类里与对话框中控件相关联的变量,这些变量通过ClassWizard工具添加,如表6-16所示,为添加变量的名称、类型、关联的控件ID及其意义。
表6-16 CReportWzdPreview类的变量
名称 |
类型 |
关联的控件ID |
意义 |
m_strReportInfo |
CString |
IDC_REPORTINFO |
报表设置信息显示 |
(2) 编写OnSetActive()消息响应函数
该函数在属性页被激活时执行,将该属性页的向导按钮显示出“上一步”和“完成”,同时还要显示报表设置信息。我们需要在该函数里添加如下代码:
// 设置属性页的向导按钮
CReportWizard *psheet = (CReportWizard *) GetParent();
psheet->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH);
// 显示报表设置信息
CString strReportFields;
strReportFields.Empty();
for(int i=0;i
strReportFields += psheet->m_saSelectedFields.GetAt(i);
if(i+1 < psheet->m_saSelectedFields.GetSize())
strReportFields += _T("、");
}
CString strPageInfo;
if(psheet->m_fHavePage){
strPageInfo = _T("使用页码,");
switch(psheet->m_uPageType){
case 0: strPageInfo += _T("底部左对齐"); break;
case 1: strPageInfo += _T("底部左居中"); break;
case 2: strPageInfo += _T("底部右对齐"); break;
}
}
else strPageInfo = _T("不使用页码");
m_strReportInfo.Format("您设置的报表格式如下:\n报表标题: %s, \
字体名称: %s, 字体大小: %d\n\
报表字段: %s, 字体名称: %s, \
字体大小: %d\n报表注脚: %s, \
字体名称: %s, 字体大小: %d\n\n%s",
psheet->m_strReportTitle,
psheet->m_strFontTitle,
psheet->m_uSizeTitle,
strReportFields,psheet->m_strFontContent,
psheet->m_uSizeContent,
psheet->m_strReportFooter,psheet->m_strFontFooter,
psheet->m_uSizeFooter,
strPageInfo);
UpdateData(FALSE);
· 编写“完成”按钮消息响应函数OnWizardFinish()
该函数在“完成”按钮按下时执行,需要保存所有设置到CODBCDemo2View类的相应变量里。在该函数里添加如下代码:
CMainFrame *pMainFrm = (CMainFrame *)AfxGetMainWnd();
CODBCDemo2View *pView = \
(CODBCDemo2View *)pMainFrm->GetActiveView();
int nCount = psheet->m_saSelectedFields.GetSize();
for(int i=0;i
pView->m_saSelectedFields.Add(psheet->m_saSelectedFields.GetAt(i));
pView->m_saSelectedFieldsTitle.Add(
psheet->m_saSelectedFieldsTitle.GetAt(i));
}
pView->m_fHavePage = psheet->m_fHavePage;
pView->m_uPageType = psheet->m_uPageType;
pView->m_strReportTitle = psheet->m_strReportTitle;
pView->m_strFontTitle = psheet->m_strFontTitle;
pView->m_uSizeTitle = psheet->m_uSizeTitle;
pView->m_strFontContentHead = psheet->m_strFontContentHead;
pView->m_uSizeContentHead = psheet->m_uSizeContentHead;
pView->m_strFontContent = psheet->m_strFontContent;
pView->m_uSizeContent = psheet->m_uSizeContent;
pView->m_strReportFooter = psheet->m_strReportFooter;
pView->m_strFontFooter = psheet->m_strFontFooter;
pView->m_uSizeFooter = psheet->m_uSizeFooter;
· 编写报表代码
报表代码在菜单项“报表>打印预览”和“报表>打印”里执行,这两个命令都调用OnPrint()函数,我们在该函数里加入如下代码:
OutputReport(pDC, pInfo);
OutputReport()函数负责报表信息的打印,它的实现代码同前面的RefreshDisplay()函数类似,不同的是,此函数是在打印设备环境下运行的,函数在CODBCDemo2View类里的声明代码如下:
public:
void OutputReport(CDC* pDC, CPrintInfo* pInfo=NULL);
13. 在ODBCDemo2View.cpp文件里,该函数的实现代码如下:
void CODBCDemo2View::OutputReport(CDC* pDC, CPrintInfo* pInfo)
{
// 检测结果集对象的有效性
if(!m_pCommonRS) return;
if(!m_pCommonRS->IsOpen()) return;
//
CString line;
TEXTMETRIC metrics;
int y = 0;
CFont TitleFont, HeadingFont, DetailFont, FooterFont;
int TabStops[] = {100, 400, 700, 1000, 1300, 1600,
1900, 2200, 2500, 2800, 3100};
int FooterTabStops[] = {350};
if (!pInfo || pInfo->m_nCurPage == 1) {
m_pCommonRS->Requery();
}
TitleFont.CreateFont(m_uSizeTitle, 0, 0, 0, FW_BOLD, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
m_strFontTitle);
HeadingFont.CreateFont(m_uSizeContentHead, 0, 0, 0,
FW_BOLD, FALSE, TRUE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_ROMAN,
m_strFontContentHead);
DetailFont.CreateFont(m_uSizeContent, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_ROMAN,
m_strFontContent);
FooterFont.CreateFont(m_uSizeFooter, 0, 0, 0, FW_NORMAL,
FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_ROMAN,
m_strFontFooter);
// 打印标题
CFont* OldFont = pDC->SelectObject(&TitleFont);
pDC->GetTextMetrics(&metrics);
int LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
y += LineHeight;
pDC->TextOut(200, 0, m_strReportTitle);
// 打印字段标题
pDC->SelectObject(&HeadingFont);
if(0
for(int i=0;i
line += m_saSelectedFieldsTitle.GetAt(i);
line += _T("\t");
}
line += m_saSelectedFieldsTitle.GetAt(i);
}
pDC->TabbedTextOut(0, y, line, m_saSelectedFieldsTitle.GetSize(),
TabStops, 0);
// 打印正文
LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
y += LineHeight;
pDC->SelectObject(&DetailFont);
pDC->GetTextMetrics(&metrics);
LineHeight = metrics.tmHeight + metrics.tmExternalLeading;
while (!m_pCommonRS->IsEOF()) {
if (pInfo && abs(y) > 1000) {
pInfo->SetMaxPage(pInfo->m_nCurPage + 1);
break;
}
line.Empty();
CString strValue;
if(0
for(int j=0;j
m_pCommonRS->GetFieldValue(m_saSelectedFields.GetAt(j),
strValue);
line += strValue;
line += _T("\t");
}
m_pCommonRS->GetFieldValue(m_saSelectedFields.GetAt(j), strValue);
line += strValue;
}
pDC->TabbedTextOut(0, y, line, m_saSelectedFields.GetSize(), TabStops, 0);
y += LineHeight; //Adjust y position
m_pCommonRS->MoveNext();
}
// 打印注脚
if (pInfo) {
pDC->SelectObject(&FooterFont);
line.Format("%s \tPage: %d \tJackie", m_strReportFooter,
pInfo->m_nCurPage);
pDC->TabbedTextOut(0, 1025, line, 2, FooterTabStops, 0);
}
// 恢复设备环境字体
pDC->SelectObject(OldFont);
}
现在我们完成了ODBCDemo2工程代码的编写,可以编译并运行应用程序了。
编译并运行ODBCDemo2工程的操作步骤:
(1) 执行“Build>Build ODBCDemo2.exe”菜单项,或者按下快捷键【F7】,VC++开始编译ODBCDemo2工程,产生ODBCDemo2.exe可执行程序。
(2) 执行“Build>Execute ODBCDemo2.exe”菜单项,或者按下快捷键【Ctrl】+【F5】,VC++开始运行ODBCDemo2.exe应用程序,启动界面如图6-21所示。
图6-21 ODBCDemo2.exe应用程序启动界面
(3) 执行“销售报告>按产品”菜单命令,运行结果如图6-22所示。
图6-22 执行“销售报告>按产品”菜单命令
(4) 执行“销售报告>按客户”菜单命令,运行结果如图6-23所示。
图6-23 执行“销售报告>按客户”菜单命令
(5) 执行“销售报告>按雇员”菜单命令,运行结果如图6-24所示。
图6-24 执行“销售报告>按雇员”菜单命令
(6) 执行“产品>产品信息”菜单命令,运行结果如图6-25所示。
图6-25 执行“产品>产品信息”菜单命令
(7) 执行“客户>客户信息”菜单命令,运行结果如图6-26所示。
图6-26 执行“客户>客户信息”菜单命令
(8) 执行“雇员>雇员信息”菜单命令,运行结果如图6-27所示。
图6-27 执行“雇员>雇员信息”菜单命令
(9) 执行“供应商>供应商信息”菜单命令,运行结果如图6-28所示。
图6-28 执行“供应商>供应商信息”菜单命令
(10) 执行“运货商>运货商信息”菜单命令,运行结果如图6-29所示。
图6-29 执行“运货商>运货商信息”菜单命令
(11) 执行“产品>产品信息”菜单命令,将产品信息显示在视图里。执行“报表>设置”菜单命令,报表设置向导开始执行第一步,报表字段选择。运行结果如图6-30所示。
图6-30 报表设置向导的第一步
(12) 在“报表标题”编辑区域输入“公司产品报表”,将所有字段选择到选用列表里,完成信息输入后,按下“下一步”按钮,报表设置向导开始执行第二步,报表格式设置。运行结果如图6-31所示。
图6-31 报表设置向导的第二步
(13) 选择所有字体为“宋体”,选择标题字体大小为48,选择列头字体大小为28,选择正文字体大小为28,选择注脚字体大小为18,完成信息设置后,按下“下一步”按钮,报表设置向导开始执行第三步,显示报表设置信息。运行结果如图6-32所示。
图6-32 报表设置向导的第三步
(14) 单击“完成”按钮,完成报表设置,返回系统界面。执行“报表>打印预览”菜单命令,将显示打印预览结果如图6-33所示。
(15) 单击“放大”按钮,将报表预览界面放大,如图6-34所示。
(16) 单击“打印”按钮,即可将报表内容打印出来。
(17) 执行“文件>退出”菜单命令,结束应用程序的运行。
图6-33 报表预览
图6-34 放大后的报表预览
本实例着重介绍了MFC的CRecordset类实现对数据库的操作,用到的主要函数有:
· Open(),打开结果集。
· Close(),关闭结果集。
· GetODBCFieldCount(),取得结果集的字段数。
· GetODBCFieldInfo(),取得结果集的字段信息。
· IsEOF(),判断结果集是否检索到末尾。
· MoveNext(),移动结果集游标到下一行。
· Requery(),再次请求结果集。
本实例另一个重点是介绍报表的生成,VC++生成报表没有现成的控件,不如VB容易,必须通过字符打印操作将信息显示在打印设备环境里。这里用到了CDC的两个重要的打印函数:
· TextOut(),将文本简单打印到指定位置。
· TabbedTextOut(),将文本格式打印到指定位置。
另外,本实例还演示了PropertySheet和PropertyPage的使用方法,对读者设置其信息会有帮助。
本实例源代码在随书光盘的code\ ODBCDemo2目录下。
本章介绍了MFC ODBC编程方法和过程。与ODBC API编程相比,MFC编程更适用于界面型数据库应用程序的开发,由于MFC的广泛支持,ODBC编程可以对数据进行很好地表示。然而MFC的CDatabase类和CRecordset类提供的数据库操作函数非常有限,支持的游标类型也很有限,限制了高效的数据库开发。从编程层次上,ODBC的MFC编程则属于高级编程。
转载:点击打开链接