MFC 用进度条在状态栏显示某个按钮响应函数的过程,并用多线程实时更新

39 篇文章 5 订阅

因为写的一个按钮的响应函数在数据量大的情况下会出现未响应的情况,所以为了让人直观的可以觉得程序仍然还在运行,并实时显示函数的运行状态。

(1)首先MFC工程中新建一个类,这个类不是MFC类而是Generic Class,在类视图下,添加一个新类,并选择Generic Class,将这个类命名为CProgressStatusBar,

设置其基类为CStatusBar

(2)在资源视图,字串表中,新增一个IDS_PROGRESS字串

并在CMainFrame.cpp文件中找到static UINT indicator[],将其添加进去

我的static UINT indicator[]为

static UINT indicators[] =
{
ID_SEPARATOR,           // status line indicator
IDS_TIMER,
IDS_PROGRESS,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};

可见我将IDS_PROGRESS添加到数组的索引为2的位置,这与之后设置状态栏的位置有关

(3)CProgStatusBar派生类中加了一个CProgressCtrl类型的数据成员 --m_wndProgBar

(4)重载,然后重载CstatusBar类的二个重要成员函数:OnCreate()、OnSize(),最后还要在该类中添加一个自定义成员函数OnProgress()

OnCreat()的函数如下

int CProgressStatusBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpCreateStruct->style=WS_CLIPCHILDREN;
VERIFY(CStatusBar::OnCreate(lpCreateStruct) ==0 );
    VERIFY(m_wndProgBar.Create(WS_CHILD|PBS_SMOOTH, CRect(), this, IDS_PROGRESS));
    m_wndProgBar.SetRange(0,100);
//m_wndProgBar.SetPos(10);
return 0;
}

OnCreate()函数在状态栏的式样中加了一个WS_CLIPCHILDREN,它告诉Windows不要绘制子窗口以下的状态栏区域,这样可以减 少屏幕闪烁。接着OnCreate()函数创建进度指示器控件并将它的范围设置成[0,100]。注意在这里创建进度指示器控件时没有用WS_VISIBLE,因为我们要实现的目标是仅仅当装载文件时进度条才显现,其余时间内应用程序都隐藏它。

OnSize()的函数如下

void CProgressStatusBar::OnSize(UINT nType, int cx, int cy)
{
CStatusBar::OnSize(nType, cx, cy);
CRect rect;
GetItemRect(2, &rect);
m_wndProgBar.MoveWindow(rect,FALSE);
}

熟悉Windows编程 的人都清楚,无论何时,只要在某个窗口里添加子窗口,那么一定要负责管理它的大小尺寸,也就是说,当父窗口大小改变后,子窗口的大小也要跟着作相应的改 变。一般来说,这个工作由父窗口的WM_SIZE消息处理函数OnSize()来作,所以我们也要处理该类的OnSize()函数。

从上述代码可以看出,CProgStatusBar::OnSize()将进度指示器放在了状态栏的第一个窗格,这个窗格通常用来显示程序的"就绪"信息和命令提示信息。注意这里不论进度指示器是处于可见状态还是隐藏状态,MoveWindow都照样起作用--所以即便是进度指示器处于隐藏状态,其窗口大小同样是可调的。

OnProgress的函数如下

 void CProgressStatusBar::OnProgress(UINT pct)
 {
  CProgressCtrl& pc = m_wndProgBar;//进度条对象
  DWORD dwOldStyle = pc.GetStyle();
     DWORD dwNewStyle = dwOldStyle;
  if (pct>0) //如果pct>0,将显示进度条
dwNewStyle |= WS_VISIBLE;
  else //否则隐藏进度条;
  dwNewStyle &= ~WS_VISIBLE;
if (dwNewStyle != dwOldStyle) 
  {
  SetWindowText(NULL); //显示进度条前清空状态条;
  SetWindowLong(pc.m_hWnd, GWL_STYLE, dwNewStyle); 
  //设置进度条处于显示状态;
  }
  // 设置进度条的当前位置;
  pc.SetPos(pct);
  if (pct==0)
  // 如果pct等于0,通知主框架窗口显示空闲信息;
GetParent()->PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
 }

