MFC ODBC数据库操作编程(二)
5. 编写报表向导操作代码
报表向导操作包括三个步骤:报表字段的选择、格式的设置以及设置信息的确认,因此制作向导时需要三个对话框。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<nFieldCount;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<nCount; 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<nCount; 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.GetCount();i++){
m_LBSelected.GetText(i, strFieldName);
psheet->m_saSelectedFields.Add(strFieldName);
}
for(i=0;i<m_saFieldTitle.GetSize();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<psheet->m_saSelectedFields.GetSize();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<nCount;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<m_saSelectedFieldsTitle.GetSize()){
for(int i=0;i<m_saSelectedFieldsTitle.GetSize()-1;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<m_saSelectedFieldsTitle.GetSize()){
for(int j=0;j<m_saSelectedFields.GetSize()-1;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);
}
6.3.3 编译并运行ODBCDemo2工程
现在我们完成了ODBCDemo2工程代码的编写,可以编译并运行应用程序了。
(1) 执行“Build>Build ODBCDemo2.exe”菜单项,或者按下快捷键【F7】,VC++开始编译ODBCDemo2工程,产生ODBCDemo2.exe可执行程序。
(2) 执行“Build>Execute ODBCDemo2.exe”菜单项,或者按下快捷键【Ctrl】+【F5】,VC++开始运行ODBCDemo2.exe应用程序,启动界面如图6-21所示。
(3) 执行“销售报告>按产品”菜单命令,运行结果如图6-22所示。
(4) 执行“销售报告>按客户”菜单命令,运行结果如图6-23所示。
(5) 执行“销售报告>按雇员”菜单命令,运行结果如图6-24所示。
(6) 执行“产品>产品信息”菜单命令,运行结果如图6-25所示。
(7) 执行“客户>客户信息”菜单命令,运行结果如图6-26所示。
(8) 执行“雇员>雇员信息”菜单命令,运行结果如图6-27所示。
(9) 执行“供应商>供应商信息”菜单命令,运行结果如图6-28所示。
(10) 执行“运货商>运货商信息”菜单命令,运行结果如图6-29所示。
(11) 执行“产品>产品信息”菜单命令,将产品信息显示在视图里。执行“报表>设置”菜单命令,报表设置向导开始执行第一步,报表字段选择。运行结果如图6-30所示。
(12) 在“报表标题”编辑区域输入“公司产品报表”,将所有字段选择到选用列表里,完成信息输入后,按下“下一步”按钮,报表设置向导开始执行第二步,报表格式设置。运行结果如图6-31所示。
(14) 单击“完成”按钮,完成报表设置,返回系统界面。执行“报表>打印预览”菜单命令,将显示打印预览结果如图6-33所示。
(15) 单击“放大”按钮,将报表预览界面放大,如图6-34所示。
6.3.4 ODBCDemo2实例小结
本实例着重介绍了MFC的CRecordset类实现对数据库的操作,用到的主要函数有:
· GetODBCFieldCount(),取得结果集的字段数。
· GetODBCFieldInfo(),取得结果集的字段信息。
本实例另一个重点是介绍报表的生成,VC++生成报表没有现成的控件,不如VB容易,必须通过字符打印操作将信息显示在打印设备环境里。这里用到了CDC的两个重要的打印函数:
· TabbedTextOut(),将文本格式打印到指定位置。
另外,本实例还演示了PropertySheet和PropertyPage的使用方法,对读者设置其信息会有帮助。
本实例源代码在随书光盘的code\ ODBCDemo2目录下。
6.4 本 章 小 结
MFC ODBC数据库操作编程(二)相关推荐
- 【MFC】数据库操作——ODBC(20)
ODBC:开放式数据库连接,是为解决异构数据库(不同数据库采用的数据存储方法不同)共享而产生的.ODBC API相对来说非常复杂,这里介绍MFC的ODBC类. 添加ODBC用户DSN 首先,在计算机中 ...
- odbc c语言 linux,C语言ODBC数据库操作
今天我们来介绍一下C语言操作数据库的方法,这里我们使用的是ODBC方式.环境是WIN7+VC6.其他环境也差不多,具体情况具体分析. 首先是环境的配置以及数据源的添加.这里就不去解释了,相关资料网上有 ...
- C语言ODBC数据库操作
今天我们来介绍一下C语言操作数据库的方法,这里我们使用的是ODBC方式.环境是WIN7+VC6.其他环境也差不多,具体情况具体分析. 首先是环境的配置以及数据源的添加.这里就不去解释了,相关资料网上有 ...
- 数据基础---mysql数据库操作(二)---JOIN用法
mysql的john用法总结 左"的笛卡尔积和"右"的笛卡尔积 根据mysql join 连接的方式我把它归为两类,"左"的笛卡尔积和"右& ...
- VS2017中SQLite配置和引用和数据库操作编程
环境配置:windows 64,VS,SQLite(点击下载),System.Data.SQLite.DLL(点击下载). 一.新建项目,添加引用 1.在VS中新建一个控制台应用程序,如下图 2.添加 ...
- Qt 数据库操作(二)
文章目录 执行SQL语句 1.执行一个查询 2.浏览结果集 3.插入.更新和删除记录 4.事务 执行SQL语句 1.执行一个查询 QSqlQuery类提供了一个接口,用于执行SQL语句和浏览查询的结果 ...
- VS2017+MFC+sqlite数据库操作
创建数据库 1.用navicat创建sqlite数据库,名称为student 2.填写数据库内容 VS2017MFC操作 1.添加以下类及配置文件到程序文件夹中 并在CDxxxxDlg程序中添加SDD ...
- Python数据库操作【三】—— SQLServer
SQLServer简介 以下摘自百度百科: SQL Server是由Microsoft开发和推广的关系数据库管理系统(DBMS),它最初是由Microsoft.Sybase和Ashton-Tate三家 ...
- java数据库编程——执行查询操作(二)
[0]README 1) 本文部分文字描述和source code 均转自 core java volume 2 , 旨在理解 java数据库编程--执行查询操作(二) 的基础知识 : 2) 本文和 ...
最新文章
- netty 进程挂起_这可能是目前最透彻的Netty原理架构解析
- ros(7)自定义service数据
- window中使用jedis连接虚拟机中的redis
- vue3,vite2,json数据通过拼接显示链接src
- 之前跳槽面试时整理的一些知识点
- css两列等高,实现一个两列等高布局,讲讲思路
- c语言 倒计时不清屏_C语言实现清屏操作
- 基于SPringBoot的药品管理系统
- 下载SVN上的文件到本地
- Faster RCNN论文详解
- 5G网络切片安全隔离机制与应用
- Linux服务器命令
- 北京/苏州内推 | 微软亚洲互联网工程院招聘NLP算法工程师(可实习)
- 矩阵键盘——按下矩阵按键,数码管显示出位置对应的字符(0123 4567 89Ab CdEF)
- 【HTMLCSS】运维、后端你该会的前端基本内容
- 一发入魂的音乐,如何来到你的耳边?
- 电脑硬件升级完全解决方案-1
- 《计算机网络原理》问答
- 等面积圆柱投影的证明
- JS实现腾讯地图搜索信息下拉以及定位...
热门文章
- UI设计师未来的发展趋势如何?前景好吗?
- CRM客户关系管理能给企业带来什么样的效果
- Python 小提琴图(violinplot)
- java微信小程序授权微信登录获取手机号
- python安装random_如何安装python randon
- 基于Docker离线部署开源视频会议系统Jitsi-Meet
- 如何制作电子画册?教你快速上手 | 云展网
- “碳排放”挑战当前,维谛技术(Vertiv)带来节能改造最佳实践
- CSS揭秘:5.条纹背景(上)
- Pegatron公司SOP管理系统的规划和实施研究(C#)