65.9K
CodeProject 正在变化。 阅读更多。
Home

MFC DataGrid

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2002 年 2 月 25 日

2分钟阅读

viewsIcon

261723

downloadIcon

7585

CDataGrid继承自CGridCtrl,并使用ADO访问数据库。

CDataGrid

之前我开始使用Visual C++操作数据库,但对MS DataGrid不满意。 我编写了自己的类CDataGrid,它继承自Chris Maunder的CGridCtrl,并使用ADO访问数据库。

示例应用程序

用法 - 分步指南

步骤1- 使用CDataGid

添加到StdAfx.h字符串。

 #import "c:\program files\common files\system\ado\msado15.dll" \
  no_namespace \
  rename ("EOF", "adoEOF")
 #include <afxtempl.h<afxtempl.h>><afxtempl.h>

在这个例子中,我使用了来自MS SQL 7、2000和Access的Northwind数据库。 你可以在Visual Studio 6.0的第1张光盘上找到nwind.mdb

接下来,你应该将DataGrid.hDataGrid.cpp添加到项目中。 你还需要将CGridCtrl文件添加到项目中。 你可以从这里下载最新版本的CGridCtrl。(本例使用CGridCtrl v 2.23)

ExString.hExString.cpp添加到项目中。

DDXFields.hDDXFields.cpp添加到项目中。

在这个例子中,我使用了这些类

我开发了CDataComboBox类,以便从组合框访问数据库。

如果你打算在你的项目中使用CDataComboBox,你应该将DataComboBox.hDataComboBox.cpp添加到项目中。

CDataGrid变量和连接添加到你的对话框头文件中

#include "DataGrid.h"

class CDataGrid_DemoDlg : public CResizableDialog
{
// Construction
public:

  CDataGrid m_Grid;
  _ConnectionPtr  m_pConnection;

在你的对话框的DoDataExchange中将网格窗口与C++对象关联起来

DDX_Control(pDX, IDC_GRID, m_Grid);

OnInitDialog中添加

  m_pConnection.CreateInstance(__uuidof(Connection)); //Create connection

  try
  {
    //Open connection
    //I prefer to use udl
    //m_pConnection->Open
    //  (L"File Name=C:\\Program Files\\Common Files\\"
    //  "System\\Ole DB\\Data Links\\nwind.udl", L"", L"", -1);
    m_pConnection->Open
      (L"Provider=Microsoft.Jet.OLEDB.4.0;"
      "Data Source=D:\\DATA\\Nwind.mdb", L"", L"", -1);     ......
    ......
  }
  catch(_com_error *e)
  {
    CString Error = e->ErrorMessage();
    AfxMessageBox(e->ErrorMessage());
  }
  catch(...)
  {

  }

  SetFields();

设置网格的字段并在SetFields()中执行查询。

void CDataGrid_DemoDlg::SetFields()
{
  int n;

  m_Grid.m_field.SetSize(2);
    
  n=0;
  m_Grid.m_field[n].Field=_T("ProductName");
  m_Grid.m_field[n].Caption=_T("Product");
  //m_Grid.m_field[n].With=75;
  m_Grid.m_field[n].Find=true;
  
  n=1;
  m_Grid.m_field[n].Field=_T("QuantityPerUnit");
  m_Grid.m_field[n].Caption=_T("QuantityPerUnit");
  //m_Grid.m_field[n].With=300;
  m_Grid.m_field[n].Find=true;
  
  ......
  ......

  m_Grid.Execute(m_pConnection,"ProductId","*","Products","",1);
}

因为我们使用CGridCtrl的虚拟模式,我们必须重写OnNotify函数。

BOOL CDataGrid_DemoDlg::OnNotify(WPARAM wParam, 
                         LPARAM lParam, LRESULT* pResult)
{
  // TODO: Add your specialized code here and/or call the base class
  if (wParam == (WPARAM)m_Grid.GetDlgCtrlID())
  {
    *pResult = 1;
    GV_DISPINFO *pDispInfo = (GV_DISPINFO*)lParam;
    m_Grid.SetValue(pDispInfo);
  }

  return CResizableDialog::OnNotify(wParam, lParam, pResult);
}

下面的截图向我们展示了DataGrid的工作情况。

步骤2- 使用CDataComboBox

DataCombo控件添加到对话框中,在样式表中选择Drop List类型。

CDataGrid变量添加到你的对话框头文件中

#include "DataComboBox.h"

class CDataGrid_DemoDlg : public CResizableDialog
{
// Construction
public:

