MFC定时器

巧妙地使用定时器能达到意想不到的效果,写界面的时候能实现渐变,也能帮助多线程控制等
我们知道,在VC的MFC中,已经为我们封装好了很多全面和强大的函数集,所以在MFC编程时,巧妙地调用MFC函数库可以为我们省去很多麻烦。其中定时器也可以在MFC程序中很好地利用。

在MFC中和定时器相关的有三个函数:
1.设置定时器(定义一个定时器的属性):
       SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD));
2.定时器响应(响应系统定义WM_TIMER消息):
         OnTimer( UINT nIDEvent );
3.释放定时器:
       KillTimer( int nIDEvent );
其中:
UINT nIDEvent:定时器的ID,在一个程序中用这个ID来确定是那个定时器发送的消息。
UINT nElapse: 定义刷新时间,即间隔多长时间刷新一次,单位是毫秒。
void (CALLBACK EXPORT* lpfnTimer)( HWND, UINT, UINT,DWORD):回调函数的参数,实现刷新时所做的操作,一般情况下都设为0。

在这里就用一个简单的例子来说明定时器在MFC中是如何使用的。
1.建立单文档程序Timer。
2.在resource.h中定义两个定时器的ID
#define TIMER1 1
#define TIMER2 2
3.在CMainFrame的OnCreate函数中定义两个定时器的属性。
SetTimer(TIMER1,3000,0);
SetTimer(TIMER2,5000,0);
4.在类视图中右击CMainFrame属性,在消息响应函数中找到WM_TIMER,然后添加响应函数OnTimer()。
void CMainFrame::OnTimer(UINT nIDEvent)
{
    // TODO: Add your message handler code here and/or calldefault
   switch(nIDEvent)
   {
      case TIMER1:
     
            AfxMessageBox("定时器1!");
            break;
    }
    case TIMER2:
    
           AfxMessageBox("定时器2!");
           break;
    }
   default:
        break;
   }
  CFrameWnd::OnTimer(nIDEvent);
}

5.在CMainFrame的析构函数中添加释放定时器函数。
             KillTimer(TIMER1);
                KillTimer(TIMER2);

这样,一个简单的定时器程序就生成了,运行后可以看到,每隔3秒就会弹出一个消息对话框“定时器1”,每隔5秒就会弹出一个消息对话框“定时器2”。

这种情况是在程序运行后定时器就直接启动了,很多时候我们需要的是在响应一次事件或者说点击一个按钮后再去执行定时器,那么这时应该怎么修改呢?也用一个实例来说明吧,因为我觉得可能说一大堆废话也抵不上一个简单而正确的实例更具说服力。

另一个人做的

现在,我生成了一个MFC多文档应用程序,我希望在点击菜单条上的某个按钮后再去执行定时器。这时就不是在CMainFrame里面操作了,因为我们是要在对话框上进行定时器的操作,而CView这个类是负责对话框的对应操作,所以要把定时器定义在CView里面。
1.建立多文档程序Timer。
2.在resource.h中定义两个定时器的ID
#define TIMER1 1
#define TIMER2 2
3.在CTimerView的OnInitialUpdate函数中定义两个定时器的属性。
SetTimer(TIMER1,3000,0);
SetTimer(TIMER2,5000,0);
4.在类视图中右击CTimerView属性,在消息响应函数中找到WM_TIMER,然后添加响应函数OnTimer()。
void CTimerView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or calldefault
//Hwnd为一个变量,初始值为false,点击某个按钮后在其响应函数中将其置为true
switch(nIDEvent && Hwnd){
case TIMER1:
{
AfxMessageBox("定时器1!");
break;
}
case TIMER2:
{
AfxMessageBox("定时器2!");
break;
}
default:
break;
}
CFrameWnd::OnTimer(nIDEvent);
}
5.在CTimerView中添加OnDestroy()函数,在函数中释放定时器。
KillTimer(TIMER1);
KillTimer(TIMER2);
现在,运行程序后,没有马上弹出预定的对话框,而是在我们点击某个按钮或者响应某个事件后对话框才弹出,同样是点击按钮后,每隔3秒弹出对话框“定时器1”,每隔5秒弹出“定时器2”。
其实在不同的类,定时器的用法都是基本一致的,只要找到类的初始函数和释放函数,就能轻松实现定时功能了,就这么简单。

 

