一些MFC笔记

    xiaoxiao2025-04-06  22

    Windows消息分类

           先讲下Windows消息的分类。Windows消息分为系统消息和用户自定义消息。Windows系统消息有三种:

           1.标准Windows消息。除WM_COMMAND外以WM_开头的消息是标准消息。例如,WM_CREATE、WM_CLOSE。

           2.命令消息。消息名为WM_COMMAND,消息中附带了标识符ID来区分是来自哪个菜单、工具栏按钮或加速键的消息。

           3.通知消息。通知消息一般由列表框等子窗口发送给父窗口,消息名也是WM_COMMAND,其中附带了控件通知码来区分控件。

     CWnd的派生类都可以接收到标准Windows消息、通知消息和命令消息。命令消息还可以由文档类等接收。

           用户自定义消息是实际上就是用户定义一个宏作为消息,此宏的值应该大于等于WM_USER,然后此宏就可以跟系统消息一样使用,窗口类中可以定义它的处理函数。

    添加消息处理函数

    1.在类定义中加入消息处理函数的函数声明,注意要以afx_msg打头。例如MainFrm.h中WM_CREATE的消息处理函数的函数声明:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);。

           2.在类的消息映射表中添加该消息的消息映射入口项。例如WM_CREATE的消息映射入口项:ON_WM_CREATE()。

           3.在类实现中添加消息处理函数的函数实现。例如,MainFrm.cpp中WM_CREATE的消息处理函数的实现:

    MFC对话框

    利用类添加向导

          1.在编辑框上点右键,在右键菜单中选择“Add Variable”。弹出添加成员变量的向导对话框。

           2.我们想为其添加值变量而不是控件变量,所以对话框中“Category”下的组合框中选择Value。

           3.“Variable type”下的组合框此时默认选中的是“CString”,CString是字符串类,显然不能进行加法运算。我们可以选择double、float、int等。这里我们选择double,即编辑框关联一个double类型的变量。

           4.在“Variable name”中写入自定义的变量名

    对话框类的数据交换和检验

    在DoDataExchange()函数中利用DDX_XXX来绑定对话框的控件程序的对象来实现数据的交换

    DDX_Control(pDX, IDC_EDT_NAME, m_EDT_strName); DDX_Text(pDX, IDC_EDT_NAME, m_strName); DDX_TEXT()可以认为是把字符串变量和控件的内容关联起来;  DDX_Control()可以认为是把变量和控件本身关联起来;  DoDataExchange(pDX)就是处理所有变量与其关联控件交换数据的函数。 DDX_TEXT()数据交换过程: UpdateData(TRUE);//将控件的内容传回 m_strName; m_strName = _T("关联变量");//修改值 UpdateData(FALSE);//将m_strName的值显示到控件 DDX_Control()数据交换过程: CString str; m_EDT_strName.GetWindowText(str);//得到控件的内容保存到str str = _T("控件绑定变量"); m_EDT_strName.SetWindowText(str);//设置控件的内容为str

    但是这种数据交换机制中,DoDataExchange()并不是被自动调用的,而是需要我们在程序中调用CDialogEx::UpdateData()函数,由UpdateData()函数再去自动调用DoDataExchange()的,如在按键消息处理函数中调用。

    还有的是GetDlgItem(里面是ID标识符)是CWnd的一个成员函数,用来获取对话框中控件的指针

    设置TAB顺序

    在主菜单中选择“Format”->"Tab Order",或者按快捷键Ctrl+D,对话框模板上就会显示各个控件的Tab顺序数字。

    模态对话框:

    CAdditionDlg dlg;        // 定义对话框类CAdditionDlg的对象dlg   m_pMainWnd = &dlg;       // 将dlg设为主窗口    INT_PTR nResponse = dlg.DoModal();   // 弹出对话框dlg,并将DoModal函数的返回值(退出时点击按钮的ID)赋值给nResponse   if (nResponse == IDOK)               // 判断返回值是否为OK按钮(其ID为IDOK,鸡啄米已经将它删除)    {       // TODO: Place code here to handle when the dialog is        //  dismissed with OK   }    else if (nResponse == IDCANCEL)      // 判断返回值是否为Cancel按钮(其ID为IDCANCEL,鸡啄米将它的Caption改为了“退出”)   {        // TODO: Place code here to handle when the dialog is       //  dismissed with Cancel    }   非模态对话框 声明一个CTipDlg  *m_pTipDlg; if (NULL == m_pTipDlg)        {           // 创建非模态对话框实例            m_pTipDlg = new CTipDlg();           m_pTipDlg->Create(IDD_TIP_DIALOG, this);        }       // 显示非模态对话框        m_pTipDlg->ShowWindow(SW_SHOW);     

    属性页对话框相关类

          我们使用属性页对话框时,用到的类主要有两个:CPropertyPage类和CPropertySheet类

    创建属性页对话框资源

    1.在资源插入新的对话框模板

    2.为各个对话框添加对应的类,因为属性页类都应继承于CPropertyPage类,所以要修改下面“Base class”的选项,在下拉列表中选择“CPropertyPage

    3.为属性页类重载OnSetActive函数呢

    4.重载函数:

    BOOL CSummandPage::OnSetActive()   {        // TODO: Add your specialized code here and/or call the base class          // 获得父窗口,即属性表CPropertySheet类       CPropertySheet* psheet = (CPropertySheet*) GetParent();        // 设置属性表只有“下一步”按钮       psheet->SetWizardButtons(PSWIZB_NEXT);          return CPropertyPage::OnSetActive();    }   消息对话框 MessageBox()和全局函数AfxMessageBox() 文件对话框 CFileDialog() 字体对话框 CFontDialog() 颜色对话框 CColorDialog() 静态创建控件都是在资源里直接添加,然后创建一个对应得变量对象在对话框初始化得时候设置相关得值。 菜单资源详解 创建一个新的MFC单文档工程 CMenu类

    菜单主要能发送两种消息:COMMAND消息和UPDATE_COMMAND_UI消息。下面分别讲解:

           COMMAND消息:在菜单项被点击时发送该消息。

           UPDATE_COMMAND_UI消息:用来维护菜单项的各项状态,包括激活、禁用、变灰、选中、未选中等。在下拉菜单每次打开的时候,所有菜单项的此消息都会被发送出去。如果所属类中为菜单项的该消息添加了处理函数,则执行相应函数更新菜单状态,如果菜单项没有此消息处理函数,也没有COMMAND消息的处理函数,那么它就会变灰。

    工具兰的创建

    1. 创建工具栏资源。

           2. 构造CMFCToolBar类的对象。

           3. 调用CMFCToolBar类的Create或CreateEx成员函数创建工具栏。

           4. 调用LoadToolBar成员函数加载工具栏资源。

    动态分割窗口

           创建动态分割窗口的步骤为:

           1. 在父框架类中定义一个CSplitterWnd类型的成员对象。

           2. 重载父框架类的CFrameWnd::OnCreateClient成员函数。

           3. 在重载的CFrameWnd::OnCreateClient函数中调用CSplitterWnd成员对象的Create函数。

    TIMER定时器类

    1、启动定时器。

           启动定时器就需要使用CWnd类的成员函数SetTimer。CWnd::SetTimer()

     UINT_PTR SetTimer(              UINT_PTR nIDEvent,              UINT nElapse,              void (CALLBACK* lpfnTimer        )(HWND,           UINT,           UINT_PTR,           DWORD        )         );

           参数nIDEvent指定一个非零的定时器ID;参数nElapse指定间隔时间,单位为毫秒;参数lpfnTimer指定一个回调函数的地址,如果该参数为NULL,则WM_TIMER消息被发送到应用程序的消息队列,并被CWnd对象处理。如果此函数成功则返回一个新的定时器的ID,我们可以使用此ID通过KillTimer成员函数来销毁该定时器,如果函数失败则返回0。

           通过SetTimer成员函数我们可以看出,处理定时事件可以有两种方式,一种是通过WM_TIMER消息的消息响应函数,一种是通过回调函数

    为WM_TIMER消息添加消息处理函数,或者定义回调函数:void CExample44Dlg::OnTimer(UINT_PTR nIDEvent) 

     如果调用CWnd::SetTimer函数时最后一个参数不为NULL,则需要定义回调函数。回调函数的形式如下:

    C++代码 void CALLBACK EXPORT TimerProc(      HWND hWnd, // handle of CWnd that called SetTimer      UINT nMsg, // WM_TIMER      UINT nIDEvent // timer identification      DWORD dwTime // system time      );  

     3、销毁定时器。

           不再使用定时器时,可以销毁它。销毁定时器需使用CWnd类的KillTimer成员函数,CWnd::KillTimer函数的原型如下:

    C++代码 BOOL KillTimer(UINT_PTR nIDEvent); 

    MFC异常类

           MFC将对异常的处理封装到了异常类--CException类及其子类中。其实即使我们不使用MFC异常宏而是使用C++标准异常处理的话,也是会用到MFC的CException类及其子类的。MFC异常类及其含义如下表:

    MFC异常类含义CSimpleException资源紧张异常的基类CInvalidArgException无效参数异常CMemoryException内存不足CNotSupportedException响应对不支持服务的请求CArchiveException存档/序列化异常CFileException文件异常CResourceExceptionWindows 资源分配异常COleExceptionOLE 异常CDBException数据库异常(ODBC 类)COleDispatchException调度(自动化)异常CUserException用消息框警告用户然后引发一般CException 的异常CDaoException数据库异常(DAO 类)CInternetException网络异常

    Cfile类

    virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL);

    lpszFileName是要打开文件的路径,可以是相对,也可以是绝对,nOpenFlags是打开的标志,有modeCreat ,modeRead等

    文件的读写如下:

    virtual UINT Read(void* lpBuf, UINT nCount); nCount 是要读的大小同时返回 virtual void Write(const void* lpBuf, UINT nCount);nCount是要写的大小

    lpBuf 是缓冲区指针 ,

    virtual ULONGLONG Seek(    LONGLONG lOff,    UINT nFrom  );

    文件指针偏移,定位文件指针的位置,若成功则返回偏移量,否则返回不定并激活一个CFlieException对象(错误对象)LOFF是偏移量,nfrom是方式有begin current end3种就是移动指针在文件内容的位置

    BOOL GetStatus(  CFileStatus& rStatus  ) const;  static BOOL PASCAL GetStatus(     LPCTSTR lpszFileName,  //文件路径    CFileStatus& rStatus,  // 用来存放返回的文件信息结构体    CAtlTransactionManager* pTM = NULL  );函数调用成功放回true。

    virtual void SetFilePath(LPCTSTR lpszNewName);lpszNewName指向指定新文件的路径字符串

    调用此成员函数指定文件路径。例如当构造一个CFile对象而文件的路径无效时,可调用此成员函数提供路径。(无效强行使用) 注意:SetFilePath不打开或创建文件,只将CFile对象和路径名联系起来,然后即可使用

    创建一个cfile对象时

    CDC环境设备类

    封装了绘图所需的所有函数,因为绘制图形和文字时必须指定相应的设备环境。所有绘图都是通过设备类 CPaintDC 类,它的构造和析构函数都是针对OnPaint进行的所以一旦获取CDC指针就能把它当人任何设备的指针来用会自动调用beginpaint和endpaint。 CClientDC只能在窗口的客户区,不包括边框,菜单状态栏标题栏等,(0,0)是客户区的左上角,CWindowDC能在窗口任何位置绘图,(0,0)是窗口左上角。构造函数分别调用GetWindowDC和GetDC析构调用ReleaseDC函数。 CMetaFileDC封装了一个在Windows土元文件中绘图的方法,对图像保存比像素精确,因而往往在要求较高的场合下使用。 绘图中常用的数据类型: SIZE:LONG X ,LONG Y 坐标,POINT:int cx int cy 水平和垂直大小 RECT 矩形对角坐标 CPoint(int inttx,int inity) CPoint(POINT initpt)   CSize(int initCX, int initCY); CSize(SIZE initSize) CRect(4个参数)、(Rect成员)(Point ,size)、(POINT,POINT) InflateRect 扩大矩形,DeflateRect缩小矩形这两个方法  IntersectRect 相交矩形(交叉)UnionRect合并矩形。 使用GDI对象进行绘图时,遵循的步骤: 定义一个GDI对象(如画笔,笔刷等)然后调用对应的函数(CreatPen)创建一个GDI对象 将构造的对象选入当前设备的环境,同时将之前的对象保存。绘画结束后恢复当前设备环境中的GDI对象,程序结束后删除创建的对象。  OLD = CDC *pDC->SelectPbject(创建的对象)//选入,这个函数返回之前的GDI对象。恢复旧对象就是再调用PDC-SelectPbject(OLD ) 画点:通过CDC::SetPixel或CDC::SetPixelV来实现都是在指定坐标设置指定颜色 GetPixel获取颜色 画线:LineTo和MoveTo函数通过Moveto到达一个点然后lineto画到另一个点 折线:Polyline PolyPolyline 和PolylineTo

    MFC异常处理的TRY块的形式如下:

    TRY    {             复合语句    }    CATCH (MFC异常类名, 变量名)    {             复合语句    }    AND_CATCH (MFC异常类名, 变量名)    {             复合语句    }    AND_CATCH (MFC异常类名, 变量名)    {             复合语句    }    ......    END_CATCH

    MFC 串口通讯

    在VS环境下先在系统注册控件,然后在工具栏中加入。从editor combox 等控件获取串口名字后。通过按键消息打开串口

       

    try{

    if (m_mscomm.get_PortOpen())

    {

    //m_mscomm.put_PortOpen(FALSE);

     

    }

    }

    catch (CException* e)

    {

    e = NULL;

    CString str;

    str.Format(" No this COM %d",comIndex);

    //MsgBox::str(str.GetBuffer(MAX_PATH));

    return FALSE;

    }

     

     

    try

    {

     

    m_mscomm.put__CommPort(comIndex);//笔记本选择com

    m_mscomm.put_InBufferSize(MAX_RECV_BUFF);//接收缓冲区1024byte

    m_mscomm.put_OutBufferSize(1024);//发送缓冲区  1024byte

    m_mscomm.put_InputLen(0);//设置当前接收区数据长度为0,表示全部读取

    m_mscomm.put_InputMode(1);//以二进制方式检取数据

    m_mscomm.put_RThreshold(1);//参数1表示每当串口接收缓冲区中有多于或等

     

    //1个字符时将引发一个接收数据的OnComm事件

    m_mscomm.put_Settings(g_comInfo);//串口基本信息

    }

    catch (CMemoryException* e)//

    {

    e = NULL;

    }

    catch (CException* e)

    {

    e = NULL;

    CString str;

    str.Format(" Unable to open COM %d",comIndex);

    //MsgBox::str(str.GetBuffer(MAX_PATH));

    AfxMessageBox(str);

    return FALSE;

    }

     

    try{

    if (!m_mscomm.get_PortOpen())

    {

    m_mscomm.put_PortOpen(true);//这个是打开串口的函数

     

    strOut.Format("\r\n * COM%d open succeeded !",comIndex);

    //AfxMessageBox(strOut);

    //MsgBox::str(strOut.GetBuffer(MAX_PATH));

    return true;

    }

    else

    {

    m_mscomm.put_OutBufferCount(0);

    strOut.Format("\r\n * COM%d open failed !",comIndex);

    //MsgBox::str(strOut.GetBuffer(MAX_PATH));

    return false;

    }

     

    }

    catch (CException* e)

    {

    e = NULL;

    strOut.Format("\r\n * COM%d is erro !",comIndex);

    //MsgBox::str(strOut.GetBuffer(MAX_PATH));

    return false;

    return true;

    }

     

    然后是对于串口事件的处理

    VariantInit(&g_variant_inp);//初始化xxxxxxxxxxx

     

    COleSafeArray safearray_inp;

    long len, k;

     

    CString strtemp;

    if (m_mscomm.get_CommEvent() == 2)//事件值为2表示接收缓冲区内有字符

    {

     

    VariantClear(&g_variant_inp);  //在使用它之前 ,要清除一下xxxxxxxxxxxx

    g_variant_inp = m_mscomm.get_Input();//读缓冲区

    safearray_inp = g_variant_inp;//VARIANT型变量转换为ColeSafeArray型变量

    VariantClear(&g_variant_inp);  //使用后要清除它xxxxxxxxxxxxxxxxxxxxxxx

     

    len = safearray_inp.GetOneDimSize();//得到有效数据长度

    if (len>MAX_RECV_BUFF)

    {

    AfxMessageBox("Recv Data too long");

    len = MAX_RECV_BUFF;

    }

    for (k = 0; k<len; k++)

    {

    safearray_inp.GetElement(&k, g_rxdata + k);//转换为BYTE型数组

    }

     

    //g_recvTxt = "";clear it before fill data

     

    for (k = 0; k<len; k++)//将数组转换为Cstring型变量

    {

    strtemp.Format(TEXT("%c"), *(g_rxdata + k));//字符型

    g_recvTxt += strtemp;

     

    }

     

     

    g_comRecvNum += len;

    CString txt;

    txt.Format("Recv:%d / Len=%d", g_comRecvNum, len);

     

    SetDlgItemText(IDC_RecvNum, txt);

     

    g_readTmOutCnt = 0;

    g_uart_flag_rx = true;

        UpdateData

    转载请注明原文地址: https://ju.6miu.com/read-1297798.html
    最新回复(0)