  CDataComboBox m_cmbDep;
  CDataComboBox m_cmbBill;

在你的对话框的DoDataExchange中将组合框与C++对象关联起来

  DDX_Control(pDX, IDC_CMB_CAT, m_cmbCat);
  DDX_Control(pDX, IDC_CMB_SUP, m_cmbSup);

填充DataComboBoxDataComboBox有两种模式:BOUNDUNBOUND

在第3步中,我将向你展示如何使用UNBOUND模式。 但现在在OnInitDialog中使用BOUND模式填充DataComboBoxes。

  m_pConnection.CreateInstance(__uuidof(Connection)); //Create connection

  try
  {
    //Open connection
    //I prefer to use udl
    //m_pConnection->Open
    //  (L"File Name=C:\\Program Files\\Common Files"
    //  "\\System\\Ole DB\\Data Links\\nwind.udl", L"", L"", -1);
    m_pConnection->Open
      (L"Provider=Microsoft.Jet.OLEDB.4.0;"
      "Data Source=D:\\DATA\\Nwind.mdb", L"", L"", -1);     ......

    m_cmbCat.Execute(m_pConnection,_
      T("SELECT * FROM Categories ORDER BY CategoryName"),
      _T("CategoryName"));
    m_cmbSup.Execute(m_pConnection,
      _T("SELECT * FROM Suppliers ORDER BY CompanyName"),
      _T("CompanyName"));

    ......
    ......
  }
  catch(_com_error *e)
  {
    CString Error = e->ErrorMessage();
    AfxMessageBox(e->ErrorMessage());
  }
  catch(...)
  {

  }

  SetFields();

创建主从关系

重写ComboBoxes4CBN_SELCHANGE消息并创建Requery()函数。 将m_Grid.ExecuteSetFields()移动到Requery()

void CDataGrid_DemoDlg::Requery()
{
  CString strCat,strSup,strWhere;

  if (!m_cmbCat.IsAddPosition())
  {
    m_strCatId=m_cmbCat.m_pSet->GetCollect(L"CategoryID");
    strCat=_T(" CategoryID=")+m_strCatId;
  }

  if (!m_cmbSup.IsAddPosition())
  {
    m_strSupId=m_cmbSup.m_pSet->GetCollect(L"SupplierID");
    strSup=_T(" SupplierID=")+m_strSupId;
  }
  
  strWhere=strCat;

  if( ( strSup.GetLength()*strWhere.GetLength() ) ==0 )
    strWhere+=strSup;
  else
    strWhere+=" AND "+strSup;
  
  m_Grid.Execute(m_pConnection,"ProductId"/*Primary key field */ 
    ,"*"//fields in SELECT statment
    ,"Products" //from 
    ,strWhere   // where 
    ,0);        // order by N of the Grid column      
}

下面的截图向我们展示了这种关系是如何工作的。

步骤3- 在DataGrid中更改数据

创建对话框,将编辑框和组合框放入对话框模板中。 将必要的类和变量添加到头文件中。

#include "DataComboBox.h"
#include "DDXFields.h"

/////////////////////////////////////////////////////////////////////////////
// CDialEdit dialog

class CDialEdit : public CDialog
{
// Construction
public:
  bool m_catChange,m_supChange;
  _RecordsetPtr m_pSet;
  CDDXFields m_DDXFields;
  enum {EDIT, COPY, NEW};
  int m_operation;

  CDataComboBox m_cmbCat;
  CDataComboBox m_cmbSup;
  . . . 
  . . .

将控件关联到DoDataExchange

  DDX_Control(pDX, IDC_CMB_CAT, m_cmbCat);
  DDX_Control(pDX, IDC_CMB_SUP, m_cmbSup);
  
