xbgs

C++博客 首页 新随笔 联系 聚合 管理
  7 Posts :: 0 Stories :: 13 Comments :: 0 Trackbacks

2008年12月17日 #

    消息映射、循环机制是Windows程序运行的基本方式。VC++ MFC 中有许多现成的消息句柄,可当我们需要完成其它的任务,需要自定义消息,就遇到了一些困难。
    在MFC ClassWizard中不允许添加用户自定义消息,所以我们必须在程序中添加相应代码,以便可以象处理其它消息一样处理自定义消息。通常的做法是采取以下步骤:
第一步:定义消息。
 推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。
#define WM_MY_MESSAGE (WM_USER+100) 
第二步:实现消息处理函数。
该函数使用WPRAM和LPARAM参数并返回LPESULT。
LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
    // TODO: 处理用户自定义消息
    ...
    return 0;

第三步:在类头文件的AFX_MSG块中说明消息处理函数:
class CMainFrame:public CMDIFrameWnd
{
    ...
    // 一般消息映射函数
    protected:
    // {{AFX_MSG(CMainFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnTimer(UINT nIDEvent);
    afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
}
第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    ON_WM_TIMER()
    ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

 
  如果用户需要一个定义整个系统唯一的消息,可以调用SDK函数RegisterWindowMessage定义消息:
static UINT WM_MY_MESSAGE=RegisterWindowMessage("User");

  并使用ON_REGISTERED_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步骤同上。
  当需要使用自定义消息时,可以在相应类中的函数中调用函数PostMessage或SendMessage发送消息PoseMessage(WM_MY_MESSAGE,O,O);
如果向其他进程发送消息可通过如下方法发送消息:
DWORD result;
SendMessageTimeout(wnd->m_hWnd, // 目标窗口
                   WM_MY_MESSAGE, // 消息
                   0, // WPARAM
                   0, // LPARAM
                   SMTO_ABORTIFHUNG |
                   SMTO_NORMAL,
                   TIMEOUT_INTERVAL,
                   &result);
以避免其它进程如果被阻塞而造成系统死等状态。
    可是如果需要向其它类 (如主框架、子窗口、视类、对话框、状态条、工具条或其他控件等 发送消息时上述方法显得无能为力,而在编程过程中往往需要获取其它类中的某个识别信号 框架给我们造成了种种限制,但是可以通过获取某个类的指针而向这个类发送消息, 而自定义消息的各种动作则在这个类中定义,这样就可以自由自在的向其它类发送消息了。 


  下面举的例子叙述了向视类和框架类发送消息的方法:
★在主框架类中向视类发送消息:
1.视类中定义消息:
ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage) //定义消息映射

