滚动条控件(模式对话框+后台线程处理)

来源:岁月联盟 编辑:exp 时间:2012-03-20

有时候,在用户处理大量数据,或是在网络通信过程中,希望自己的主线程阻塞,等待该过程完成后,再继续执行,但同时由于该处理过程耗时较长,会让用户体验下降。这时候我们会想到使用一个滚动条来通知用户当前程序的执行进度,防止用户以为程序长时间不动,造成的误认为程序已死,强制关闭。

         这时候最简单的一个做法就是,制作一个模式对话框,然后在该对话框中执行一个线程,将用户需要执行的操作放在该线程中来执行。在程序中,创建自己的一个对话框资源,添加滚动条,以及相应的Text模块说明进度在执行的操作。这时,最大的问题在于,怎么让滚动条模态阻塞后,执行数据处理部分的线程代码。

这时候,我的解决办法是采用观察者模式,创建一个观察者类,提供一个CallBack的方法。在进度条对话框中提供SetCallBack(CObserver*)的方法设置观察者对象。这样在需要处理数据的类中,就可以继承CallBack的方法,然后所有的耗时处理过程,都放在那里面执行。然后在进度条对话框中创建的线程中,调用观察者的CallBack方法。好吧,还是直接上源码吧。


[cpp] // DlgProcessBar.cpp : implementation file  
//  
 
#include "stdafx.h"  
#include "DlgProcessBar.h"  
#include "process.h"  
#include "../Observer.h"  
// CDlgProcessBar dialog  
IMPLEMENT_DYNAMIC(CDlgProcessBar, CDialog) 
 
CDlgProcessBar::CDlgProcessBar(CWnd* pParent /*=NULL*/) 
    : CDialog(CDlgProcessBar::IDD, pParent) 

    ob = NULL; 
    m_hThread = NULL; 

 
CDlgProcessBar::~CDlgProcessBar() 

    ob = NULL; 
    if (NULL != m_hThread) 
    { 
        WaitForSingleObject(m_hThread,INFINITE); 
        m_hThread = NULL; 
    } 

 
void CDlgProcessBar::DoDataExchange(CDataExchange* pDX) 

    CDialog::DoDataExchange(pDX); 
    DDX_Control(pDX, IDC_PROGRESS, m_ProcessCtl); 
    DDX_Control(pDX, IDC_STATIC_NOTE, m_StaticCtl); 

 
 
BEGIN_MESSAGE_MAP(CDlgProcessBar, CDialog) 
    ON_WM_CTLCOLOR() 
END_MESSAGE_MAP() 
 
 
// CDlgProcessBar message handlers  
 
unsigned __stdcall CDlgProcessBar::ThreadProc(void *pArgParam) 

    TRACE("执行回调函数/r/n"); 
    if (NULL == pArgParam) 
    { 
        TRACE("请设置线程参数/r/n"); 
        _endthreadex(0); 
    } 
    CDlgProcessBar *dlg = (CDlgProcessBar *)pArgParam; 
    if (NULL == dlg->ob) 
    { 
        TRACE("请设置观察者/r/n"); 
        _endthreadex(0); 
    } 
    TRACE(L"Call CallBack Function/r/n"); 
    dlg->ob->CallbackFunc(); 
    TRACE(L"End CallBack Function/r/n"); 
    if (NULL != dlg->GetSafeHwnd()) 
    { 
        TRACE(L"Post Message/r/n"); 
        dlg->PostMessage(WM_COMMAND, 
            MAKEWPARAM(IDOK,BN_CLICKED),(LPARAM)dlg->GetSafeHwnd()); 
    } 
    return 0; 

BOOL CDlgProcessBar::SetCallBack(CObserver *ob) 

    if (NULL == ob) 
    { 
        TRACE("设置观察者失败"); 
        return FALSE; 
    } 
    this->ob = ob; 
    return TRUE; 

BOOL CDlgProcessBar::SetNotice(const CString ¬ice /* = L"正在处理..." */, 
     const COLORREF &color /* = RGB */) 

    m_TextColor = color; 
    m_StaticCtl.SetWindowText(notice); 
    RedrawWindow();         // ??  
    return TRUE; 

