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工程代码的编写,可以编译并运行应用程序了。

编译并运行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 放大后的报表预览

6.3.4 ODBCDemo2实例小结

本实例着重介绍了MFC的CRecordset类实现对数据库的操作,用到的主要函数有:

·       Open(),打开结果集。

·       Close(),关闭结果集。

·       GetODBCFieldCount(),取得结果集的字段数。

·       GetODBCFieldInfo(),取得结果集的字段信息。

·       IsEOF(),判断结果集是否检索到末尾。

·       MoveNext(),移动结果集游标到下一行。

·       Requery(),再次请求结果集。

本实例另一个重点是介绍报表的生成,VC++生成报表没有现成的控件,不如VB容易,必须通过字符打印操作将信息显示在打印设备环境里。这里用到了CDC的两个重要的打印函数:

·       TextOut(),将文本简单打印到指定位置。

·       TabbedTextOut(),将文本格式打印到指定位置。

另外,本实例还演示了PropertySheet和PropertyPage的使用方法,对读者设置其信息会有帮助。

本实例源代码在随书光盘的code\ ODBCDemo2目录下。

6.4 本 章 小 结

本章介绍了MFC ODBC编程方法和过程。与ODBC API编程相比,MFC编程更适用于界面型数据库应用程序的开发,由于MFC的广泛支持,ODBC编程可以对数据进行很好地表示。然而MFC的CDatabase类和CRecordset类提供的数据库操作函数非常有限,支持的游标类型也很有限,限制了高效的数据库开发。从编程层次上,ODBC的MFC编程则属于高级编程。

转载:点击打开链接

MFC ODBC数据库操作编程(二)相关推荐

  1. 【MFC】数据库操作——ODBC(20)

    ODBC:开放式数据库连接,是为解决异构数据库(不同数据库采用的数据存储方法不同)共享而产生的.ODBC API相对来说非常复杂,这里介绍MFC的ODBC类. 添加ODBC用户DSN 首先,在计算机中 ...

  2. odbc c语言 linux,C语言ODBC数据库操作

    今天我们来介绍一下C语言操作数据库的方法,这里我们使用的是ODBC方式.环境是WIN7+VC6.其他环境也差不多,具体情况具体分析. 首先是环境的配置以及数据源的添加.这里就不去解释了,相关资料网上有 ...

  3. C语言ODBC数据库操作

    今天我们来介绍一下C语言操作数据库的方法,这里我们使用的是ODBC方式.环境是WIN7+VC6.其他环境也差不多,具体情况具体分析. 首先是环境的配置以及数据源的添加.这里就不去解释了,相关资料网上有 ...

  4. 数据基础---mysql数据库操作(二)---JOIN用法

    mysql的john用法总结 左"的笛卡尔积和"右"的笛卡尔积 根据mysql join 连接的方式我把它归为两类,"左"的笛卡尔积和"右& ...

  5. VS2017中SQLite配置和引用和数据库操作编程

    环境配置:windows 64,VS,SQLite(点击下载),System.Data.SQLite.DLL(点击下载). 一.新建项目,添加引用 1.在VS中新建一个控制台应用程序,如下图 2.添加 ...

  6. Qt 数据库操作(二)

    文章目录 执行SQL语句 1.执行一个查询 2.浏览结果集 3.插入.更新和删除记录 4.事务 执行SQL语句 1.执行一个查询 QSqlQuery类提供了一个接口,用于执行SQL语句和浏览查询的结果 ...

  7. VS2017+MFC+sqlite数据库操作

    创建数据库 1.用navicat创建sqlite数据库,名称为student 2.填写数据库内容 VS2017MFC操作 1.添加以下类及配置文件到程序文件夹中 并在CDxxxxDlg程序中添加SDD ...

  8. Python数据库操作【三】—— SQLServer

    SQLServer简介 以下摘自百度百科: SQL Server是由Microsoft开发和推广的关系数据库管理系统(DBMS),它最初是由Microsoft.Sybase和Ashton-Tate三家 ...

  9. java数据库编程——执行查询操作(二)

    [0]README 1) 本文部分文字描述和source code 均转自 core java volume 2 , 旨在理解 java数据库编程--执行查询操作(二) 的基础知识 : 2) 本文和 ...

最新文章

  1. netty 进程挂起_这可能是目前最透彻的Netty原理架构解析
  2. ros(7)自定义service数据
  3. window中使用jedis连接虚拟机中的redis
  4. vue3,vite2,json数据通过拼接显示链接src
  5. 之前跳槽面试时整理的一些知识点
  6. css两列等高,实现一个两列等高布局,讲讲思路
  7. c语言 倒计时不清屏_C语言实现清屏操作
  8. 基于SPringBoot的药品管理系统
  9. 下载SVN上的文件到本地
  10. Faster RCNN论文详解
  11. 5G网络切片安全隔离机制与应用
  12. Linux服务器命令
  13. 北京/苏州内推 | 微软亚洲互联网工程院招聘NLP算法工程师(可实习)
  14. 矩阵键盘——按下矩阵按键,数码管显示出位置对应的字符(0123 4567 89Ab CdEF)
  15. 【HTMLCSS】运维、后端你该会的前端基本内容
  16. 一发入魂的音乐,如何来到你的耳边?
  17. 电脑硬件升级完全解决方案-1
  18. 《计算机网络原理》问答
  19. 等面积圆柱投影的证明
  20. JS实现腾讯地图搜索信息下拉以及定位...

热门文章

  1. UI设计师未来的发展趋势如何?前景好吗?
  2. CRM客户关系管理能给企业带来什么样的效果
  3. Python 小提琴图(violinplot)
  4. java微信小程序授权微信登录获取手机号
  5. python安装random_如何安装python randon
  6. 基于Docker离线部署开源视频会议系统Jitsi-Meet
  7. 如何制作电子画册?教你快速上手 | 云展网
  8. “碳排放”挑战当前,维谛技术(Vertiv)带来节能改造最佳实践
  9. CSS揭秘:5.条纹背景(上)
  10. Pegatron公司SOP管理系统的规划和实施研究(C#)