2.视类定义消息处理函数:
LRESULT CMessageView::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
    // TODO: 处理用户自定义消息
    ...
    return 0;
}
3.发送消息的测试函数
void CMainFrame::OnTest()
{
    CView * active = GetActiveView();//获取当前视类指针
    if(active != NULL)
    active->PostMessage(WM_MY_MESSAGE,0,0);
}
★在其它类中向视类发送消息:
//发送消息的测试函数
void CMyDialog::OnTest()
{
    CMDIFrameWnd *pFrame;
    CMDIChildWnd *pChild;
    CView *pView;
    //获取主窗口指针
    pFrame =(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
    // 获取子窗口指针
    pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();
    //获取视类指针
    pView = pChild->GetActiveView();
    if(pView != NULL)
    pView->PostMessage(WM_MY_MESSAGE,0,0);//发送消息
}
其余步骤同上。
★在视类中向主框架发送消息:
首先在主框架中定义相关的消息,方法同上,然后在发送消息的函数中添加代码如下
//发送消息的测试函数
void CMessageView::OnTest()
{
    CFrameWnd * active = GetActiveFrame();//获取当前主窗口框架指针
    if(active != this)
    active->PostMessage(WM_MY_MESSAGE,0,0);
    return 0;
}
  在其它类中向不同的类发送消息可依次方法类推,这样我们的程序就可以的不受限制向其它类和进程发送消息,而避免了种种意想不到的风险。
 
【实例】:
  下面一个例子程序为多文档程序里在一对话框中向视类发送消息,详述了发送自定义消息的具体过程。
  实现步骤:

第一步:在VC++中新建工程Message,所有ClassWizard步骤选项均为缺省,完成。
第二步:在主菜单中添加测试菜单为调出对话框,在框架类中建立相应函数OnTest().
第三步:在资源中建立对话框,通过ClassWizard添加新类TestDialog,添加测试按钮, 在对话框类中建立相应函数OnDialogTest()
//通过对话框按钮发送消息的函数
void TestDialog::OnDialogTest()
{
    CMDIFrameWnd *pFrame;
    CMDIChildWnd *pChild;
    CView *pView;
    //获取主窗口指针
    pFrame =(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
    // 获取子窗口指针
    pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();
    //获取视类指针
    pView = pChild->GetActiveView();
    if(pView != NULL)
    pView ->PostMessage(WM_MY_MESSAGE,0,0);//发送消息
}
  在Message.h头文件中添加如下语句:
static UINT WM_MY_MESSAGE=RegisterWindowMessage("Message");
第四步:在视类中添加自定义消息:
在头文件MessageView.h中添加消息映射
protected:
//{{AFX_MSG(CMessageView)
//}}AFX_MSG
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); //此行为添加代码
DECLARE_MESSAGE_MAP()
在视类文件MessageView.cpp中的消息映射中添加自定义消息映射
BEGIN_MESSAGE_MAP(CMessageView, CView)
//{{AFX_MSG_MAP(CMessageView)
//}}AFX_MSG_MAP
// Standard printing commands
ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage) //此行添加代码定义唯一消息
END_MESSAGE_MAP()
添加相应的0消息处理函数
LRESULT CMessageView::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
    CRect rect;
    GetClientRect(&rect);
    InvalidateRect(&rect);
    test=!test;
    return 0;
}
在MessageView.h中添加布尔变量 public:BOOL test;
在视类构造函数中初始化 test变量:test=FALSE;
修改CMessageView::OnDraw()函数
void CMessageView::OnDraw(CDC* pDC)
{
    CMessageDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // 以下程序显示消息响应效果
    if(test)
    pDC->TextOut(0,0,"消息响应!");
}
第五步:显示测试对话框
在MainFrame类中包含对话框头文件:
#include "TestDialog.h";
OnTest()函数中添加代码
void CMainFrame::OnTest()
{
    TestDialog dialog;
    dialog.DoModal();
}
运行程序,在测试菜单打开对话框,点击测试按钮即可看到结果
posted @ 2008-12-17 13:18 让心飞翔 阅读(1559) | 评论 (0)编辑 收藏

2006年6月23日 #

很多人都觉得自己的程序的界面不那么美观,往往VC默认产生的对话框比较单调,因此很多人往往找到很多其它的控件对对话框进行美化修饰,例如给静态控件设置字体,设置背景颜色等等, 其实这些完全可以由VC自己的WM_CTLCOLOR消息来完成!

WM_CTLCOLOR消息用来完成对EDIT、STATIC、BUTTON等控件设置背景和字体颜色,其用法如下:

1.首先在自己需要设置界面的对话框上点击右键-->建立类向导-->加入WM_CTLCOLOR消息-->自动生成OnCtlColor()函数,此函数可以对本对话框的控件的界面外观做修饰,用法如下:
将类向导产生的函数做如下修改:

HBRUSH CDialogColor::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
HBRUSH hbr = CDialog::OnCtlColor(pDC,pWnd, nCtlColor);
// TODO: Change any attributes of theDC here
//设置显示字体
CFont * cFont=new CFont;
cFont->CreateFont(16,0,0,0,FW_SEMIBOLD,FALSE,FALSE,0,
ANSI_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
DEFAULT_PITCH&FF_SWISS,"Arial");
//对特定的控件做修改
switch()
{
case CTLCOLOR_STATIC: //对所有静态文本控件的设置
{
pDC->SetBkMode(TRANSPARENT);
//设置背景为透明
pDC->SetTextColor(RGB(255,255,0)); //设置字体颜色
pWnd->SetFont(cFont); //设置字体
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
//创建画刷
return (HBRUSH) B; //返回画刷句柄
}
case CTLCOLOR_EDIT: //对所有编辑框的设置
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,255,0));
pWnd->SetFont(cFont);
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
return (HBRUSH) B;
}
default:
return CDialog::OnCtlColor(pDC,pWnd, nCtlColor);
}
}

注:case的类别有以下几种:
CTLCOLOR_BTN 按钮控件
CTLCOLOR_DLG 对话框
CTLCOLOR_EDIT 编辑框
CTLCOLOR_LISTBOX 列表框
CTLCOLOR_MSGBOX 消息框
CTLCOLOR_SCROLLBAR 滚动条
CTLCOLOR_STATIC 静态文本