BOOL CDlgProcessBar::InitProcessBar(const int &range) 

     
    m_ProcessCtl.SetRange(0,range); 
    return TRUE; 

BOOL CDlgProcessBar::SetProcessBar(const int &inc) 

    int iCurPos = m_ProcessCtl.GetPos(); 
    m_ProcessCtl.SetPos(iCurPos + inc); 
    return TRUE; 

HBRUSH CDlgProcessBar::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 

    switch (nCtlColor) 
    { 
    case CTLCOLOR_STATIC: 
        pDC->SetTextColor(m_TextColor); 
        pDC->SetBkMode(TRANSPARENT); 
        return (HBRUSH)GetStockObject(NULL_BRUSH); 
    default: 
        return CDialog::OnCtlColor(pDC,pWnd,nCtlColor); 
    } 

BOOL CDlgProcessBar::OnInitDialog() 

    CDialog::OnInitDialog(); 
    m_TextColor = RGB(255,0,0); 
    m_hThread = NULL; 
    m_threadID = 0; 
    if (NULL == ob) 
    { 
        TRACE("观察者设置失败"); 
        return FALSE; 
    } 
    m_hThread = (HANDLE)_beginthreadex(NULL,0,ThreadProc,(void*)this,0,&m_threadID); 
    if (NULL == m_hThread) 
    { 
        TRACE("创建线程失败"); 
        OnOK(); 
    } 
    return TRUE;  // return TRUE unless you set the focus to a control  
    // EXCEPTION: OCX Property Pages should return FALSE  

BOOL CDlgProcessBar::PreTranslateMessage(MSG* pMsg) 

    // TODO: 在此添加专用代码和/或调用基类www.2cto.com 
    if (pMsg->message==WM_KEYDOWN && (pMsg->wParam==VK_RETURN || 
     pMsg->wParam==VK_ESCAPE || pMsg->wParam==VK_SPACE)) 
    { 
        return TRUE; 
    } 
    return CDialog::PreTranslateMessage(pMsg); 

void CDlgProcessBar::OnOK() 

    // TODO: Add your specialized code here and/or call the base class  
    if (NULL != m_hThread) 
    { 
        WaitForSingleObject(m_hThread,INFINITE); 
        m_hThread = NULL; 
    } 
    ob = NULL; 
    CDialog::OnOK(); 

// DlgProcessBar.cpp : implementation file
//

#include "stdafx.h"
#include "DlgProcessBar.h"
#include "process.h"
#include "../Observer.h"
// CDlgProcessBar dialog
IMPLEMENT_DYNAMIC(CDlgProcessBar, CDialog)

CDlgProcessBar::CDlgProcessBar(CWnd* pParent /*=NULL*/)
 : CDialog(CDlgProcessBar::IDD, pParent)
{
    ob = NULL;
    m_hThread = NULL;
}

CDlgProcessBar::~CDlgProcessBar()
{
    ob = NULL;
    if (NULL != m_hThread)
    {
        WaitForSingleObject(m_hThread,INFINITE);
        m_hThread = NULL;
    }
}

void CDlgProcessBar::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_PROGRESS, m_ProcessCtl);
    DDX_Control(pDX, IDC_STATIC_NOTE, m_StaticCtl);
}


BEGIN_MESSAGE_MAP(CDlgProcessBar, CDialog)
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()


// CDlgProcessBar message handlers