基于对话框的定时器程序

1.打开VC,新建一基于对话框的工程,工程名为Test在对话框上添加一按钮,将其ID改为IDC_BUTTON_START,Caption改为Start. 映像该按钮的BN_CLICKED消息,voidCTestDlg::OnButtonStart();

2.再在对话框上添加一按钮,ID为ID_BUTTON_STOP,Caption改为Stop,映像消息为voidCTestDlg::OnButtonStop();

3.添加一个Lable,ID改为IDC_STATIC_TIME,用于记数,表明定时器函数的执行。

4.映像对话框的WM_TIMER消息,void CTestDlg::OnTimer(UINT nIDEvent);


   以上的实现函数如下所示:

void CTestDlg::OnButtonStart()

{

SetTimer(1,1000,NULL);//启动定时器1,定时时间是1秒

}

 

void CTestDlg::OnButtonStop()

{

KillTimer(1);       //关闭定时器1。

}

 

void CTestDlg::OnTimer(UINT nIDEvent)

{

static int nTimer=0;

CString strTmp="";

strTmp.Format("Timer:   %d",nTimer++);

CWnd *pWnd=GetDlgItem(IDC_STATIC_TIME);

pWnd->SetWindowText(strTmp);//在Lable中设置新值,表明定时器已经工作。

CDialog::OnTimer(nIDEvent);

}

 

回调函数的使用。

如果不想使用窗体的WM_TIMER消息函数处理,可以使用回调函数来取代,读者可以在上面例子的基础上,增加一个回调函数,以证实前面的讨论。

首先,定义一个回调函数,回调函数的定义必须按照如下格式。

void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORDdwTime );

 

我的实现函数如下:

void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORDdwTime )

{

AfxMessageBox("Timer is running!");//定时器时间到,强出一对话框,表明定时器已经运行。

}


将上面的启动函数稍做修改

void CTestDlg::OnButtonStart()

{

//   SetTimer(1,1000,NULL);//启动定时器1,定时时间是1秒

SetTimer(1,1000,(TIMERPROC)TimerProc);//用回调函数处理,此时对话框的消息处理函数不再处理。

}