2.你可能觉得对所有的控件使用统一的界面设置觉得不自由,其实VC同样可以对特定的ID的控件进行设置,方法如下:


switch (pWnd->GetDlgCtrlID())
{
//针对ID为IDC_CTL1、IDC_CTL2和IDC_CTL3的控件进行同样的设置
case IDC_CTL1:
case IDC_CTL2:
case IDC_CTL3:
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,255, 0));
pWnd->SetFont(cFont);
HBRUSH B = CreateSolidBrush(RGB(125,125,255));
return (HBRUSH) B;
}
default:
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
posted @ 2006-06-23 23:21 让心飞翔 阅读(6916) | 评论 (1)编辑 收藏

    在对话框程序的打印功能中,如果使用针式打印机,那么你可能希望连续打印的多个表中不换页。我们知道,在使用打印功能的时候,每当 dc.startpage()就会开始一页的打印,而dc.endpage()就会结束一页的打印,那么如何能使多个dc.startpage()与 dc.endpage()不换页而实现连续打印呢,方法如下: 假设我们要打印m_nTotalNum个表,每个表有m行,那么要实现m_nTotalNum个表连续打印不换页可以如下实现:(由于打印机制问题,在打满一张后是必须要换页的,我们的做法是在换页的地方让给它留出一定的空白,不太影响表的连续性)
/////////////主函数
void CDlgTest::PrintOneNum() 
{
CDC dc;
CPrintDialog printDlg(FALSE);
CPrintInfo Info;
printDlg.GetDefaults();
dc.Attach(printDlg.GetPrinterDC());
dc.m_bPrinting=TRUE;
CString strTitle;
strTitle.LoadString(AFX_IDS_APP_TITLE);
DOCINFO di;
::ZeroMemory(&di,sizeof(DOCINFO));
di.cbSize=sizeof(DOCINFO);
di.lpszDocName=strTitle;
if(dc.m_hDC)
{
BOOL bPrintingOk=dc.StartDoc(&di);
CPrintInfo Info;
m_nCurrentNum_WhenPrint=1; //当前表数为1,这个用来控制打印的表数
int nPageTotalScale=0; //初始化从0开始
Info.m_nCurPage=1; //从第一页开始
dc.StartPage(); //开始打印页
for(;;)
{
//打印完需要的表数m_nTotalNum则退出循环
if(m_nCurrentNum_WhenPrint >m_nTotalNum) break;
else //打印一张表
{
nPageTotalScale=OnPrintReport (&dc,&Info,nPageTotalScale, m_nCurrentNum_WhenPrint);
m_nCurrentNum_WhenPrint ++;
nPageTotalScale+=30; //打印下一张表的行距从+30开始
}
}
bPrintingOk=(dc.EndPage()>0); //结束一页
m_nCurrentNum_WhenPrint =0;
if(bPrintingOk)
dc.EndDoc();
else
dc.AbortDoc();
}
else
{
MessageBox("请检查打印机是否连接完好?","我的程 序",MB_ICONEXCLAMATION);
}
dc.Detach();
}

/////////////打印函数
int CdlgTest::OnPrintReport(CDC *pDC, CPrintInfo *pInfo, 
int nXBasePointWhenPrint, int m_nCurrentNum_WhenPrint)
{
int nXCurrentPoint=nXBasePointWhenPrint; //进行行控制的变量
//--------换页---------
if(nXCurrentPoint+210> 2000) //210为将要占用的长度
{
pDC->EndPage();
pDC->StartPage();
nXCurrentPoint=0;
pInfo->m_nCurPage++;
}
nXCurrentPoint+=210;
pDC->TextOut(0,nXCurrentPoint,"我的报表内容,从0-210的表长度 ");
//每当打印的输出要换行的时候就要判断if(nXCurrentPoint+将要使用的高度> 2000)2000为规定的一张纸的高度。 如果大于纸张高度就要换页。这样就保证打印内容连续。
return nXCurrentPoint; //最后返回打印完当前表时当前的页面高度,
以便下一张表接着这个高度打印

}
posted @ 2006-06-23 23:18 让心飞翔 阅读(958) | 评论 (0)编辑 收藏

 在单文档中的使用方法
步骤一 初始化

在CView类的.h头文件中包含文件:
    #include "Gridctrl.h"

并且手写加入如下的成员函数:
    CGridCtrl * m_pGridCtrl;

