zoukankan      html  css  js  c++  java
  • MFC的定时器OnTimer

    本文总结来源出自鸡啄米,感谢鸡啄米。来源:http://www.jizhuomi.com/software/232.html

    定时器简介

           定时器,可以帮助开发者或者用户定时完成某项任务。在使用定时器时,我们可以给系统传入一个时间间隔数据,然后系统就会在每个此时间间隔后触发定时处理程序,实现周期性的自动操作。例如,我们可以在数据采集系统中,为定时器设置定时采集时间间隔为1个小时,那么每隔1个小时系统就会采集一次数据,这样就可以在无人操作的情况下准确的进行操作。

           MFC定时器

           VS2010编程中,我们可以使用MFC的CWnd类提供的成员函数SetTimer实现定时器功能,也可以使用Windows API函数SetTimer来实现。两者使用方法实际上很类似,但也有不同。

           CWnd类的SetTimer成员函数只能在CWnd类或其派生类中调用,而API函数SetTimer则没有这个限制,这是一个很重要的区别。因为本教程主要是讲解MFC编程,所以这里就先重点讲解MFC定时器的用法,关于API函数SetTimer的用法鸡啄米会在MFC定时器讲解的基础上进行延伸。

           鸡啄米下面分步骤给出使用MFC定时器的方法。

           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消息的消息响应函数,一种是通过回调函数

           如果要启动多个定时器就多次调用SetTimer成员函数。另外,在不同的CWnd中可以有ID相同的定时器,并不冲突。

           2、为WM_TIMER消息添加消息处理函数,或者定义回调函数。

           如果调用CWnd::SetTimer函数时最后一个参数为NULL,则通过WM_TIMER的消息处理函数来处理定时事件。添加WM_TIMER消息的处理函数的方法是,在VS2010工程的Class View类视图中找到要添加定时器的类,点击右键,选择Properties,显示其属性页,然后在属性页工具栏上点击Messages按钮,下面列表就列出了所有消息,找到WM_TIMER消息,添加消息处理函数。添加后,cpp文件中会出现类似如下内容:

    C++代码
    1. BEGIN_MESSAGE_MAP(CExample44Dlg, CDialogEx)   
    2.     ......   
    3.     ON_WM_TIMER()   
    4. END_MESSAGE_MAP()   
    5.   
    6. void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)   
    7. {   
    8.     // TODO: Add your message handler code here and/or call default   
    9.   
    10.     CDialogEx::OnTimer(nIDEvent);   
    11. }  

           之后就可以在OnTimer函数中进行相应的处理了。OnTimer的参数nIDEvent为定时器ID,即在SetTimer成员函数中指定的定时器ID,如果有多个定时器,我们可以像下面这样处理:

    C++代码
    1. void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)      
    2. {      
    3.     // TODO: Add your message handler code here and/or call default      
    4.     switch (nIDEvent)      
    5.     {      
    6.     case 1:      
    7.         // 如果收到ID为1的定时器的消息则调用func1函数      
    8.         func1();      
    9.         break;      
    10.     case 2:      
    11.         // 如果收到ID为2的定时器的消息则调用func2函数      
    12.         fun2();    
    13.        break;     
    14.     ......      
    15.     default:      
    16.         break;      
    17.     }      
    18.      
    19.     CDialogEx::OnTimer(nIDEvent);      
    20. }     

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

    C++代码
    1. void CALLBACK EXPORT TimerProc(   
    2.   
    3. HWND hWnd, // handle of CWnd that called SetTimer   
    4.   
    5. UINT nMsg, // WM_TIMER   
    6.   
    7. UINT nIDEvent // timer identification   
    8.   
    9. DWORD dwTime // system time   
    10.   
    11. );  

           参数hWnd为调用SetTimer成员函数的CWnd对象的句柄,即拥有此定时器的窗口的句柄;参数nMsg为WM_TIMER,而且总是为WM_TIMER;参数nIDEvent为定时器ID;参数dwTime为系统启动以来的毫秒数,即GetTickCount函数的返回值。

           这样CWnd::SetTimer函数最后一个参数就可以为TimerProc。

           这里注意下,回调函数的名称不一定为TimerProc,可以取其他名字,但返回值类型、参数的类型和个数不能改变。

           鸡啄米给出一个回调函数的例子:

    C++代码
    1. void CALLBACK EXPORT TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime)      
    2. {      
    3.    switch(nTimerid)      
    4.    {      
    5.    case 1:       
    6.          // 处理ID为1的定时器的事件      
    7.          func1();      
    8.          break;      
    9.    case 2:       
    10.          // 处理ID为2的定时器的事件      
    11.          func2();      
    12.          break;    
    13.    ......   
    14.    default:   
    15.         break;     
    16.    }      
    17. }     

           回调函数为全局函数,需要写在使用它的位置的前面,或者写在后面然后在使用之前声明。

           3、销毁定时器。

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

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

           参数nIDEvent为要销毁的定时器的ID,是调用CWnd::SetTimer函数时设置的定时器ID。如果定时器被销毁则返回TRUE,而如果没有找到指定的定时器则返回FALSE。

           如果要销毁多个定时器,则多次调用KillTimer函数并分别传入要销毁的定时器的ID。

           通过Windows API函数使用定时器

           如果我们不使用MFC定时器,而通过Windows API函数使用定时器,其实是很类似的。下面鸡啄米简单说下步骤吧。

           1、启动定时器。

           使用API函数SetTimer启动定时器,SetTimer函数的原型如下:

    C++代码
    1. UINT_PTR SetTimer(         
    2.     HWND    
    3.             hWnd,   
    4.     UINT_PTR    
    5.             nIDEvent,   
    6.     UINT    
    7.             uElapse,   
    8.     TIMERPROC    
    9.             lpTimerFunc   
    10. );  

           参数hWnd为与定时器关联的窗口的句柄;参数nIDEvent为非零的定时器ID,如果hWnd等于NULL,且还不存在ID为nIDEvent的定时器,那么nIDEvent参数被忽略,然后生成一个新ID的定时器,而如果hWnd不为NULL,且hWnd指定的窗口已存在ID为nIDEvent的定时器,那么这个已存在的定时器被新定时器所取代。参数uElapse和lpTimerFunc同CWnd::SetTimer函数。

           2、为WM_TIMER消息添加消息处理函数,或者定义回调函数。

           如果调用SetTimer函数时最后一个参数为NULL,我们需要自己为WM_TIMER消息添加处理函数,要注意的是,WM_TIMER消息的附加数据wParam为定时器ID,lParam为回调函数的指针,如果调用SetTimer时回调函数为NULL,那么lParam也为NULL。

           而如果调用SetTimer函数时最后一个参数不为NULL,我们就需要定义回调函数。回调函数的定义同MFC定时器。

           3、销毁定时器。

           销毁定时器使用KillTimer API函数,原型如下:

    C++代码
    1. BOOL KillTimer(HWND hWnd,UINT_PTR uIDEvent);   

           参数hWnd为与定时器关联的窗口的句柄,与启动定时器时SetTimer函数的hWnd参数值相同;参数uIDEvent为要销毁的定时器的ID,如果传递给SetTimer的参数hWnd有效,则uIDEvent应与传递给SetTimer的参数nIDEvent相同,而如果SetTimer的参数hWnd为NULL,则uIDEvent应为SetTimer返回的定时器ID。该函数成功则返回TRUE,否则返回FALSE。

           MFC定时器应用实例

           鸡啄米给大家演示一个定时器的例子,该实例功能很简单,就是使用两个定时器,定时更新两个编辑框中的显示内容,第一个编辑框每秒刷新一次,从1刷新到10,然后销毁定时器,第二个编辑框每两秒刷新一次,从1刷新到5,然后销毁定时器。下面简单说下步骤:

           1、创建基于对话框的工程,名称设为“Example44”。

           2、在自动生成的对话框模板IDD_EXAMPLE44_DIALOG中,删除“TODO: Place dialog controls here.”静态文本控件。添加两个静态文本框控件,Caption分别设为“1秒钟刷新一次”和“2秒钟刷新一次”,再添加两个个Edit Control控件,ID使用默认的IDC_EDIT1和IDC_EDIT2,两者的Read Only属性都设为True。此时的对话框模板如下图:

    MFC定时器对话框模板

           3、为CExample44Dlg类添加两个成员变量,分别为m_nData1、m_nData2,并在CExample44Dlg类的构造函数中初始化:

    C++代码
    1. CExample44Dlg::CExample44Dlg(CWnd* pParent /*=NULL*/)   
    2.     : CDialogEx(CExample44Dlg::IDD, pParent)   
    3. {   
    4.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);   
    5.     // 两个数据初始化为0   
    6.     m_nData1 = 0;   
    7.     m_nData2 = 0;   
    8. }  

           4、在对话框模板上双击OK按钮,添加点击消息的处理函数,并修改如下:

    C++代码
    1. void CExample44Dlg::OnBnClickedOk()   
    2. {   
    3.     // TODO: Add your control notification handler code here   
    4.     // 启动ID为1的定时器,定时时间为1秒   
    5.     SetTimer(1, 1000, NULL);   
    6.     // 启动ID为2的定时器,定时时间为2秒   
    7.     SetTimer(2, 2000, NULL);   
    8.   
    9.     //CDialogEx::OnOK();   
    10. }  

           这样,点击OK按钮时就不会退出,而是启动两个定时器。

           5、根据上面MFC定时器讲解中为WM_TIMER消息添加处理函数的方法,添加WM_TIMER的消息处理函数OnTimer,并修改其实现如下:

    C++代码
    1. void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)   
    2. {   
    3.     // TODO: Add your message handler code here and/or call default   
    4.     switch (nIDEvent)   
    5.     {   
    6.     case 1:   
    7.         // 如果m_nData1已经达到10,则销毁ID为1的定时器   
    8.         if (10 == m_nData1)   
    9.         {   
    10.             KillTimer(1);   
    11.             break;   
    12.         }   
    13.         // 刷新编辑框IDC_EDIT1的显示   
    14.         SetDlgItemInt(IDC_EDIT1, ++m_nData1);   
    15.         break;   
    16.     case 2:   
    17.         // 如果m_nData2已经达到5,则销毁ID为2的定时器   
    18.         if (5 == m_nData2)   
    19.         {   
    20.             KillTimer(2);   
    21.             break;   
    22.         }   
    23.         // 刷新编辑框IDC_EDIT2的显示   
    24.         SetDlgItemInt(IDC_EDIT2, ++m_nData2);   
    25.     default:   
    26.         break;   
    27.     }   
    28.   
    29.     CDialogEx::OnTimer(nIDEvent);   
    30. }  

           6、运行程序,点击OK按钮,查看效果。

    MFC定时器实例

     、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

    1、在类中定义

    afx_msg void OnTimer(UINT nIDEvent);

    2、在CPP中添加

    BEGIN_MESSAGE_MAP(YuJingDlg, CDialog)
    ON_WM_CTLCOLOR() //颜色消息
    //ON_WM_PAINT()
    ON_WM_TIMER
    ON_BN_CLICKED(IDCANCEL, &YuJingDlg::OnBnClickedCancel)
    END_MESSAGE_MAP()

    3、在初始话函数或其他按钮响应中添加

    SetTimer(1,50,NULL);     //定时器1,50ms刷新一次,使用OnTimer函数

    4、写OnTimer函数  

    void YuJingDlg::OnTimer(UINT nIDEvent)
    {
    th+=0.157;
    if(th>6.28) th-=6.28;
    //Invalidate();
    InvalidateRect(CRect(0,0,500,500));        //刷新区域
    CDialog::OnTimer(nIDEvent);
    }

  • 相关阅读:
    阿里P8架构师谈:阿里双11秒杀系统如何设计?
    秒杀系统设计的知识点
    秒杀系统架构优化思路
    秒杀系统解决方案
    Entity Framework Code First (七)空间数据类型 Spatial Data Types
    Entity Framework Code First (六)存储过程
    Entity Framework Code First (五)Fluent API
    Entity Framework Code First (四)Fluent API
    Entity Framework Code First (三)Data Annotations
    Entity Framework Code First (二)Custom Conventions
  • 原文地址:https://www.cnblogs.com/LouMengzhao/p/5955102.html
Copyright © 2011-2022 走看看