滚动条控件(模式对话框+后台线程处理)
2012-03-20 09:57
267 查看
有时候,在用户处理大量数据,或是在网络通信过程中,希望自己的主线程阻塞,等待该过程完成后,再继续执行,但同时由于该处理过程耗时较长,会让用户体验下降。这时候我们会想到使用一个滚动条来通知用户当前程序的执行进度,防止用户以为程序长时间不动,造成的误认为程序已死,强制关闭。
这时候最简单的一个做法就是,制作一个模式对话框,然后在该对话框中执行一个线程,将用户需要执行的操作放在该线程中来执行。在程序中,创建自己的一个对话框资源,添加滚动条,以及相应的Text模块说明进度在执行的操作。这时,最大的问题在于,怎么让滚动条模态阻塞后,执行数据处理部分的线程代码。
这时候,我的解决办法是采用观察者模式,创建一个观察者类,提供一个CallBack的方法。在进度条对话框中提供SetCallBack(CObserver*)的方法设置观察者对象。这样在需要处理数据的类中,就可以继承CallBack的方法,然后所有的耗时处理过程,都放在那里面执行。然后在进度条对话框中创建的线程中,调用观察者的CallBack方法。好吧,还是直接上源码吧。
本类的使用就是,首先拥有个观察者对象,继承本例提供的CObserver类,在该类中重载CallbackFunc函数,所有的耗时操作都在该函数体中执行。
在进度条对话框类中,如上源码,只需要在ThreadProc中调用该观察者方法CallbackFunc,便在后台进程中执行耗时操作。同时用户可以使用进度条对话框对象的SetProcessBar方法设置进度条的运行进度。同时,设置SetNotice可以提示用户实时更新显示提示信息。
上个图以看效果:
这时候最简单的一个做法就是,制作一个模式对话框,然后在该对话框中执行一个线程,将用户需要执行的操作放在该线程中来执行。在程序中,创建自己的一个对话框资源,添加滚动条,以及相应的Text模块说明进度在执行的操作。这时,最大的问题在于,怎么让滚动条模态阻塞后,执行数据处理部分的线程代码。
这时候,我的解决办法是采用观察者模式,创建一个观察者类,提供一个CallBack的方法。在进度条对话框中提供SetCallBack(CObserver*)的方法设置观察者对象。这样在需要处理数据的类中,就可以继承CallBack的方法,然后所有的耗时处理过程,都放在那里面执行。然后在进度条对话框中创建的线程中,调用观察者的CallBack方法。好吧,还是直接上源码吧。
// 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可以提示用户实时更新显示提示信息。
上个图以看效果:
相关文章推荐
- Windows 窗体控件中的多线程处理之:如何使用后台线程搜索文件
- unity 打开文件对话框功能及非Editor模式下全屏播放打开文件对话框后程序转后台问题的处理
- C#如何使用BackgroundWork后台辅助线程控件
- 在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
- MFC应用程序中处理消息,创建窗口的过程,关闭窗口(非模态窗口),打开模式对话框等的函数调用顺序 .
- 模式对话框与非模式对话框 消息处理顺序
- JQuery EasyUI弹出对话框解决Asp.net服务器控件无法执行后台代码的方法(转)
- JQuery EasyUI弹出对话框解决Asp.net服务器控件无法执行后台代码的方法
- 使用GCD处理后台线程和UI线程的交互(转自唐巧的技术博客)
- AsyncTask 使用后台线程处理耗时操作
- C#后台线程工作时更新界面的一种处理方法
- C#跨线程操作控件 通过委托处理,MSDN上又很详细用法的说明
- JQuery EasyUI弹出对话框解决Asp.net服务器控件无法执行后台代码的方法
- severlet是单例模式还是多例模式,怎样处理线程问题?
- 在基于对话框的应用中执行空闲状态处理(比如用ON_UPDATE_COMMAND_UI更新控件)
- C#【控件使用/异常处理】richtextbox中,ScrollToCaret()造成的线程堵塞
- VS2010/MFC编程入门之九(对话框:为控件添加消息处理函数)
- 对话框滚动条与显示区的处理
- 用一个对话框显示后台线程进度的实现
- 每日学习心得:CustomValidator验证控件验证用户输入的字符长度、Linq 多字段分组统计、ASP.NET后台弹出confirm对话框,然后点击确定,执行一段代码