<think>好的,用户现在想了解MFC中的定时器。之前他问过关于等待光标的问题,现在转向定时器,可能是想学习如何在MFC应用程序中处理时间相关的任务。我需要先回顾MFC定时器的基本概念和使用方法。 首先,用户可能想知道如何设置和启动定时器。我应该从CWnd类的SetTimer方法开始介绍,说明参数的含义,比如定时器ID和时间间隔。同时,要提到WM_TIMER消息的处理,以及如何通过OnTimer函数来响应定时事件。 接下来,用户可能需要知道如何停止定时器,使用KillTimer方法。这部分应该简明扼要,强调ID的重要性,确保停止正确的定时器。 然后,考虑到用户可能有不同的使用场景,比如单次触发和周期性触发,我需要举例说明。比如设置一个只触发一次的定时器,可以用SetTimer的参数,或者结合标志位在OnTimer中处理。另外,多定时器的使用场景也很常见,需要说明如何管理多个ID,并分别处理它们的事件。 还要注意定时器的精度问题,因为WM_TIMER是基于消息循环的,精度可能不高,大约55毫秒左右。如果需要更高精度的定时,可能需要提到多媒体定时器或其他方法,但用户可能暂时不需要那么高级的内容,所以简要提及即可。 另外,用户可能在多线程环境中使用定时器,需要提醒他们定时器消息是在主线程处理的,避免在回调中执行耗时操作,否则会导致界面卡顿。这时可能需要建议将耗时任务放在工作线程,或者使用PeekMessage处理消息,保持界面响应。 常见问题方面,用户可能会遇到定时器不触发的情况,这时候需要检查窗口是否有效,消息循环是否被阻塞。比如在长时间循环中没有处理消息,定时器消息就无法被处理。此外,多次调用SetTimer可能导致多个定时器同时运行,需要确保正确管理ID。还有资源泄漏的问题,使用KillTimer及时释放资源。 最后,补充一些高级用法,比如结合CWinThread创建独立的定时器线程,或者使用时间事件处理器类来封装定时器逻辑,提高代码的可维护性。这些内容可以作为扩展,供用户进一步学习参考。 总结一下,回答的结构应该包括基本使用步骤、不同使用场景的示例、精度说明、多线程注意事项、常见问题及解决方案,以及高级用法的提示。这样用户可以从基础到进阶全面了解MFC定时器的使用,同时避免常见的陷阱。</think>在MFC中使用定时器(Timer)可通过以下方式实现: --- ### **一、基础使用步骤** 1. **启动定时器** ```cpp // 在窗口类中设置(如View/Dialog/Frame窗口) // 参数1: 定时器ID(自定义非零值) // 参数2: 间隔时间(毫秒) // 返回值: 实际创建的定时器ID(失败返回0) UINT_PTR nTimerID = SetTimer(1001, 1000, NULL); // 每1秒触发 ``` 2. **处理定时消息** ```cpp // 在消息映射中添加 BEGIN_MESSAGE_MAP(CMyView, CView) ON_WM_TIMER() END_MESSAGE_MAP() // 实现OnTimer函数 void CMyView::OnTimer(UINT_PTR nIDEvent) { switch(nIDEvent) { case 1001: // 定时任务逻辑 UpdateData(); // 示例:更新界面 break; } CView::OnTimer(nIDEvent); } ``` 3. **销毁定时器** ```cpp KillTimer(1001); // 通过ID停止定时器 ``` --- ### **二、典型使用场景** #### 1. 单次触发定时器 ```cpp // 设置单次定时器(3秒后触发) SetTimer(1002, 3000, NULL); // 在OnTimer中处理 void CMyView::OnTimer(UINT_PTR nIDEvent) { if(nIDEvent == 1002) { KillTimer(1002); // 立即销毁 MessageBox(L"3秒时间到!"); } } ``` #### 2. 多定时器管理 ```cpp // 设置不同ID的定时器 SetTimer(1001, 500, NULL); // 0.5秒刷新界面 SetTimer(1002, 2000, NULL); // 2秒保存数据 // 通过ID区分处理 void CMyView::OnTimer(UINT_PTR nIDEvent) { switch(nIDEvent) { case 1001: /* 高频任务 */ break; case 1002: /* 低频任务 */ break; } } ``` --- ### **三、重要注意事项** 1. **精度问题**: - WM_TIMER消息精度约55ms(受系统时钟周期限制) - 实际触发时间 = 设定时间 + 0~55ms随机值 2. **多线程限制**: - 定时器消息仅在创建线程的消息队列中处理 - 耗时操作需配合工作线程: ```cpp // 在OnTimer中启动线程 AfxBeginThread(MyThreadFunc, this); ``` 3. **资源管理**: - 窗口销毁时会自动销毁关联定时器 - 建议在OnDestroy()中显式调用KillTimer --- ### **四、常见问题解决方案** 1. **定时器不触发**: - 检查窗口是否有效(隐藏/销毁的窗口无法接收消息) - 确认消息循环正常(长时间阻塞操作中需调用`PeekMessage`) 2. **精度补偿技巧**: ```cpp // 记录实际触发时间 DWORD dwActualTime = GetTickCount(); // 下次触发时间计算时补偿偏差 ``` 3. **定时器冲突**: - 使用ID分配管理器 - 推荐模式: ```cpp class CMyTimerIDs { public: static const UINT LOG_TIMER = 1001; static const UINT UI_UPDATE_TIMER = 1002; // 更多ID定义... }; ``` --- ### **五、高级用法** 1. **多媒体定时器(高精度)**: ```cpp #include <mmsystem.h> #pragma comment(lib, "winmm.lib") // 精度可达1ms(但消耗更多资源) timeSetEvent(100, 1, TimerProc, 0, TIME_PERIODIC); ``` 2. **定时器封装类**: ```cpp class CAutoTimer { public: CAutoTimer(CWnd* pWnd, UINT nElapse) { m_pWnd = pWnd; m_nID = pWnd->SetTimer(1001, nElapse, NULL); } ~CAutoTimer() { m_pWnd->KillTimer(m_nID); } private: CWnd* m_pWnd; UINT_PTR m_nID; }; // 使用示例 { CAutoTimer timer(this, 1000); // 作用域内有效 // 执行操作... } // 自动销毁定时器 ``` --- 通过合理使用定时器,可以实现数据采集、界面刷新、定时保存等关键功能。建议优先使用窗口定时器,需要高精度时再考虑多媒体定时器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值