步骤二 构造与析构

构造函数中:
   m_pGridCtrl = NULL;
析构函数中:
   if(m_pGridCtrl)
       delete m_pGridCtrl;


步骤三 如果需要打印功能的话添加同名打印函数代码

在CView类的OnBeginPrinting()函数中添加如下代码:
if(m_pGridCtrl)
    m_pGridCtrl->OnBeginPrinting(pDC,pInfo);
//简单吧,这就是类的好处

其它两个打印函数也一样的做法.

步骤四 在OnInitaUpdate()函数中或者你自己添加的要显示Grid的消息函数中如下初始化:

   //创建非模式对话框
CDlg *dlg;
dlg=new CDlg();
dlg->Create(IDD_Dlg,this);

//初始化GridCtrl控件
if(m_pGridCtrl!=NULL)
{
delete m_pGridCtrl;
m_pGridCtrl=NULL;
}
if (m_pGridCtrl == NULL)
{
// Create the Gridctrl object
m_pGridCtrl = new CGridCtrl;
if (!m_pGridCtrl) return 0;
// Create the Gridctrl window
CRect rect;
GetClientRect(rect);
m_pGridCtrl->Create(rect, this, 100);
// fill it up with stuff
m_pGridCtrl->SetEditable(false);
m_pGridCtrl->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0)); //黄色背景
m_pGridCtrl->EnableDragAndDrop(false);
try {
m_pGridCtrl->SetRowCount(k); //设置行数为k行
m_pGridCtrl->SetColumnCount(4); //k列
m_pGridCtrl->SetFixedRowCount(1); //标题行为一行
m_pGridCtrl->SetFixedColumnCount(1); //同上
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
return 0;
}
//填充列标题
int row=0;
for(int col=0;col<4;col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if(col==0){
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.strText.Format(_T("【类别】"),col);
}
else if(col==1){
Item.nFormat = DT_LEFT|DT_WORDBREAK;
Item.strText.Format(_T("第一列"),col);
}
else if(col==2){
Item.nFormat = DT_LEFT|DT_WORDBREAK;
Item.strText.Format(_T("第二列"),col);
}
m_pGridCtrl->SetItem(&Item);
}
// fill rows/cols with text
for (row = 1; row < k; row++)
for (col = 0; col < h; col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if (col < 1) { //行标题头
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
Item.strText.Format(_T("%d"),row);
}
else if(col==1){ //第一列的值
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
str="aa";
Item.strText.Format(_T("%s"),str);
}else if(col==2){ //第二列第值
Item.nFormat = DT_CENTER|DT_VCENTER
|DT_SINGLELINE|DT_END_ELLIPSIS
|DT_NOPREFIX;
CString str;
str="bb";
Item.strText.Format(_T("%s"),str);
}
m_pGridCtrl->SetItem(&Item);
}
m_pGridCtrl->AutoSize();

//--------------设置行列距------------------
for(int a=1;a<m;a++)
m_pGridCtrl->SetRowHeight(a,21); //设置各行高
m_pGridCtrl->SetRowHeight(0,24); //设置0行高
m_pGridCtrl->SetColumnWidth(1,110); //设置2列宽
m_pGridCtrl->SetColumnWidth(2,160); //设置3列宽
m_pGridCtrl->SetColumnWidth(3,100); //设置4列宽
}
上例取自实际工程,稍有修改!
部分注释:
void SetVirtualMode(TRUE) //设为虚模式
BOOL SetRowCount(int nRows) //设置总的行数。
BOOL SetFixedRowCount(int nFixedRows = 1)//设置固定的行数据
BOOL SetColumnCount(int nCols) //设置列数
BOOL SetFixedColumnCount(int nFixedCols = 1)//设置固定的列数


步骤五: 添加WM_SIZE消息,调整控件的界面占屏幕大小

  if(m_pGridCtrl->GetSafeHWnd())
   {
      CRect rect;
     GetClientRect(rect);
     m_pGridCtrl->MoveWindow(rect);
   }



 在对话框中的使用方法

步骤一 创建数据显示表格对话框

在资源管理器中新创建一个对话框,假设为CDlgTestReportBox。 从工具箱中加入Custom Control,就是人头像的那个,将其区域拉伸至要显示数据表格的大小,充满整个对话框。

在CDlgTestReportBox类的头文件中:

#include "GridCtrl.h"

再定义成员变量:

CGridCtrl* m_pGrid;

