基于对话框的应用中执行空闲状态处理

来源:互联网 发布:热血无赖 ps4 淘宝 编辑:程序博客网 时间:2024/04/27 16:11
可以使用在mainframe中一样得ON_UPDATE_COMMAND_UI宏。可以更新对话框中得子控件,但是对对话框中得工具条按钮不起作用。必须另外处理。
简单如下
1.
#define WM_KICKIDLE 0x036A
LRESULT CTestDlg2Dlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class
    if(message == WM_KICKIDLE)
    {
        UpdateDialogControls(this,true);     
    }
}
2.自己添加处理ON_UPDATE_COMMAND_UI宏就可以了。


June 1995,Microsoft System Journal
  
  Paul DiLascia 是一个自由软件顾问,专长是训练和软件开发(C++ and Windows).他是Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992)的作者.
   问:我的问题是OnIdle在通常的文档/视图程序中可以工作,但是看起来在基于对话框的程序中不行。我的CApp::InitInstance调用 dlg.DoModal,调用一个函数:不调用OnIdle的CWnd::RunModalLoop。我想我应该在WM_ENTERIDLE中做一些后台 处理,但是这个消息是发送到对话框的父窗口的。在我的这种情况下,父窗口不存在。请帮忙!
  Jim Kallimani
  如你所见, “模态”对话框在MFC4.0中实际上是非模态的。当你调用CDialog::DoModal的时候,MFC并不调用::DialogBox,像它以前所 做的,而是调用CreateDialogIndirect (在三思之后)然后通过禁用父窗口并且进入自己的消息循环模拟模态行为。这是::DialogBox所做的本质工作。这样做的好处是MFC拥有对话框的消 息循环,以前它被Windows API函数::DialogBox隐藏起来了。这样MFC通过通常MFC的消息泵CWinThread::PumpMessage取模态对话框消息,像其 他窗口一样。特别的,你可以为模态对话框重载CWnd::PreTranslateMessage—例如实现加速键。MFC的早期版本允许你为模态对话框 实现你自己的PreTranslateMessage,但是没有被系统调用,因为CDialog::DoModal直接执行::DialogBox,直到 你的对话框消息处理函数调用EndDialog才返回。同样,使用::DialogBox, 使用通常的MFC方法进行消息处理是不可能的,因为程序控制消失在::DialogBox中,直到对话框结束才返回。
  作为替代方案, Windows有它自己的机制, WM_ENTERIDLE, 在模态对话框中进行消息处理。当处理完一个或多个消息之后,Windows 发送 WM_ ENTERIDLE 到一个模态对话框或菜单的所有者,如果消息队列中没有等待的消息的话。只有模态对话框发送WM_ENTERIDLE,而非模态对话框不发送。因为MFC现 在使用非模态对话框,甚至是在使用模态对话框的时候实际上也是使用非模态对话框,MFC不得不自己发送WM_ENTERIDLE以模仿模态对话框—-但是 仅当对话框有父窗口的时候才这么干。Jim碰到麻烦,因为没有父窗口来接收WM_ENTERIDLE。你的头快昏了吗?
  如果MFC通过标准消 息泵取模态对话框消息,为什么不调用CWinApp::OnIdle作为自己消息处理的一部分?为题是CWnd::RunModalLoop 调用了CWinThread::PumpMessage但是OnIdle在CWinThread::Run中出现。当你的应用程序调用了 InitInstance函数之后,MFC调用CWinThread::Run运行你的应用程序。CWinThread::Run的浓缩形式看起来像这 样:
  // (from THRDCORE.CPP)
  int CWinThread::Run()
  {
   // 为了空闲状态处理
   BOOL bIdle = TRUE;
   LONG lIdleCount = 0;
   for (;;) {
   while (bIdle && !::PeekMessage(...)) {
   //当在bIdle状态时调用OnIdle
   if (!OnIdle(lIdleCount++))
   // 假设"非空闲" 状态
   bIdle = FALSE;
   }
   // 获取/预处理/分派消息
   // (调用 CWinThread::PumpMessage)
   }
  }
   我砍掉了很多,以强调空闲处理如何工作。如果没有消息在等待,MFC重复调用CWinThread::OnIdle,传递给它每次增加的一个计数器参 数。你可以使用这个参数区分不同种类的空闲处理的优先次序。你可能在空闲计数为1时作格式化,空闲计数为2时更新一个指示当天时间的时钟。当你的 OnIdle返回FALSE时,MFC停止调用它并且等待,直到你的线程得到另一个消息,因此空闲循环从头开始。
  模态对话框从不执行这个代 码,因为CWnd::RunModalLoop直接在自己的消息循环中调用CWinThread::PumpMessage。它没有调用 CWinThread::Run,因此从不调用CWinThread::OnIdle。 Redmond 的人员告诉我这是由设计上决定的。显然,在模态对话框中调用OnIdle是危险的,因为许多消息处理函数建立临时CWnd对象,它们被期望在对话框生存期 中存在。默认空闲处理的一部分就是释放临时句柄映射。(译者注:临时CWnd对象依赖于临时句柄映射而存在。.)
  (我不得不告诉你,依我所见,整个MFC用来连接HWND和CWnd的临时/永久句柄映射机制是整个架构中的灾难之一,甚至比它们的消息映射还要坏。临时映射机制的问题不断出现在程序中—特别是在多线程应用中,使得他们很难用MFC编写。)
   这样看来,你如何在基于对话框的应用程序中进行消息处理,当对话框没有父窗口的时候?幸运的是,它易如反掌。MFC开发者提供一个钩子: WM_KICKIDLE。 RunModalLoop 不断发送这个MFC私有消息,当消息队列中没有消息的时候—就像CWinThread::Run调用OnIdle一样。 RunModalLoop甚至还为你提供一个计数器并且依次递增。实际上,WM_KICKIDLE是对话框的OnIdle替代品。 (历史信息:早期版本的MFC为属性表作这个模态/非模态切换和提供WM_KICKIDLE。显然它工作的如此之好,以至于他们决定使所有的模态对话框非 模态化。)
  要警告你的是:你可能在OnKickIdle函数中,想调用你的主应用程序的OnIdle函数
  LRESULT CMyDlg::OnKickIdle(WPARAM, LPARAM lCount)
  {
   return AfxGetApp()->OnIdle(lCount);
  }
  MFC人员告诉我这是危险的;因为临时映射问题。在OnKickIdle中执行你的空闲处理会更安全一些。如果有必要,你可以组合共有的空闲处理成为一个辅助函数,在CApp::OnIdle 和 CMyDlg::OnKickIdle中调用。
  当我在处理空闲处理的问题的时候,发现不是所有程序员都知道CDocTemplate和CDocument的OnIdle函数! 如果你要在文档或文档模板中执行空闲处理,只需重载这些函数。
  
  
  Internet:
  Paul DiLascia
  72400.2702@compuserve.com
  From the June 1996 issue of Microsoft Systems Journal.
  July 1997,Microsoft System Journal
  ......
   下面我将指出如何使ON_COMMAND_UPDATE_UI处理函数在对话框中工作。在通常的MFC文档/视图应用中,MFC使用内部消息 WM_IDLEUPDATECMDUI更新菜单项、工具栏按钮、状态栏格等用户界面对象。作为空闲处理的一部分,IDLEUPDATECMDUI广播到所 有你的应用程序的窗口。工具栏、状态栏和对话栏的命令处理依次调用UpdateDialogControls广播另一个命令, CN_UPDATE_COMMAND_UI,到窗口上的所有控件。从你的程序员的角度来看,这些消息是不可见的。你只需实现 ON_UPDATE_COMMAND_UI处理你的菜单项和按钮,然后,看? 它们被变魔法似的更新了。(需要更多信息的话,参见我的在1995年6月MSJ.上的文章 "Meandering Through the Maze of MFC Message and Command Routing" )
   不幸的是,这个奇妙的UI更新机制不能用于对话框—至少不是自动的。 你必须自己修补一下。幸好它很简单。你只需处理WM_KICKIDLE,一个MFC私有消息;当对话框空闲时发送出来(类似应用程序的OnIdle处理) 给.你自己调用UpdateDialogControls。.
  LRESULT CTabDialog::OnKickIdle(WPARAM wp,
   LPARAM lCount)
  {
   UpdateDialogControls(this, TRUE);
   return 0;
  }
  
  CWnd::UpdateDialogControls发送魔术般的CN_ UPDATE_COMMAND_UI 消息给所有对话框控件,结果是现在ON_COMMAND_ UPDATE_UI处理突然在对话框中可以工作了。
原创粉丝点击