  m_DDXFields.DDX(pDX);

添加到构造函数中

CDialEdit::CDialEdit(CWnd* pParent /*=NULL*/)
  : CDialog(CDialEdit::IDD, pParent)
{
  //{{AFX_DATA_INIT(CDialEdit)
    // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
  m_operation=EDIT;
  m_catChange=m_supChange=false;
  m_DDXFields.SetSize(5);
}

OnInitDialog中添加

BOOL CDialEdit::OnInitDialog()
{
  CDialog::OnInitDialog();


  // TODO: Add extra initialization here


  int i;

  m_DDXFields.m_pWnd=this;

  i=0;
  m_DDXFields.ElementAt(i).Set(_T( "ProductName" ),IDC_ED_PROD) ;
  m_DDXFields.ElementAt(i).m_description=_T("Product");

  i=1;
  m_DDXFields.ElementAt(i).Set(_T( "QuantityPerUnit" ), 
                       IDC_ED_QTY, _T( "1" ),false,true) ;
  m_DDXFields.ElementAt(i).m_description=_T("Quantity Per Unit");

  i=2;
  m_DDXFields.ElementAt(i).Set(_T( "UnitPrice" ), 
                       IDC_ED_PRICE, _T( "0" ),false,true) ;
  m_DDXFields.ElementAt(i).m_description=_T("Price") ;

  i=3;
  m_DDXFields.ElementAt(i).Set(_T( "UnitsInStock" ),
                       IDC_ED_UNITS, _T( "0" ),false,true) ;
  m_DDXFields.ElementAt(i).m_description=_T("Units In Stock");

  i=4;
  m_DDXFields.ElementAt(i).Set(_T( "Discontinued" ),
                       IDC_ED_DISCONT,_T( "0" ),false,true) ;

  switch (m_operation)
  {
    case NEW:
      break;
    default:
      m_DDXFields.ReadData(m_pSet);
    break;
  }
  UpdateData(FALSE);

  m_cmbSup.Fill();
  m_cmbCat.Fill();
 .  . .
 .  . .

在对话框中创建“保存”按钮和函数。

void CDialEdit::OnBtnSave()
{
  // TODO: Add your control notification handler code here
  CExString strField;
  UpdateData();
  try
  {
    switch (m_operation)
    {
      case EDIT:
        break;
      default:
        m_pSet->AddNew();

        strField=_T("CategoryID");
        m_pSet->Fields->GetItem
           (strField.Variant())->Value = m_catId.Variant();

        strField=_T("SupplierID");
        m_pSet->Fields->GetItem
          (strField.Variant())->Value = m_supId.Variant();

        break;
    }
    try
    {
      if( m_DDXFields.WriteData(m_pSet)==-1)
        return;
    }
    catch(...)
    {
      ;
    }
    CExString strCatId,strSupId;
    strCatId=m_cmbCat.GetCurId();
    strSupId=m_cmbSup.GetCurId();;

    if(strCatId!=m_catId)
    {

      m_catChange=true;
      strField=_T("CategoryID");
      m_pSet->Fields->GetItem
        (strField.Variant())->Value=strCatId.Variant();
    }

    if(strSupId!=m_supId)
    {
      m_supChange=true;
      strField=_T("SupplierID");
      m_pSet->Fields->GetItem
        (strField.Variant())->Value=strSupId.Variant();
    }
    m_pSet->Update();

    CDialog::OnOK();


  }//try
  catch( _com_error &e )
  {

    CString mes1,mes2,mes3,mes4;

    mes1.Format(_T("Error:%08lx.\n"), e.Error());
    mes2.Format(_T("ErrorMessage:%s.\n"), e.ErrorMessage());
    mes3.Format(_T("Source:%s.\n"), 
        (LPCTSTR) _bstr_t(e.Source()));
    mes4.Format(_T("Description:%s.\n"), 
        (LPCTSTR) _bstr_t(e.Description()));
    MessageBox(mes1+mes2+mes3+mes4,
        _T("Invalid field "),MB_ICONERROR);

    return;
  }
  catch(...)
  {

    MessageBox(_T("Unhandled Exception"),
       _T("Invalid field ")+strField,MB_ICONERROR);
    return;
  }

}

在主对话框类的定义中插入CArrayStringBox变量,因为我们在UNBOUND模式下使用m_cmbSupm_cmbCat

class CDataGrid_DemoDlg : public CResizableDialog
{
// Construction
public:
  CArrayStringBox m_SupArray;
  CArrayStringBox m_CatArray;

在主对话框的OnInitDialog中填充它。

  try
  {
    //Open connection
    //I prefer to use udl
    //m_pConnection->Open
    //  (L"File Name=C:\\Program Files\\"
    //   "Common Files\\System\\Ole DB\\Data Links\\nwind.udl", 
    //   L"", L"", -1);
    m_pConnection->Open(L"Provider=Microsoft.Jet.OLEDB.4.0;"
       "Data Source=D:\\DATA\\Nwind.mdb", L"", L"", -1);

    . . .
    . . .
    m_CatArray.Fill(m_pConnection,
      _T("SELECT * FROM Categories ORDER BY CategoryName"),
      _T("CategoryName"),_T("CategoryID"));
    m_SupArray.Fill(m_pConnection,
      _T("SELECT * FROM Suppliers ORDER BY CompanyName"),
      _T("CompanyName"),_T("SupplierID"));

  }
  catch(_com_error *e)
  {
    CString Error = e->ErrorMessage();
    AfxMessageBox(e->ErrorMessage());
  }
  catch(...)
  {

  }

在主对话框中创建“编辑”、“添加”、“复制”和“删除”按钮和函数

#include "DialEdit.h"

void CDataGrid_DemoDlg::OnBtnEdit()
{
  // TODO: Add your control notification handler code here
  int nRow=m_Grid.IsSelectRow();
  if(nRow==-1)
    return;

  CDialEdit dlg;

  dlg.m_pSet=m_Grid.m_pSet;

  dlg.m_catId=m_Grid.m_pSet->GetCollect(L"CategoryID") ;
  dlg.m_supId=m_Grid.m_pSet->GetCollect(L"SupplierID") ;

  dlg.m_cmbCat.Attach(&m_CatArray,dlg.m_catId);
  dlg.m_cmbSup.Attach(&m_SupArray,dlg.m_supId);

  dlg.m_operation=CDialEdit::EDIT;

  if (dlg.DoModal() == IDOK)
  {
    if( ((dlg.m_catChange)&&(!m_cmbCat.IsAddPosition()))
        || ( (dlg.m_supChange)&&(!m_cmbSup.IsAddPosition()) ) )
    {
      if(nRow==1)
      {
        if(1!=m_Grid.GetRowCount())
          m_Grid.SetRowFocus(nRow);
        else
          m_Grid.SetRowFocus(0);
      }
      else
      {
        m_Grid.SetRowFocus(nRow-1);
      }
      m_Grid.RequerySource();
    }
    else
      m_Grid.Invalidate();
  }
}

void CDataGrid_DemoDlg::OnBtnAd()
{
  // TODO: Add your control notification handler code here
  CDialEdit dlg;
  dlg.m_pSet=m_Grid.m_pSet;

  dlg.m_catId=m_Grid.m_pSet->GetCollect(L"CategoryID") ;
  dlg.m_supId=m_Grid.m_pSet->GetCollect(L"SupplierID") ;

  dlg.m_cmbCat.Attach(&m_CatArray,dlg.m_catId);
  dlg.m_cmbSup.Attach(&m_SupArray,dlg.m_supId);

  dlg.m_operation=CDialEdit::NEW;

  if (dlg.DoModal() == IDOK)
  {
    m_Grid.AddNew();
  }
}

void CDataGrid_DemoDlg::OnBtnCopy()
{
  // TODO: Add your control notification handler code here
  if(m_Grid.IsSelectRow()==-1)
    return;

  CDialEdit dlg;
  dlg.m_pSet=m_Grid.m_pSet;

  dlg.m_operation=CDialEdit::COPY;

  dlg.m_catId=m_Grid.m_pSet->GetCollect(L"CategoryID") ;
  dlg.m_supId=m_Grid.m_pSet->GetCollect(L"SupplierID") ;

  dlg.m_cmbCat.Attach(&m_CatArray,dlg.m_catId);
  dlg.m_cmbSup.Attach(&m_SupArray,dlg.m_supId);

  if (dlg.DoModal() == IDOK)
  {
    m_Grid.AddNew();
  }
}

void CDataGrid_DemoDlg::OnBtnDel()
{
  // TODO: Add your control notification handler code here
  m_Grid.Delete();
}

添加菜单,查找对话框,你的应用程序就准备好了。

© . All rights reserved.