调整好进度指示器的窗口大小后,下面要作的就是进度指示器的显示,进度指示器当前进度状态的显示在CProgStatusBar::OnProgress中完成。它有一个类型为UINT的入口参数:参数值的范围从0到100,表示进度百分比,0表示进 度没开始,100表示全部完成。如果这个参数的值大于0,则OnProgress显示进度控制并设置指示器的位置;如果参数值等于0,则 OnProgress隐藏进度控制。


虽然子窗口控件通常都是放在父窗口能绘制的区域的最上面,但这样做在绘制方面是有一定风险的。在 隐藏/显示进度控制时尤其如此,这时候会出现两个问题:第一,因为进度指示器显示在状态栏的第一个窗格位置,所以如果进度条指示器显示时已经显示有状态信 息,那么进度指示器和状态信息文本就会有冲突,相互干扰。之所以会这样,是因为进度控制假设其绘制背景是干净的,并且只绘制进度控制的着色部分。解决这个 问题最简单的方法是调用CStatusBar::SetWindowText(NULL)函数在显示进度指示器之前打扫一下环境卫生,清除以前的文本。

对于状态栏来说,SetWindowText函数的作用是设置状态栏第一个窗格的文本。反之,当调用OnProgress(0)清除进度控制时也存在类 似的问题,CProgStatusBar::OnProgress 隐藏进度控制后,状态栏第一个窗格该显示什么信息呢?一般显示"就绪"或其它的提示信息。当应用程序不做任何事情时,MFC程序总是在这个位置显示资源串AFX_IDS_IDLEMESSAGE表示的文本,其缺省值为"就绪",当然读者朋友们可以在当前项目的RC文件中任意修改这个值,不管怎样,在MFC程序的状态栏中显示"就绪"信息很容易,需要作的就是在CProgStatusBar::OnProgress()函数中调用语句GetParent()->PostMessage(WM_SETMESSAGESTRING,AFX_IDS_IDLEMESSAGE)向父窗口 发送一个WM_SETMESSAGESTRING消息就可以了,需要注意的是,使用消息WM_SETMESSAGESTRING时必须包含它的定义文 件"afxpriv.h",否则程序会报告编译错误。

主要思路:上述CprogStatusBar类实现了状态栏中包含进度条控件,该类的使用 方法很简单,首先在应用程序的CmainFrame类中用CProgStatusBar代替CStatusBar声明实例,然后在任何想要显示进度控制指 示的地方调用CProgStatusBar::OnProgress。本例中定义了一个消息MYWM_PROGRESS,它将进度条当前的进度作为WPARAM参数传递到CProgStatusBar::OnProgress()函数。

(5)在程序的Resource.h文件中添加自定义消息的定义:#define MYWM_PROGRESS (WM_USER+1)

(6)在程序的主框架窗口CMainFrame类的CMainFrame.h头文件中声明MYWM_PROGRESS的消息响应函数afx_msg LRESULT OnProgress(WPARAM wp, LPARAM lp)

在该类的实现CMainFrame.cpp中添加消息映射ON_MESSAGE(MYWM_PROGRESS,OnProgress)

(7)在CMainFrame.h文件中将原有的CStatusBar  m_wndStatusBar;注释掉,增加刚添加的新类CProgressStatusBar m_wndStatusBar;

(8)在CMainFrame.cpp文件LRESULT OnProgress(WPARAM wp, LPARAM lp)函数中增加消息效应,代码如下

LRESULT CMainFrame::OnProgress(WPARAM wp, LPARAM pct)
{
m_wndStatusBar.OnProgress(wp);
return 0;
UpdateData(FALSE);
}

(9)在需要添加进度条的按钮响应函数中,如我的是CXXXView类中的OnMenuCal()函数中

先获取框架类当前指针CWnd* pFrame = AfxGetMainWnd();

然后通过pFrame->sendMessage(MYWM_PROGRESS ,pct(为当前进度条的位置))来调用状态栏上的进度条

其效果如下,当我按下计算按钮时,执行OnMenuCal()消息效应函数,在其中用pFrame发送框架类消息MYWM_PROGRESS,而我们在框架类中让CMainFrame的OnProgress作为MYWM_PROGRESS的响应函数,也就是说当我们在按钮响应函数中pFrame->sendMessage(MYWM_PROGRESS ,pct)发出消息时自动调用框架类的OnProgress函数,而在OnProgress中又调用了CProgressStatusBar的OnProgress的函数,设置状态栏上进度条的进度位置