添加OnShowWindow()消息处理函数如下:

void CDlgTestReportBox::OnShowWindow(BOOL bShow, UINT nStatus) 
{
CDialog::OnShowWindow(bShow, nStatus);
// TODO: Add your message handler code here
if(m_pGrid!=NULL)
{
delete m_pGrid;
m_pGrid=NULL;
}
if(m_pGrid==NULL)
{
m_pGrid=new CGridCtrl;
CRect rect;
GetDlgItem(IDC_ReportAera)->GetWindowRect(rect); //得到显示区域
ScreenToClient(&rect);
m_pGrid->Create(rect,this,100);
m_pGrid->SetEditable(false);
m_pGrid->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0)); //黄色背景
try
{
m_pGrid->SetRowCount(10); //初始为10行
m_pGrid->SetColumnCount(11); //初始化为11列
m_pGrid->SetFixedRowCount(1); //表头为一行
m_pGrid->SetFixedColumnCount(1); //表头为一列
}
catch (CMemoryException* e)
{
e->ReportError();
e->Delete();
// return FALSE;
}
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;
if(row==0&&col==0) //第(0,0)格
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T("报表显示"),col);
}
else if (row < 1) //设置0行表头显示
{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" 项目%d"),col);
}
else if (col < 1) //设置0列表头显示
{
if(row< m_pGrid->GetRowCount()-4)
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.szText.Format(_T("第%d次"),row);
}
}
else
{
Item.nFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
Item.szText.Format(_T(""),2);
}
m_pGrid->SetItem(&Item);
}
m_pGrid->Invalidate();
}
//--------------设置行列距------------------
for(int a=0;aGetRowCount();a++)
m_pGrid->SetRowHeight(a,16); //设置各行高
m_pGrid->SetColumnWidth(0,58); //设置0列宽
for(int b=1;bGetColumnCount();b++)
m_pGrid->SetColumnWidth(b,59); //设置各列宽
}

 

步骤二 嵌入上面的对话框 显示数据

在你需要显示数据的对话框上的头文件中,假设为CDlgTest,加入

#include "GridCtrl.h"

CDlgTestReportBox* m_pTestReportBox;

将数据显示对话框放入你的对话框相应位置上,在CDlgTest::OnInitDialog() 中:

if(!m_pTestReportBox)
{
     m_pTestReportBox=new CDlgTestReportBox(this);
}

m_pTestReportBox->Create(IDD_DlgTestReportBox,this);

//定义区域变量
CRect rectDraw;
GetDlgItem(IDC_AeraReport)->GetWindowRect(rectDraw);
ScreenToClient(&rectDraw); //动态测试数据显示区域rectDraw

//将对应的对话框放到指定区域
m_pTestReportBox->MoveWindow(rectDraw);
m_pTestReportBox->ShowWindow(SW_SHOW);

自定义填充数据的函数:CDlgTest::FillGrid() 如下:

CGridCtrl* pGrid=m_pTestReportBox->m_pGrid;
for (int row = pGrid->GetRowCount()-1; row >= pGrid->GetRowCount()-3; row--)
{
for (int col = 1; col <= pGrid->GetColumnCount(); col++)
{
GV_ITEM Item;
Item.mask = GVIF_TEXT|GVIF_FORMAT;
Item.row = row;
Item.col = col;
if(row==pGrid->GetRowCount()-3&&col>0) //平均值
{
if(col==10){
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" %6.2f "),avjch);
}
else{
Item.nFormat = DT_CENTER|DT_WORDBREAK;
Item.szText.Format(_T(" %6.2f "),av[col-1]);
}
}
pGrid->SetItem(&Item); //提交数据
if(row==0||col==0)
{
COLORREF clr = RGB(0, 0, 0);
pGrid->SetItemBkColour(row, col, clr);
pGrid->SetItemFgColour(row, col, RGB(255,0,0));
}
}//循环结束
pGrid->Invalidate();
}
好累啊,忙了一天时间终于写完了!


posted @ 2006-06-23 23:16 让心飞翔 阅读(10060) | 评论 (12)编辑 收藏