unsigned __stdcall CDlgProcessBar::ThreadProc(void *pArgParam)
{
    TRACE("执行回调函数/r/n");
    if (NULL == pArgParam)
    {
        TRACE("请设置线程参数/r/n");
        _endthreadex(0);
    }
    CDlgProcessBar *dlg = (CDlgProcessBar *)pArgParam;
    if (NULL == dlg->ob)
    {
        TRACE("请设置观察者/r/n");
        _endthreadex(0);
    }
    TRACE(L"Call CallBack Function/r/n");
    dlg->ob->CallbackFunc();
    TRACE(L"End CallBack Function/r/n");
    if (NULL != dlg->GetSafeHwnd())
    {
        TRACE(L"Post Message/r/n");
        dlg->PostMessage(WM_COMMAND,
            MAKEWPARAM(IDOK,BN_CLICKED),(LPARAM)dlg->GetSafeHwnd());
    }
    return 0;
}
BOOL CDlgProcessBar::SetCallBack(CObserver *ob)
{
    if (NULL == ob)
    {
        TRACE("设置观察者失败");
        return FALSE;
    }
    this->ob = ob;
    return TRUE;
}
BOOL CDlgProcessBar::SetNotice(const CString ¬ice /* = L"正在处理..." */,
     const COLORREF &color /* = RGB */)
{
    m_TextColor = color;
    m_StaticCtl.SetWindowText(notice);
    RedrawWindow();         // ??
    return TRUE;
}
BOOL CDlgProcessBar::InitProcessBar(const int &range)
{
   
    m_ProcessCtl.SetRange(0,range);
    return TRUE;
}
BOOL CDlgProcessBar::SetProcessBar(const int &inc)
{
    int iCurPos = m_ProcessCtl.GetPos();
    m_ProcessCtl.SetPos(iCurPos + inc);
    return TRUE;
}
HBRUSH CDlgProcessBar::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    switch (nCtlColor)
    {
    case CTLCOLOR_STATIC:
        pDC->SetTextColor(m_TextColor);
        pDC->SetBkMode(TRANSPARENT);
        return (HBRUSH)GetStockObject(NULL_BRUSH);
    default:
        return CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
    }
}
BOOL CDlgProcessBar::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_TextColor = RGB(255,0,0);
    m_hThread = NULL;
    m_threadID = 0;
    if (NULL == ob)
    {
        TRACE("观察者设置失败");
        return FALSE;
    }
    m_hThread = (HANDLE)_beginthreadex(NULL,0,ThreadProc,(void*)this,0,&m_threadID);
    if (NULL == m_hThread)
    {
        TRACE("创建线程失败");
        OnOK();
    }
    return TRUE;  // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}
BOOL CDlgProcessBar::PreTranslateMessage(MSG* pMsg)
{
    // TODO: 在此添加专用代码和/或调用基类
    if (pMsg->message==WM_KEYDOWN && (pMsg->wParam==VK_RETURN ||
     pMsg->wParam==VK_ESCAPE || pMsg->wParam==VK_SPACE))
    {
        return TRUE;
    }
    return CDialog::PreTranslateMessage(pMsg);
}
void CDlgProcessBar::OnOK()
{
    // TODO: Add your specialized code here and/or call the base class
    if (NULL != m_hThread)
    {
        WaitForSingleObject(m_hThread,INFINITE);
        m_hThread = NULL;
    }
    ob = NULL;
    CDialog::OnOK();
}
        如上源代码,模式对话框会接管主线程的消息处理过程,由于对话框本身会相应Enter、Esc按键的,所以这里将这两个按键屏蔽掉,否则会出现异常。同时在实际的测试中,发现使用空格键也会造成异常,而且空格键的消息ID是32,然而响应的事件确实IDCANCEL(ID为2)的消息异常。让我甚是不得其解??????(有大神了解的麻烦给我讲讲)。刚开始我采用的是IDCANCEL消息处理销毁对话框,最后发现出现异常,随之最后选择了IDOK来处理。不过这里也可以用户自定义消息来处理该过程。

        本类的使用就是,首先拥有个观察者对象,继承本例提供的CObserver类,在该类中重载CallbackFunc函数,所有的耗时操作都在该函数体中执行。

在进度条对话框类中,如上源码,只需要在ThreadProc中调用该观察者方法CallbackFunc,便在后台进程中执行耗时操作。同时用户可以使用进度条对话框对象的SetProcessBar方法设置进度条的运行进度。同时,设置SetNotice可以提示用户实时更新显示提示信息。

 上个图以看效果:
  
摘自  meiyuli的专栏