上述程序在小数据量的情况下,状态栏上的进度条还是会随着这个按钮的消息响应函数运行而实时更新,但是当数据量很大的时候,会出现程序假死的情况即整个程序未响应的

的情况,所以虽然程序依然在运行,但是进度条不会随时更新,为了解决这个问题,引进了多线程,开辟一个新线程去运行这个新的函数,而状态栏的更新是在主线程中的,虽然我们在那个按钮的消息响应函数中调用了CMainFrame的OnProgress函数,所以其实状态栏进度条的更新是在主线程中运行的。

在view类中添加一个新函数MenuCal(),将原来属于OnMenuCal函数的代码剪切到此函数当中,

因为我们按钮OnMenuCal按钮是在CXXXView类中,所以我们的线程也加在View类中,在View类中的.h文件代码protected后加入以下的代码

protected:
CWinThread *m_pMyThread;//多进程对象
friend UINT MyThread(LPVOID pParam);//线程函数

在CPP的文件下

UINT MyThread(LPVOID pParam)

{

CDEM_YCZView *pView=(CDEM_YCZView *)pParam;

pView->MenuCal();
return 0;

}

然后在OnMenuCal()的按钮消息效应的函数中添加如下代码

void CXXXView::OnMenuCal() 
{
m_pMyThread=AfxBeginThread(MyThread,this);
m_pMyThread=NULL;


}

就是说当我们按下OnMenuCal按钮后,就开辟了一个新的线程,将函数MyThread添加到新进程中,而MyThread函数实际调用了MenuCal函数,实际上效果与之前差不多,但是开辟了一个新的线程执行庞大的函数体不会造成进度条不会实时更新。

其CProgressStatusBar的CProgressStatusBar.h源代码如下

 

// ProgressStatusBar.h: interface for the CProgressStatusBar class.
//
//

#if !defined(AFX_PROGRESSSTATUSBAR_H__76E07AF8_611F_4DED_9AF6_202CEFA0936B__INCLUDED_)
#define AFX_PROGRESSSTATUSBAR_H__76E07AF8_611F_4DED_9AF6_202CEFA0936B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CProgressStatusBar : public CStatusBar  
{
public:
	//void OnSize(UINT nType, int cx, int cy);
	//int OnCreate(LPCREATESTRUCT lpCreateStruct);
	CProgressStatusBar();
	virtual ~CProgressStatusBar();
	//CProgressCtrl m_Progress;
    CProgressCtrl& GetProgressCtrl()
	{
     return m_wndProgBar;
	};
	void OnProgress(UINT pct);
protected:
	CProgressCtrl m_wndProgBar; // the progress bar
   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   afx_msg void OnSize(UINT nType, int cx, int cy);
   DECLARE_MESSAGE_MAP()
   DECLARE_DYNAMIC(CProgressStatusBar)


};

#endif // !defined(AFX_PROGRESSSTATUSBAR_H__76E07AF8_611F_4DED_9AF6_202CEFA0936B__INCLUDED_)


CProgressStatusBar.cpp源代码如下

 

 

 

 

// ProgressStatusBar.cpp: implementation of the CProgressStatusBar class.
//
//

#include "stdafx.h"
#include "DEM_YCZ.h"
#include "ProgressStatusBar.h"
#include "afxpriv.h"

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

IMPLEMENT_DYNAMIC(CProgressStatusBar, CStatusBar)
BEGIN_MESSAGE_MAP(CProgressStatusBar, CStatusBar)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()

//
// Construction/Destruction
//

CProgressStatusBar::CProgressStatusBar()
{

}

CProgressStatusBar::~CProgressStatusBar()
{

}

int CProgressStatusBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	lpCreateStruct->style=WS_CLIPCHILDREN;
	VERIFY(CStatusBar::OnCreate(lpCreateStruct) ==0 );
    VERIFY(m_wndProgBar.Create(WS_CHILD|PBS_SMOOTH, CRect(), this, IDS_PROGRESS));
    m_wndProgBar.SetRange(0,100);
	//m_wndProgBar.SetPos(10);
	return 0;
}