一、ADO概述
ADO是Microsoft为最新和最强大的数据访问范例 OLE DB 而设计的,是一个便于使用的应用程序层接口。ADO 使您能够编写应用程序以通过 OLE. DB 提供者访问和操作数据库服务器中的数据。ADO 最主要的优点是易于使用、速度快、内存支出少和磁盘遗迹小。ADO 在关键的应用方案中使用最少的网络流量,并且在前端和数据源之间使用最少的层数,所有这些都是为了提供轻量、高性能的接口。之所以称为 ADO,是用了一个比较熟悉的暗喻,OLE 自动化接口。
OLE DB是一组”组件对象模型”(COM) 接口,是新的数据库低层接口,它封装了ODBC的功能,并以统一的方式访问存储在不同信息源中的数据。OLE DB是Microsoft UDA(Universal Data Access)策略的技术基础。OLE DB 为任何数据源提供了高性能的访问,这些数据源包括关系和非关系数据库、电子邮件和文件系统、文本和图形、自定义业务对象等等。也就是说,OLE DB 并不局限于 ISAM、Jet 甚至关系数据源,它能够处理任何类型的数据,而不考虑它们的格式和存储方法。在实际应用中,这种多样性意味着可以访问驻留在 Excel 电子数据表、文本文件、电子邮件/目录服务甚至邮件服务器,诸如 Microsoft Exchange 中的数据。但是,OLE DB 应用程序编程接口的目的是为各种应用程序提供最佳的功能,它并不符合简单化的要求。您需要的API 应该是一座连接应用程序和OLE DB 的桥梁,这就是 ActiveX Data Objects (ADO)。
二、在VC中使用ADO(开发步骤好下:)
1、引入ADO库文件
使用ADO前必须在工程的stdafx.h头文件里用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下所示:
用#import引入ADO库文件
 
#import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF")
 
这行语句声明在工程中使用ADO,但不使用ADO的名字空间,并且为了避免常数冲突,将常数EOF改名为adoEOF。现在不需添加另外的头文件,就可以使用ADO接口了。
2、初始化OLE/COM库环境
必须注意的是,ADO库是一组COM动态库,这意味应用程序在调用ADO前,必须初始化OLE/COM库环境。在MFC应用程序里,一个比较好的方法是在应用程序主类的InitInstance成员函数里初始化OLE/COM库环境。
 
BOOL CMyAdoTestApp::InitInstance()
{
if(!AfxOleInit())//这就是初始化COM库
{
AfxMessageBox(“OLE初始化出错!”);
return FALSE;
}
……
}
 
3、ADO接口简介
ADO库包含三个基本接口:_ConnectionPtr接口、_CommandPtr接口和_RecordsetPtr接口。
_ConnectionPtr 接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用 _ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr来实现。而用 _ConnectionPtr操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr时不需要。
_CommandPtr 接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局 _ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但 如果你要频繁访问数据库,并要返回很多记录集,那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用_CommandPtr 接口执行存储过程和SQL语句。
_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同 _CommandPtr接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给 _RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同Command对象一样使用 已经创建了数据连接的全局_ConnectionPtr接口
,然后使用_RecordsetPtr执行存储过程和SQL语句。 
4、使用_ConnectionPtr接口
_ConnectionPtr主要是一个连接接口,取得与数据库的连接。它的连接字符串可以是自己直接写,也可以指向一个ODBC DSN。
 
_ConnectionPtr pConn;
if (FAILED(pConn.CreateInstance("ADODB.Connection")))
{
AfxMessageBox("Create Instance failed!");
return;
}

CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="suppersoft";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=SA;PWD=";
CString strSQL = "Insert into student(no,name,sex,address) values(3,'aaa','male','beijing')";
_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);
_bstr_t bstrSRC(strSRC);
if (FAILED(pConn->Open(bstrSRC,"","",-1)))
{
AfxMessageBox("Can not open Database!");
pConn.Release();
return;
}
COleVariant vtOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);
pConn->Execute(_bstr_t(strSQL),&vtOptional,-1);
pConn.Release();
AfxMessageBox("ok!");
 
5、使用_RecordsetPtr接口(以连接SQL Server为例)

