Chinaunix首页 | 论坛 | 博客
  • 博客访问: 589024
  • 博文数量: 752
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5005
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(752)

文章存档

2011年(1)

2008年(751)

我的朋友

分类:

2008-10-13 16:46:27

在你列出的解决方案中,将定时器放入视图是最糟的想法,因为那样的话你就得为每个视图建一个定时器,你应该将定时器视为一种相对有限的资源(这一点在过去尤其如此,现在这个问题考虑得较少)。创建单独的线程对于定时器这样简单的东西又有牛刀弑鸡之嫌。线程无疑使事情复杂化。那么还有第三个方案:在主框架中创建定时器并从中调用文档函数。我会告诉你如何用这个方案直截了当地实现所要的功能,然后我还会展示另外一种你没有想到的方法。
假设你不愿意用第三种方案的原因是它需要从主框架中调用文档,这样做有点丑陋。(为什么框架要掺乎到文档中去呢?)但有一种直截了当的方法来做,不用直接调用CMyDoc::DoTimerThing,你可以将WM_TIMER消息转换成一个ID为ID_APPTIMER的WM_COMMAND,并用通常的方式广播这个命令以便文档能用ON_COMMAND处理它,文档无法处理所有的窗口消息,但它们可以处理WM_COMMAND。事实上,这是MFC命令处理例程体系结构的主要创新之一,它使非窗口对象可以处理命令。所以这样看来,你要做的事情就是:
--------------------next---------------------
也就是说,当主框架得到定时器信号的同时,也向自己发送了一个ID_APPTIMER命令。MFC会将这个命令发送到系统,任何具有ON_COMMAND处理器并能处理ID_APPTIMER命令的对象都可以处理这个事件。你可以用ON_COMMAND_EX来对付多个对象处理相同事件的情况。
这样做虽然能行得通,但有一个问题。MFC只把命令发送到活动视图/文档。如果其它文档处于打开状态,但没有被激活,则它们不会得到WM_COMMAND消息。虽然你可以修改程序把命令广播到非激活文档,但那样的话,像“文件|保存”这样普通的命令会被发送到所有的文档——很狼狈!因为我们只需要定时器命令到达所有文档。怎么办呢?如何发送WM_COMMAND到所有的文档?
MFC将命令发送到文档这样的非窗口对象,其方法是通过虚函数CCmdTarget::OnCmdMsg来实现的。当窗口获得WM_COMMAND消息时,它要运行许多CWnd代码和虚函数。最终,控制到达CWnd::OnCommand,由它调用OnCmdMsg。
--------------------next---------------------
//CDocIterator 类代码
//
DocIter.h 
//
class CDocIterator {
protected:
    CPtrList      m_doclist;
    CDocument*    m_pDoc;
    POSITION      m_pos;

public:
    CDocIterator();

    // get current doc
    CDocument* doc() {
        return m_pDoc;
    }

    // move to 1st doc: not needed after construct
    BOOL First() {
        m_pDoc = NULL;
        m_pos = m_doclist.GetHeadPosition();
        return Next();
    }

    // move to next doc
    BOOL Next() {
        if (m_pos) {
            m_pDoc = (CDocument*)m_doclist.GetNext(m_pos);
        } else {
            m_pDoc=NULL;
        }
        return m_pDoc != NULL;
    }

    // next, ++ style
    const CDocIterator& operator++(int) {
        Next();
        return *this;
    }

    // for "for" loops
    BOOL operator()() {
        return m_pDoc != NULL;
    }
};

DocIter.cpp 
//
#include "stdafx.h"
#include "DocIter.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Constructor intitializes document list and goes to first doc.
//
CDocIterator::CDocIterator()
{
    CWinApp* pApp = AfxGetApp();
   POSITION pos1 = pApp->GetFirstDocTemplatePosition();
   while (pos1) {
      CDocTemplate* ptempl = (CDocTemplate*)pApp->GetNextDocTemplate(pos1);
      POSITION pos2 = ptempl->GetFirstDocPosition();
      while (pos2) {
         CDocument *pdoc;
         if ((pdoc=ptempl->GetNextDoc(pos2)) != NULL) {
                m_doclist.AddHead(pdoc);
            }
      }
   }
    First();
}
//

--------------------next---------------------
//Implementing a Timer 
////////////////////////////////////////////////////////////////
//
#include "stdafx.h"
#include "doctime.h"
#include "Doc.h"
#include "DocIter.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
   ON_COMMAND_EX(ID_APPTIMER, OnAppTimer)
END_MESSAGE_MAP()

int CMyDoc::g_nIDTimer = 0;
int CMyDoc::g_nDocObj = 0;

// timer proc called by windows whenever the timer clicks
//
void WINAPI MyTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, 
                        DWORD dwTime)
{
   CDocIterator it;
   for (it.First(); it.doc(); it.Next()) {
      it.doc()->OnCmdMsg(ID_APPTIMER, CN_COMMAND, NULL, NULL);
   }
}

