VC结合Access制作通讯录程序

MFC与ADO
本文介绍了一个基于MFC的通讯录程序设计,通过分层架构实现界面层与数据访问层的分离。利用ADO技术进行数据库操作,并详细展示了CDataSource类的设计与实现过程。


设计思路:
分层:界面层,数据访问层,数据库;
数据访问层的作用是为界面层提供一个数据访问接口,隔离界面层和数据库;界面层不需要知道采用的哪种数据库,只需要调用数据访问层提供的接口来完成各种操作;
1.新建一个基于对话框的MFC程序,命名CallList
2.设计程序界面如下图:


3.数据访问层的实现:
在StdAfx.h中添加#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile")
新建一个类CDataSource,作用是封装数据层,定义如下

ContractedBlock.gifExpandedBlockStart.gifCode
class CDataSource  
{
public:
    
//当前记录指针是否到了所有记录之后
    BOOL IsEOF();
    
//当前记录指针是否到了所有记录之前
    BOOL IsBOF();
    
//删除当前记录
    void Delete();
    
//设置FieldName字段的值为Value(int型)
    void SetAsInteger(CString FieldName, int Value);
    
//设置FieldName字段的值为Value(CString型)
    void SetAsString(CString FieldName, CString Value);
    
//将记录的修改更新到数据库中
    void Update();
    
//新增一条记录
    void New();
    
//得到FieldName字段的值(int型)
    int GetAsInteger(CString FieldName);
    
//得到FieldName字段的值(CString型)
    CString GetAsString(CString FieldName);
    
//当前记录指针是否到了最后一条记录
    BOOL IsLast();
    
//当前记录指针是否到了第一条记录
    BOOL IsFirst();
    
//移动当前记录指针到下一条记录
    void MoveNext();
    
//移动当前记录指针到上一条记录
    void MovePrev();
    
//移动当前记录指针到最后一条记录
    void MoveLast();
    
//移动当前记录指针到第一条记录
    void MoveFirst();
    
//初始化数据
    void InitData();
    CDataSource();
    
virtual ~CDataSource();

private:
    
int m_MaxID;
    _RecordsetPtr m_pRecordset;
    _ConnectionPtr m_pConn;
    
//释放数据
    void FreeData();
    
};

该类对外提供的方法是一些基本数据库操作,用户可根据需要进行添加。
4.对各种方法的实现,本例中采用的是ADO访问,所以定义了m_pConn和m_pRecordset两个对象。
实现InitData,作用是初始化数据库访问模块,包括数据库连接的初始化、记录集的初始化和当前数据库中最大ID的初始化,实现代码为:

ContractedBlock.gifExpandedBlockStart.gifCode
void CDataSource::InitData()
{
    
//初始化Com对象,为使用ADO做准备
    CoInitialize(NULL);
    
    
//初始化连接对象
    m_pConn.CreateInstance("ADODB.Connection");
    
//初始化记录集对象
    m_pRecordset.CreateInstance("ADODB.Recordset");
    
try
    {
        
//打开数据库连接
        m_pConn->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Demo.mdb;Persist Security Info=False""""", adConnectUnspecified);
        
        
//初始化m_MaxID
        m_pRecordset->Open("Select Max(ID) as MAXID From Profile", _variant_t(m_pConn, true), adOpenStatic, adLockOptimistic, adCmdText);        
        m_MaxID 
= GetAsInteger("MAXID");
        m_pRecordset
->Close();
        
        
//打开指定记录集
        m_pRecordset->Open("Select * From Profile", _variant_t(m_pConn, true), adOpenStatic, adLockOptimistic, adCmdText);
        
    }
    
catch(_com_error &e)
    {
        ::AfxMessageBox(e.ErrorMessage());
    }
}

和InitData方法对应的是FreeData方法,作用是关闭打开的记录集和数据库并释放到记录集和数据库指针,
注意该函数在CDataSource的析构函数中调用,实现代码为:

void CDataSource::FreeData()
{
    
if (m_pConn)
    {
        m_pConn
->Close();
        m_pRecordset.Release();
        m_pConn.Release();
        CoUninitialize();
    }
}

接来下实现数据指针移动函数,这些函数是对ADO数据指针移动操作的封装:

ContractedBlock.gifExpandedBlockStart.gifCode
void CDataSource::MoveFirst()
{
    m_pRecordset
->MoveFirst();
}

void CDataSource::MoveLast()
{
    m_pRecordset
->MoveLast();
}

void CDataSource::MovePrev()
{
    m_pRecordset
->MovePrevious();
}

void CDataSource::MoveNext()
{
    m_pRecordset
->MoveNext();
}

下面实现的是当前记录指针位置的函数:

ContractedBlock.gifExpandedBlockStart.gifCode
BOOL CDataSource::IsFirst()
{
    
if (m_pRecordset->BOF)
    {
        
return TRUE;
    }
    
else
    {
        m_pRecordset
->MovePrevious();
        BOOL Result 
= m_pRecordset->BOF;
        m_pRecordset
->MoveNext();
        
return Result;
    }
}

BOOL CDataSource::IsLast()
{
    
if (m_pRecordset->EndOfFile)
    {
        
return TRUE;
    }
    
else
    {
        m_pRecordset
->MoveNext();
        BOOL Result 
= m_pRecordset->EndOfFile;
        m_pRecordset
->MovePrevious();
        
return Result;
    }
}
BOOL CDataSource::IsBOF()
{
    
return m_pRecordset->BOF;
}

BOOL CDataSource::IsEOF()
{
    
return m_pRecordset->EndOfFile;
}

其中IsBOF和IsEOF函数是对ADO的直接封装,很容易理解,接来下实现设置和返回记录值的函数

ContractedBlock.gifExpandedBlockStart.gifCode
CString CDataSource::GetAsString(CString FieldName)
{
    
//如果在第一条记录之前或者最后一条记录之后,返回空
    if (IsBOF() || IsEOF())
        
return "";
    
    LPTSTR lpFieldName 
= FieldName.GetBuffer(FieldName.GetLength());
    
    
//得到当前记录指定列的值
    _variant_t vValue = m_pRecordset->Fields->Item[lpFieldName]->Value;
    
    
//如果为空值则返回空
    if ((V_VT(&vValue) == VT_NULL) || (V_VT(&vValue) == VT_EMPTY))
    {
        
return "";
    }
    
//否则以字符串形式返回vValue的值
    else
    {
        CString strResult;
        LPTSTR lpResult 
= strResult.GetBuffer(strlen(_bstr_t(vValue)));
        strcpy(lpResult, _bstr_t(vValue));
        strResult.ReleaseBuffer();
        
return strResult;
    }
}

int CDataSource::GetAsInteger(CString FieldName)
{
    
//如果在第一条记录之前或者最后一条记录之后,返回0
    if (IsBOF() || IsEOF())
        
return 0;
    LPTSTR lpFieldName 
= FieldName.GetBuffer(FieldName.GetLength());
    
    
//得到当前记录指定列的值
    _variant_t vValue = m_pRecordset->Fields->Item[lpFieldName]->Value;
    
    
//如果为空值则返回空
    if (V_VT(&vValue) == VT_NULL)
    {
        
return 0;
    }
    
    
//否则以int形式返回vValue的值
    else
    {
        
return atoi(_bstr_t(vValue));
    }
}
void CDataSource::SetAsString(CString FieldName, CString Value)
{
    
//将列名(FieldName)由CString转为LPTSTR型
    LPTSTR lpFieldName = FieldName.GetBuffer(FieldName.GetLength());
    
    
//将Value由CString转为LPTSTR型
    LPTSTR lpValue = Value.GetBuffer(Value.GetLength());
    
    
//将Value值更新到Recordset中
    m_pRecordset->Fields->Item[lpFieldName]->Value = lpValue;

    
//释放缓冲区
    FieldName.ReleaseBuffer();    
    Value.ReleaseBuffer();    
}

void CDataSource::SetAsInteger(CString FieldName, int Value)
{
    CString cs;
    
    
//将Value由int型转为CString型
    cs.Format("%d", Value);

    
//使用SetAsString设置指定列的值
    SetAsString(FieldName, cs);
}

最后要实现的是记录集的新建、更新和删除函数,代码如下:

ContractedBlock.gifExpandedBlockStart.gifCode
void CDataSource::New()
{
    
//添加一条新的记录
    m_pRecordset->AddNew();
    
    
//设置初始值
    m_MaxID++;
    SetAsInteger(
"ID", m_MaxID);
    SetAsString(
"NAME""无名氏");
    SetAsString(
"NUMBER""");
    SetAsInteger(
"CALL"0);
    SetAsString(
"QQ""");
    SetAsString(
"HOME""");
    SetAsString(
"NOTE""");
    
    
//更新
    m_pRecordset->Update();
}
void CDataSource::Update()
{
    m_pRecordset
->Update();
}
void CDataSource::Delete()
{
    m_pRecordset
->Delete(adAffectCurrent);//只删除当前记录
}