_RecordsetPtr pPtr;
if (FAILED(pPtr.CreateInstance("ADODB.Recordset")))
{
AfxMessageBox("Create Instance failed!");
return FALSE;
}
CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="210.46.141.145";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=sa;PWD=";
strSRC+="sa";
CString strSQL = "select id,name,gender,address from personal";
_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);
if(FAILED(pPtr->Open(varSQL,varSRC,adOpenStatic,adLockOptimistic,adCmdText)))
{
AfxMessageBox("Open table failed!");
pPtr.Release();
return FALSE;
}
while(!pPtr->GetadoEOF())
{
_variant_t varNo;
_variant_t varName;
_variant_t varSex;
_variant_t varAddress;
varNo = pPtr->GetCollect ("id");
varName = pPtr->GetCollect ("name");
varSex = pPtr->GetCollect ("gender");
varAddress = pPtr->GetCollect ("address");
CString strNo =(char *)_bstr_t(varNo);
CString strName =(char *)_bstr_t(varName);
CString strSex =(char *)_bstr_t(varSex);
CString strAddress =(char *)_bstr_t(varAddress);
strNo.TrimRight();
strName.TrimRight();
strSex.TrimRight();
strAddress.TrimRight();
int nCount = m_list.GetItemCount();
int nItem = m_list.InsertItem (nCount,_T(""));
m_list.SetItemText (nItem,0,strNo);
m_list.SetItemText (nItem,1,strName);
m_list.SetItemText (nItem,2,strSex);
m_list.SetItemText (nItem,3,strAddress);
pPtr->MoveNext();
}
pPtr->Close();
pPtr.Release();
 
6、使用_CommandPtr接口
_CommandPtr接口返回一个Recordset对象,并且提供了更多的记录集控制功能,以下代码示例了使用_CommandPtr接口的方法:
代码11:使用_CommandPtr接口获取数据
 
_CommandPtr pCommand;
_RecordsetPtr pRs;
pCommand.CreateInstance(__uuidof(Command));
pCommand->ActiveConnection=pConn;
pCommand->CommandText="select * from student";
pCommand->CommandType=adCmdText;
pCommand->Parameters->Refresh();
pRs=pCommand->Execute(NULL,NULL,adCmdUnknown);
_variant_t varValue = pRs->GetCollect("name");
Cstring strValue=(char*)_bstr_t(varValue);
 
7、关于数据类型转换由于COM对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据,
因此Cstring 类和COM对象是不兼容的,我们需要一组API来转换COM对象和C++类型的数据。_vatiant_t和_bstr_t就是这样两种对象。它们提供了通用的方法转换COM对象和C++类型的数据。
posted @ 2006-06-23 23:14 让心飞翔 阅读(1959) | 评论 (0)编辑 收藏

    Forms2.0系列的控件,如: Microsoft forms 2.0 labelMicrosoft forms 2.0 commandbutton等,其依赖的动态链接库为Fm20.dlls。在使用过程中我发现,如果在发布软件的时候,用户的客户机上没有安装word的话,程序就不能运行(虽然程序运行了,但是看不到界面出现)。 后来在网上搜索了很多资料,终于找到了问题的关键,在微软网站上找到关于这个问题的解释为: Fm20.dlls 不是可以重分配的,你必须安装一个象OFFICE97那样的系统在你的目标客户机上作为你软件分发的一部分。 如果不安装Microsoft Office 97那样的系统的话,即使你使用regsvr32注册都没有作用(我翻译的,有点不通顺哦^_^)。
    最终的解决办法就是,做为Microsoft Office 97的替代,你可以免费下载和安装Microsoft ActiveX Control Pad. 安装完毕,客户机上就可以无需安装office而使用你的程序了。[需要的请留下email,我会发给你的]
posted @ 2006-06-23 23:11 让心飞翔 阅读(1247) | 评论 (0)编辑 收藏

下面是我在编程中运用CTypedPtrMap指针模板类的方法,其存储结构占用物理空间小,比单纯的用数组结构存储的文件将小很多,所以是一种非常推荐的存储及读取文件的方法。

你必须在stdafx.h文件中 包含  #include <afxtempl.h>    // MFC templates
在头文件中包含  #include <io.h>

其中 CBank.h文件如下:

class CBank : public CObject  
{
DECLARE_SERIAL(CBank)   //声明序列化存储
public:
       CBank();
       CBank(CString str){ m_bankname = str; }
       CBank(const CBank &b){ m_bankname= b.m_bankname;} //拷贝构造函数
        virtual ~CBank();
private:
       CString  m_bankname;
public:
    virtual void Serialize(CArchive& ar);
};

typedef CTypedPtrMap<CMapStringToOb,

                     CString,

                     CBank*>CBanktmp;  //这个才是我们根据此类生成的类型

它的实现文件.cpp如下:

IMPLEMENT_SERIAL(CBank, CObject, 1)   //实现序列化存储
CBank::CBank()
{
    m_bankname="";
}
CBank::~CBank()
{

}
void CBank::Serialize(CArchive& ar)
{
    if (ar.IsLoading())
    {
        ar >> m_bankname;
    }
    else
    {
        ar << m_bankname;
    }
}