void CProgressStatusBar::OnSize(UINT nType, int cx, int cy)
{
CStatusBar::OnSize(nType, cx, cy);
CRect rect;
GetItemRect(2, &rect);
m_wndProgBar.MoveWindow(rect,FALSE);
}

 void CProgressStatusBar::OnProgress(UINT pct)
 {
 	CProgressCtrl& pc = m_wndProgBar;//进度条对象
 	DWORD dwOldStyle = pc.GetStyle();
     DWORD dwNewStyle = dwOldStyle;
 	if (pct>0) //如果pct>0,将显示进度条
		dwNewStyle |= WS_VISIBLE;
 	else //否则隐藏进度条;
 		dwNewStyle &= ~WS_VISIBLE;
	if (dwNewStyle != dwOldStyle) 
 	{
 	SetWindowText(NULL); //显示进度条前清空状态条;
 	SetWindowLong(pc.m_hWnd, GWL_STYLE, dwNewStyle); 
 	//设置进度条处于显示状态;
 	}
 	// 设置进度条的当前位置;
 	pc.SetPos(pct);
 	if (pct==0)
 	// 如果pct等于0,通知主框架窗口显示空闲信息;
	GetParent()->PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
 }


CMainFrame.h

 

 

// MainFrm.h : interface of the CMainFrame class
//
/

#if !defined(AFX_MAINFRM_H__A9E6869E_CA08_4F10_9426_7E844D42CDC9__INCLUDED_)
#define AFX_MAINFRM_H__A9E6869E_CA08_4F10_9426_7E844D42CDC9__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "ProgressStatusBar.h"

#include "afxpriv.h"

class CMainFrame : public CFrameWnd
{
	
protected: // create from serialization only
	CMainFrame();
	DECLARE_DYNCREATE(CMainFrame)

// Attributes
public:
   //CProgressCtrl m_progress;
	//CProgressCtrl m_wndProgBar;
// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMainFrame)
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CMainFrame();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

protected:  // control bar embedded members
	//CStatusBar  m_wndStatusBar;
	CProgressStatusBar m_wndStatusBar;
	CToolBar    m_wndToolBar;

// Generated message map functions
protected:
	//{{AFX_MSG(CMainFrame)
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnTimer(UINT nIDEvent);
	//}}AFX_MSG
	afx_msg LRESULT OnProgress(WPARAM wp, LPARAM lp);
	DECLARE_MESSAGE_MAP()
};

/

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MAINFRM_H__A9E6869E_CA08_4F10_9426_7E844D42CDC9__INCLUDED_)


CMainFrame.cpp

 

 

// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "DEM_YCZ.h"

#include "MainFrm.h"

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

/
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_WM_CREATE()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_MESSAGE(MYWM_PROGRESS,OnProgress)
END_MESSAGE_MAP()

static UINT indicators[] =
{
	ID_SEPARATOR,           // status line indicator
	IDS_TIMER,
	IDS_PROGRESS,
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
};

/
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
	// TODO: add member initialization code here
	
}
CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}

	if (!m_wndStatusBar.Create(this) ||
		!m_wndStatusBar.SetIndicators(indicators,
		  sizeof(indicators)/sizeof(UINT)))
	{
		TRACE0("Failed to create status bar\n");
		return -1;      // fail to create
	}

	// TODO: Delete these three lines if you don't want the toolbar to
	//  be dockable
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);
	
	/
	
	
	//
	
 SetTimer(1,1000,NULL);
	return 0;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return TRUE;
}

/
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
	CFrameWnd::Dump(dc);
}

#endif //_DEBUG

/
// CMainFrame message handlers


void CMainFrame::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	

	CTime t=CTime::GetCurrentTime();
    CString str=t.Format("%H:%M:%S");
	CClientDC dc(this);
	CSize sz=dc.GetTextExtent(str);
	
	int index=0;
	index=m_wndStatusBar.CommandToIndex(IDS_TIMER);
	m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx);
    m_wndStatusBar.SetPaneText(1,str);

	/

    
	
	CFrameWnd::OnTimer(nIDEvent);
}

LRESULT CMainFrame::OnProgress(WPARAM wp, LPARAM pct)
{
	
	m_wndStatusBar.OnProgress(wp);
	return 0;
	UpdateData(FALSE);
}

 

如果您觉得这篇博文有用,请访问我的个人站:http://www.stubbornhuang.com,更多博文干货等着您。

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HW140701

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值