至此,数据访问层类CDataSource已经实现了。整体上看,是对ADO操作的封装,更改数据库时,只需重写InitData函数即可。

5.界面层的实现
界面层是对界面显示控制的封装,本例模块比较简单,可将代码直接写入CCallList类中,
在CCallList类中定义一个CDataSource类对象:    CDataSource m_ds;
在对话框初始化函数OnIninDialog中初始化m_ds的数据,添加:m_ds.InitData();

为每个编辑框定义一个CEdit类型的对象,如图


为CCaiiListDlg类添加函数void LoadData,作用是将当前记录的内容装载到界面中

ContractedBlock.gifExpandedBlockStart.gifCode
void CCallListDlg::LoadData()
{
     
//年龄
    m_edtAge.SetWindowText(m_ds.GetAsString("AGE"));
    
//姓名
    m_edtName.SetWindowText(m_ds.GetAsString("NAME"));
    
//学号
    m_edtNumber.SetWindowText(m_ds.GetAsString("NUMBER"));
    
//手机
    m_edtCall.SetWindowText(m_ds.GetAsString("CALL"));
    
//QQ
    m_edtQq.SetWindowText(m_ds.GetAsString("QQ"));
    
//家庭住址
    m_edtHome.SetWindowText(m_ds.GetAsString("ADDRESS"));
    
//备注
    m_edtNote.SetWindowText(m_ds.GetAsString("NOTE"));
}


为CCaiiListDlg类添加函数BOOL IsNeedUpdate(),作用是判断用户对界面上的数据是否进行了修改,判断的方法是使用记录中的字段和当前界面中数据比较,如有不同,说明用户修改了具体代码为:

ContractedBlock.gifExpandedBlockStart.gifCode
BOOL CCallListDlg::IsNeedUpdate()
{
    CString strTemp;
    
    
//判断家庭住址
    m_edtHome.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("HOME"))
        
return TRUE;    
    
//判断学号
    m_edtNumber.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("NUMBER"))
        
return TRUE;
    
//判断姓名
    m_edtName.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("NAME"))
        
return TRUE;
    
//判断备注
    m_edtNote.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("NOTE"))
        
return TRUE;    
    
//判断手机
    m_edtCall.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("CALL"))
        
return TRUE;
    
//判断QQ
    m_edtQq.GetWindowText(strTemp);
    
if (strTemp != m_ds.GetAsString("QQ"))
        
return TRUE;        
    
return FALSE;
}

为每个按钮定义一个CButton类型的对象,如上图2中
为CCallListDlg类添加函数 void RefreshUpdateBtn(),作用是刷新“更新”按钮的可用与否;添加void RefreshView(),作用是刷新界面各控件的可用性,两个函数的代码如下:

ContractedBlock.gifExpandedBlockStart.gifCode
void CCallListDlg::RefreshUpdateBtn()
ExpandedBlockStart.gifContractedBlock.gif
{
    
//存在更改,设置“更新”按钮可用
    if (IsNeedUpdate())
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_btnUpdate.EnableWindow(TRUE);
    }

    
//不存在更改,设置“更新”按钮不可用
    else 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_btnUpdate.EnableWindow(FALSE);
    }

}



void CCallListDlg::RefreshView()
ExpandedBlockStart.gifContractedBlock.gif
{
    
//如果当前记录指针处于数据集第一条记录之前或最后一条记录之后,
    
//编辑框控件和单选按钮控件不可用
    if (m_ds.IsBOF() || m_ds.IsEOF())
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_edtHome.EnableWindow(FALSE);
        m_edtNumber.EnableWindow(FALSE);
        m_edtName.EnableWindow(FALSE);
        m_edtCall.EnableWindow(FALSE);
        m_edtNote.EnableWindow(FALSE);
        m_edtQq.EnableWindow(FALSE);
            
    }

    
//否则编辑框控件和单选按钮控件可用
    else
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_edtHome.EnableWindow(TRUE);
        m_edtNumber.EnableWindow(TRUE);
        m_edtName.EnableWindow(TRUE);
        m_edtCall.EnableWindow(TRUE);
        m_edtNote.EnableWindow(TRUE);
        m_edtQq.EnableWindow(TRUE);
    }

    
    
//首先设置各控件均可用
    m_btnDelete.EnableWindow(TRUE);

    m_btnInsert.EnableWindow(TRUE);

    m_btnNext.EnableWindow(TRUE);
+
        m_btnPrev.EnableWindow(TRUE);
    m_btnUpdate.EnableWindow(TRUE);
    
    
//如果当前记录集处于第一条记录,“上一条”按钮不可用
    if (m_ds.IsFirst())
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_btnPrev.EnableWindow(FALSE);
    }

    
    
//如果当前记录集处于最后一条记录,“下一条”按钮不可用
    if (m_ds.IsLast())
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        m_btnNext.EnableWindow(FALSE);
    }

    
    
//如果记录集中没有数据,“删除”按钮不可用
    if (m_ds.IsEOF() && m_ds.IsBOF())
        m_btnDelete.EnableWindow(FALSE);
    
    
//刷新“更新”按钮
    RefreshUpdateBtn();
}


为每一个按钮定义一个 BN_CLICKED 事件,编辑代码如下:

ContractedBlock.gifExpandedBlockStart.gifCode
void CCallListDlg::OnBtnDelete() 
{
    
// TODO: Add your control notification handler code here
    m_ds.Delete();
    m_ds.MoveNext();
    
if (!m_ds.IsBOF() && m_ds.IsEOF())
    {
        m_ds.MovePrev();
    };
    LoadData();
    RefreshView();
}
void CCallListDlg::OnBtnInsert() 
{
    
// TODO: Add your control notification handler code here
    
//新增一条记录
    m_ds.New();
    
//装载当前记录的数据
    LoadData();
    
//刷新界面
    RefreshView();
}
void CCallListDlg::OnBtnNext() 
{
    
// TODO: Add your control notification handler code here
    
//移动到下一条记录
    m_ds.MoveNext();
    
//装载当前记录的数据
    LoadData();    
    
//刷新界面显示
    RefreshView();
}
void CCallListDlg::OnBtnPrev() 
{
    
// TODO: Add your control notification handler code here
    
//移动到上一条记录
    m_ds.MovePrev();
    
//装载当前记录的数据
    LoadData();
    
//刷新界面显示
    RefreshView();
}
void CCallListDlg::OnBtnUpdate() 
{
    
// TODO: Add your control notification handler code here
    CString strTemp;
    
    
//将家庭住址保存到记录集
    m_edtHome.GetWindowText(strTemp);
    m_ds.SetAsString(
"HOME", strTemp);
    
    
//将姓名保存到记录集
    m_edtName.GetWindowText(strTemp);
    m_ds.SetAsString(
"NAME", strTemp);
    
    
//将学号保存到记录集
    m_edtNumber.GetWindowText(strTemp);
    m_ds.SetAsString(
"NUMBER",strTemp);
    
    
//将备注保存到记录集
    m_edtNote.GetWindowText(strTemp);
    m_ds.SetAsString(
"NOTE", strTemp);
    
    
//将QQ保存到记录集
    m_edtQq.GetWindowText(strTemp);
    m_ds.SetAsString(
"QQ", strTemp);

    
//将手机保存到记录集
    m_edtCall.GetWindowText(strTemp);
    m_ds.SetAsString(
"CALL", strTemp);

    
//更新到数据库
    m_ds.Update();
    
    
//设置更新按钮不可用
    m_btnUpdate.EnableWindow(FALSE);
}

为每个编辑框定义一个EN_CHARGE事件,编辑代码为:

ContractedBlock.gifExpandedBlockStart.gifCode
void CCallListDlg::OnChangeEditCall() 
{
    RefreshUpdateBtn();
}


void CCallListDlg::OnChangeEditHome() 
{
    RefreshUpdateBtn();    
}

void CCallListDlg::OnChangeEditName() 
{
    RefreshUpdateBtn();
}

void CCallListDlg::OnChangeEditNote() 
{
    RefreshUpdateBtn();    
}

void CCallListDlg::OnChangeEditNumber() 
{
    RefreshUpdateBtn();    
}

void CCallListDlg::OnChangeEditQq() 
{
    RefreshUpdateBtn();
}


至此基本信息和按钮部分已经设定完毕,对数据库的表格显示部分操作为:

在对话框编辑窗口右键单击,选择 Insert ActiveX Control ,插入 Microsoft ADO Data Control 6.0 (SP6) 控件,设置其属性
选中Control页签——Use Connection String ,按Build按钮,本例中选择 Microsoft Jet 4.0 OLE DB Provider,点击下一步,写入 C:\\Demo.mdb ; 
选中RecordSource页签,在Command Type中选择 2-adCmdTable ,在下面的表中选择储存通讯录的表名;

插入Microsoft Hierarchical FlexGrid Control控件
在其属性的ALL标签页中将DataSourcr属性设置为 IDC_ADODC1

编译运行该程序吧!

转载于:https://www.cnblogs.com/haosw/archive/2009/09/25/1574244.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值