/*----------------------------------------------------
从文件中读取参数:
注意: 其中的m_map是这样定义的:CBanktmp  m_map;
-------------------------------------------------*/

void CDlgBankIn::LoadBankName()
{
       UINT  nFlags = CFile::typeBinary |CFile::modeReadWrite;
       if(_access(_T("BankName.dat"),0))
       {
              nFlags |= CFile::modeCreate;
       }
       CFile file;
       CFileException fe;
       if (file.Open(_T("BankName.dat"),nFlags, &fe))
       {
           if(file.GetLength()==0)   //如果文件为空则不进行读取操作
           {
           }
           else
           {
                CArchive ar(&file, CArchive::load);
                m_map.Serialize(ar);   //读取文件
               //根据文件内容填充列表框
                POSITION   pos;
                CString    strKey;
                CBank   *pB=NULL;
                for( pos = m_map.GetStartPosition(); pos != NULL; )
                {
                    m_map.GetNextAssoc(pos,strKey,(CBank*)pB);
                    m_CmbBank.AddString(strKey);
                }
                delete  pB;
              }
       }
       file.Close();
}

/*-------------------------------------------------
功能: 先打开文件,然后查找文件中是否有与编辑框关键字相同的内容,
       如果有相同内容则不添加,如果没有相同内容则写文件.
------------------------------------- ------------*/

void CDlgBankList::OnBtnAdd() 
{
       // TODO: Add your control notification handler code here
       CString  strKey;
       GetDlgItemText(IDC_EDITBank,strKey);
       if (0 != strKey.GetLength())
       {
          CBank  *pB;
          //打开文件
         UINT  nFlags = CFile::typeBinary |CFile::modeRead;
          CFile file;
          CFileException fe;
          if (file.Open(_T("BankName.dat"),nFlags, &fe))
          {
                 if(file.GetLength()==0)  //如果文件为空则不进行序列化操作
                 {

                 }
                 else
                 {
                  CArchive ar(&file, CArchive::load);
                  m_map.Serialize(ar);
                 }
               //分析文件并给文件添加编辑框的内容
                if(!m_map.Lookup(strKey,(CBank*)pB))  //当不存在时才需要添加
                  {
                      if (CB_ERR != m_ListBox.AddString(strKey))
                      {
                         m_map.SetAt(strKey, new CBank(strKey));  //给关键字strKey赋值
                      }
                 }
          }
       file.Close();

       //存储文件
     nFlags = CFile::typeBinary |CFile::modeWrite;
       if (file.Open(_T("BankName.dat"),nFlags, &fe))
       {
            CArchive ar(&file, CArchive::store);
            m_map.Serialize(ar);   //序列化存储
     }
    }
}

/*-------------------------------------------------
功能: 先打开文件,然后查找文件中的关键字,
    找到后则删除此关键字, 但是注意关键字对应的内容仍然没有删除,这点期待改进.
-------------------------------------------------*/

void CDlgBankList::OnBtnDel() 
{
       // TODO: Add your control notification handler code here
       int iIndex;      
       CString  strKey;
       if(LB_ERR!=m_ListBox.GetCurSel())
       {
        //打开文件
     & nbsp;     UINT  nFlags = CFile::typeBinary |CFile::modeRead;
           CFile file;
           CFileException fe;
           if (file.Open(_T("BankName.dat"),nFlags, &fe))
           {
              CArchive ar(&file, CArchive::load);
              m_map.Serialize(ar); 
              iIndex=m_ListBox.GetCurSel();
              m_ListBox.GetText(iIndex,strKey);
              m_ListBox.DeleteString(iIndex);
              //分析文件并给文件添加编辑框的内容
               CBank  *pB=NULL;
              if(m_map.Lookup(strKey,(CBank*)pB))
              {
                  m_map.RemoveKey(strKey);   //删除关键字,但没有删除关键字对应的内容
               }
            }
            file.Close();
            //存储文件
           nFlags = CFile::typeBinary |CFile::modeWrite;
           if (file.Open(_T("BankName.dat"),nFlags, &fe))
           {
                CArchive ar(&file, CArchive::store);
                m_map.Serialize(ar);   //序列化存储
           }
       }
}

posted @ 2006-06-23 23:07 让心飞翔 阅读(1906) | 评论 (0)编辑 收藏

仅列出标题