// constructor creates timer the first time a doc is created
//
CMyDoc::CMyDoc()
{
   m_count = 0;
   if (g_nDocObj++ == 0) {
      g_nIDTimer = SetTimer(NULL,0,1000,MyTimerProc);
   }
}

// destructor kills timer when the last doc goes bye-bye
//
CMyDoc::~CMyDoc()
{
   if (—g_nDocObj == 0) {
      KillTimer(NULL,g_nIDTimer);
   }
}

// Handle timer event: this is where you would do your background data
// collection or whatever you want to do. This sample simply increments a
// counter and updates the views. 
//
BOOL CMyDoc::OnAppTimer(UINT nID)
{
   m_count++;
   UpdateAllViews(NULL);
   return FALSE;
}
//

--------------------next---------------------
当然,如果你在自己的文档类中做所有的事情,定时器过程可直接调用CMyDoc::DoSomething来代替将定时器事件转换为命令事件。
使用定时器过程是稍微复杂了一些,但这是我喜欢的方法。从概念上讲,定时器是一个任何对象都能使用的全局服务。你应该能实现一些种类的定时器对象,以便可以随时信手摘来把它用到自己的程序中去,然后不同的对象能“侦听”它。那么如何实现这个小东东呢?
很容易。你要做的全部工作就是改变前面所述的两种方法中以文档为中心的做法。首先,将定时器过程和SetTimer/KillTimer调用放入一个单独的CAppTimer类中,将它广播到任意的命令目标列表。你只需要在这个列表中添加和删除对象。
DocTime2就是用这种方法实现的。它使用两个新类: 和。前者是一个通用的命令目标列表类,其对象可以调用这个类中的函数将自己在列表中注册和注销(添加和删除),SendCommand函数用于将某个命令ID广播到每一个列表对象。CAppTimer从CCmdTargetList派生,因为别的东西相比,此定时器类是个命令目标列表。CAppTimer操作着这个定时器并提供一个单一全局实例——theTimer,它有一个应用必须调用以设置定时器的Init函数。
--------------------next---------------------
 

CmdTargList.h 
////////////////////////////////////////////////////////////////
//
#pragma once

// List of command targets. Targets can register and unregister, and
// SendCommand lets you send a command (CN/WM_COMMAND) to all the targets 
// on the list. This is a general class that has nothing per se to do 
// with timers.
//
class CCmdTargetList {
protected:
   CPtrList m_list;                     // the list

public:
   // Register command target to receive commands sent to this list
   //
   void Register(CCmdTarget* pTarg) {
      m_list.AddHead(pTarg);     
   }

   // Unregister command target from list
   //
   void Unregister(CCmdTarget* pTarg) {
      POSITION pos = m_list.Find(pTarg);
      if (pos) {
         m_list.RemoveAt(pos);
      }
   }

   // Send command to all command targets on this list
   //
   int SendCommand(UINT nID);
};

CmdTargList.cpp 
////////////////////////////////////////////////////////////////
//
#include "stdafx.h"
#include "doctime.h"
#include "CmdTargList.h"
#include "AppTimer.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Send a command to all command targets on this list
//
int CCmdTargetList::SendCommand(UINT nID)
{
   int count=0;
   POSITION pos = m_list.GetHeadPosition();
   while (pos) {
      CCmdTarget* pTarg = (CCmdTarget*)m_list.GetNext(pos);
      if (pTarg) {
         pTarg->OnCmdMsg(nID, CN_COMMAND, NULL, NULL);
         count++;
      }
   }
   return count;
}

--------------------next---------------------
 

AppTimer.h 
////////////////////////////////////////////////////////////////
//
#include "CmdTargList.h"

//////////////////
// Timer object is a list of cmd targets to receive timer command/event.
//
class CAppTimer : public CCmdTargetList {
protected:
   static void WINAPI CAppTimer::TimerProc(HWND, UINT, UINT_PTR, DWORD);
   UINT m_nIDTimer;        // timer ID
   UINT m_nIDTimerCmd;     // command ID to send when timer clicks

public:
   CAppTimer();
   ~CAppTimer();
   void Init(int msec, UINT nIDCmd);
};

// THE timer
extern CAppTimer theTimer;

AppTimer.cpp 
////////////////////////////////////////////////////////////////
//
#include "stdafx.h"
#include "resource.h"
#include "AppTimer.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// global timer object
CAppTimer theTimer;

void WINAPI CAppTimer::TimerProc(HWND hwnd,
   UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
   theTimer.SendCommand(theTimer.m_nIDTimerCmd);
}

CAppTimer::CAppTimer()
{
   ASSERT(this==&theTimer); // only one global timer allowed
}

CAppTimer::~CAppTimer()
{
   KillTimer(NULL, m_nIDTimer);
}

// Initialize: set timer and command ID to use
//
void CAppTimer::Init(int msec, UINT nIDCmd)
{
   ASSERT(this==&theTimer);
   m_nIDTimerCmd = nIDCmd;
   m_nIDTimer = SetTimer(NULL, 0, msec, TimerProc);
}

--------------------next---------------------

阅读(249) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~