mfc对话框显示表格有那么几种方法:使用微软的CListCtrl控件,使用Joe和chris编写的GridCtrl控件等。
GridCtrl更专业些。这款控件出世将近20年,使用也很广泛。但是用于显示SQL表格的帮助文件较少。在网上找到10年前Mazdak写的教程,尝试并实现,对他表示感谢。
Mazdak-Binding MFC Grid to database
一、首先介绍GridCtrl应用于简单表格的建立。
1、新建MFC对话框 IDD_DataOut,类名CDataOut,生成DataOut.h和DataOut.cpp;
2、将GridCtrl.cpp、GridCtrl.h等文件加到工程中,并编译
添加GridCtrl_src下文件后,VS2010编译报错:
gridctrltest\memdc.h(26): error C2011: “CMemDC”:“class”类型重定义
解决办法重命名CMemDC为GCMemDC,记得同时修改MemDC.h及GridCtrl.cpp所有用到的CMemDC项。
编译通过。
3、在对话框IDD_DataOut中增加Custom Control,如下
控件属性如下,Class项要写MFCGridCtrl
4、在文件DataOut.h中增加成员
#pragma once
#include "GridCtrl.h"
class CDataOut : public CDialogEx
{
DECLARE_DYNAMIC(CDataOut)
public:
CDataOut(CWnd* pParent = NULL); // 标准构造函数
virtual ~CDataOut();
CGridCtrl m_pGrid;
void GridCtrlInit();
// 对话框数据
enum { IDD = IDD_DataOut };
5、在DataOut.cpp文件里的函数DoDataExchange增加映射:
void CDataOut::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_datagrid, m_pGrid);
}
6、在对话框初始化函数中实现控件的显示,若没有控件显示程序,整个对话框都无法显示,这点困扰了我好长时间。
BOOL CDataOut::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
GridCtrlInit();//调用函数
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
调用函数如下:
void CDataOut::GridCtrlInit()
{
m_pGrid.SetEditable(true);
m_pGrid.SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黄色背景
m_pGrid.SetRowCount(8); //初始为10行
m_pGrid.SetColumnCount(8); //初始化为11列
m_pGrid.SetFixedRowCount(1); //表头为一行
m_pGrid.SetFixedColumnCount(1); //表头为一列
for (int row = 0; row < m_pGrid.GetRowCount(); row++)
for (int col = 0; col < m_pGrid.GetColumnCount(); col++)
{
//设置表格显示属性
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
m_pGrid.SetRowHeight(row,25); //设置各行高
m_pGrid.SetColumnWidth(0,64); //设置0列宽
m_pGrid.SetColumnWidth(col,64); //设置各列宽
if(row==0&&col==0) //第(0,0)格
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.strText.Format(_T("报表显示"),col);
}
else if (row < 1) //设置0行表头显示
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.strText.Format(_T(" 项目%d"),col);
}
else if (col < 1) //设置0列表头显示
{
if(row< m_pGrid.GetRowCount())
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.strText.Format(_T("第%d次"),row);
}
}
else
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.strText.Format(_T(""),2);
}
m_pGrid.SetItem(&Item);
}
}
二、显示SQL表格并复制某些行记录
前三步骤同上。
4、在DataOut.h中
#include "GridCtrl_src\GridCtrl.h"
#include "math.h"
// CDataOut 对话框
class CDataOut : public CDialogEx
{
DECLARE_DYNAMIC(CDataOut)
public:
CDataOut(CWnd* pParent = NULL); // 标准构造函数
virtual ~CDataOut();
// CDataOut* m_dataout;
void GridCtrlInit();
// 对话框数据
enum { IDD = IDD_DataOut };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
HICON m_hIcon;
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
CGridCtrl m_pGrid;
CString m_strDataSource;
CString m_strQuery;
int FillGrid(void);
_RecordsetPtr pRecordset;
private:
void CheckVariant(_variant_t vtFld,int propType);
double dval;
long lval;
CY cyVal;
CList<int,int> m_listIntegerCol;
CList<int,int> m_listDateCol;
_bstr_t str;
COleDateTime date;
BOOL bdate;
BOOL bCY;
BOOL blong;
BOOL bfloat;
public:
afx_msg void OnBnClickedCopy();
};
第5步同上文
6、ADO方式连接数据库,简单介绍如下:
在stdafx.h中加入:#import “c:/program files/common files/system/ado/msado15.dll” no_namespace rename (“EOF”, “adoEOF”)
在项目头文件中添加
_ConnectionPtr m_pConnection; //+ ADO连接变量指针
在项目cpp文件中添加
CoInitialize(NULL); //初始化Com组件
m_pConnection.CreateInstance(__uuidof(Connection));
try
{
m_pConnection->Open("Provider=SQLOLEDB.1; Data Source=10.**.**.**\\SQLEXPRESS,1433;Database=maintenance;Persist Security Info=False;UID=sa;PWD=123456;","","",NULL);//连接字符串
}
catch(_com_error e) //捕捉异常
{
AfxMessageBox(e.ErrorMessage());
}
CoUninitialize(); //释放com组件
具体mfc连接sql中注意问题和连接字符串相关内容,后续补充补充内容链接。
然后在DataOut.cpph中调用Fillgrid()函数
BOOL CDataOut::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// CDataOut m_gridctrl;//定义对象
FillGrid();//调用函数
// GridCtrlInit();
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
int CDataOut::FillGrid(void)
{
CString sqlStr;
sqlStr = "select * from maintenance_record_tb";
//+打开数据库
pRecordset.CreateInstance(__uuidof(Recordset));
try
{
pRecordset->Open((_variant_t)sqlStr,
theApp.m_pConnection.GetInterfacePtr(),
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
m_pGrid.DeleteAllItems();
m_pGrid.SetListMode(true);
m_pGrid.SetHeaderSort(true);
try
{
FieldsPtr fs = pRecordset->GetFields();
int iCol = fs->Count+1;
m_pGrid.SetColumnCount(iCol);
m_pGrid.SetFixedRowCount();
m_pGrid.SetFixedColumnCount();
m_pGrid.SetColumnWidth(0,20);
iCol = m_pGrid.GetColumnCount();
for(int i = 0 ; i < (int)fs->GetCount() ; i++)
{
_variant_t index;
index.Clear();
index.vt = VT_I2;
index.iVal = i ;
CString str = (LPSTR) fs->GetItem(index)->GetName();
m_pGrid.SetItemText(0,i+1,str);
}
int iRow = 1;
while(!pRecordset->adoEOF)
{
m_pGrid.InsertRow("");
for(int i = 0 ; i < (int)fs->GetCount() ; i++)
{
_variant_t index;
index.Clear();
index.vt = VT_I2;
index.iVal = i ;
_variant_t vtFld;
vtFld.Clear();
vtFld =pRecordset->Fields->GetItem(fs->GetItem(index)->GetName())->Value;
int propType = (int)fs->GetItem(index)->GetType();
bdate=false;
blong = false;
bfloat = false;
bCY= false;
CheckVariant(vtFld,propType);//下文实现
if(blong!=false || bfloat!=false)
{
CString str1;
if(blong)
{
m_pGrid.SetCellType(iRow,i+1, RUNTIME_CLASS(CGridCell));
str1.Format("%d",lval);
}
else
str1.Format("%f",dval);
m_pGrid.SetItemText(iRow,i+1,str1);
m_listIntegerCol.AddTail((i+1));
}
else if(bdate)
{
CString str1 = date.Format(_T("%d/ %m/ %Y"));
m_listDateCol.AddTail(i+1);
m_pGrid.SetItemText(iRow,i+1,str1);
}
else if(bCY)
{
COleCurrency cur = (COleCurrency)cyVal;
m_listIntegerCol.AddTail((i+1));
m_pGrid.SetItemText(iRow,i+1,cur.Format());
}
else
{
m_pGrid.SetItemText(iRow,i+1,str);
str = "";
}
}
iRow++;
pRecordset->MoveNext();
}
}
catch(_com_error e)
{
AfxMessageBox(e.ErrorMessage());
return E_FAIL;
}
return 0;
}
CheckVariant()函数
void CDataOut::CheckVariant(_variant_t vtFld, int propType)
{
switch(vtFld.vt)
{
case VT_CY:
{
cyVal = vtFld.cyVal;
bCY = true;
break;
}
case VT_R4:
{
dval = vtFld.fltVal;
bfloat=true;
break;
}
case VT_R8:
{
dval = vtFld.dblVal;
bfloat=true;
break;
}
case VT_DECIMAL:
{
if(adDecimal==propType || adNumeric==propType)
{
dval = vtFld.decVal.Lo32;
dval *= (vtFld.decVal.sign == 128)? -1 : 1;
dval /= pow(10.0, vtFld.decVal.scale);
bfloat=true;
}
else
{
lval = vtFld.decVal.Lo32;
lval *= (vtFld.decVal.sign == 128)? -1 : 1;
lval /= pow(10*1.0, vtFld.decVal.scale);
blong=true;
}
break;
}
case VT_UI1:
{
lval = vtFld.iVal;
blong=true;
break;
}
case VT_BOOL:
{
lval = vtFld.boolVal;
if(lval==-1)
lval = 1;
blong=true;
break;
}
case VT_I2:
{
lval = vtFld.iVal;
blong=true;
break;
}
case VT_I4:
{
lval = vtFld.lVal;
blong=true;
break;
}
case VT_INT:
{
lval = vtFld.intVal;
blong=true;
break;
}
case VT_DATE:
{
date = (COleDateTime)vtFld.date;
bdate = true;
break;
}
case VT_NULL:
case VT_EMPTY:
{
break;
}
default:
str = vtFld.bstrVal;
//break;
}
}
记录复制函数如下,可以选中内容复制到excel中
void CDataOut::OnBnClickedCopy()
{
// TODO: 在此添加控件通知处理程序代码
COleDataSource* pSource = m_pGrid.CopyTextFromGrid();
if (!pSource)
return;
pSource->SetClipboard();
MessageBox("复制成功,粘贴至别处","",0);
}
效果图: