先看下效果,MFC对话框中实现多线程进度条,对话框支持拖拽不卡死。
直接上代码,此代码参考了文库代码:
我这里提供完整的对话框代码:
// MultiThreadProgressDlg.h: 头文件
//
#pragma once
// CMultiThreadProgressDlg 对话框
class CMultiThreadProgressDlg : public CDialogEx
{
// 构造
public:
CMultiThreadProgressDlg(CWnd* pParent = nullptr); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MULTITHREADPROGRESS_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedOk();
CProgressCtrl m_hThread1;
CProgressCtrl m_hThread2;
CProgressCtrl m_hThread3;
static DWORD _stdcall ThreadOne(LPVOID IpParameter);
static DWORD _stdcall ThreadTwo(LPVOID IpParameter);
static DWORD _stdcall ThreadThree(LPVOID IpParameter);
HANDLE m_hThreadOne;
HANDLE m_hThreadTwo;
HANDLE m_hThreadThree;
};
然后是实现文件:
// MultiThreadProgressDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "MultiThreadProgress.h"
#include "MultiThreadProgressDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMultiThreadProgressDlg 对话框
CMultiThreadProgressDlg::CMultiThreadProgressDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MULTITHREADPROGRESS_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMultiThreadProgressDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PROGRESS1, m_hThread1);
DDX_Control(pDX, IDC_PROGRESS2, m_hThread2);
DDX_Control(pDX, IDC_PROGRESS3, m_hThread3);
}
BEGIN_MESSAGE_MAP(CMultiThreadProgressDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CMultiThreadProgressDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// CMultiThreadProgressDlg 消息处理程序
BOOL CMultiThreadProgressDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_hThread1.SetRange(0, 100000);
m_hThread2.SetRange(0, 100000);
m_hThread3.SetRange(0, 100000);
//创建线程
m_hThreadOne = CreateThread(NULL, 100, ThreadOne, (void*)this, CREATE_SUSPENDED, NULL); // CREATE_SUSPENDED标识创建的线程可以被挂起的
SetThreadPriority(m_hThreadOne, THREAD_PRIORITY_ABOVE_NORMAL); //标识创建的线程具有的优先级别
m_hThreadTwo = CreateThread(NULL, 100, ThreadTwo, (void*)this, CREATE_SUSPENDED, NULL);
SetThreadPriority(m_hThreadTwo, THREAD_PRIORITY_NORMAL);
m_hThreadThree = CreateThread(NULL, 100, ThreadThree, (void*)this, CREATE_SUSPENDED, NULL);
SetThreadPriority(m_hThreadThree, THREAD_PRIORITY_BELOW_NORMAL);
//启动线程
ResumeThread(m_hThreadOne);
ResumeThread(m_hThreadTwo);
ResumeThread(m_hThreadThree);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMultiThreadProgressDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMultiThreadProgressDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMultiThreadProgressDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMultiThreadProgressDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
CDialogEx::OnOK();
}
DWORD _stdcall CMultiThreadProgressDlg::ThreadOne(LPVOID IpParameter)
{
CMultiThreadProgressDlg* pDlg = (CMultiThreadProgressDlg*)IpParameter;
int low, high, pos;
pos = pDlg->m_hThread1.GetPos();
pDlg->m_hThread1.GetRange(low, high);
while (pos < high)
{
pos = pDlg->m_hThread1.GetPos();
Sleep(10);
pDlg->m_hThread1.SetPos(pos + 1);
}
pDlg->m_hThread1.SetPos(0);
return 0;
}
DWORD _stdcall CMultiThreadProgressDlg::ThreadTwo(LPVOID IpParameter)
{
CMultiThreadProgressDlg* pDlg = (CMultiThreadProgressDlg*)IpParameter;
int low, high, pos;
pos = pDlg->m_hThread2.GetPos();
pDlg->m_hThread2.GetRange(low, high);
while (pos < high)
{
pos = pDlg->m_hThread2.GetPos();
Sleep(10);
pDlg->m_hThread2.SetPos(pos + 1);
}
pDlg->m_hThread2.SetPos(0);
return 0;
}
DWORD _stdcall CMultiThreadProgressDlg::ThreadThree(LPVOID IpParameter)
{
CMultiThreadProgressDlg* pDlg = (CMultiThreadProgressDlg*)IpParameter;
int low, high, pos;
pos = pDlg->m_hThread3.GetPos();
pDlg->m_hThread3.GetRange(low, high);
while (pos < high)
{
pos = pDlg->m_hThread3.GetPos();
Sleep(10);
pDlg->m_hThread3.SetPos(pos + 1);
}
pDlg->m_hThread3.SetPos(0);
return 0;
}
直接编译运行,即可看到效果。
为了进一步研究动态添加进度条,于是我写了下面这个小程序,可以根据对话框的大小,动态增减进度条的个数,其中宽度为当前对话框的宽度:
直接上完整代码:
// DynamicProgressBarsDlg.h: 头文件
//
#pragma once
#include <vector>
// CDynamicProgressBarsDlg 对话框
class CDynamicProgressBarsDlg : public CDialogEx
{
// 构造
public:
CDynamicProgressBarsDlg(CWnd* pParent = nullptr); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_DYNAMICPROGRESSBARS_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
//创建一个进度条
CProgressCtrl* CreateProgressBar(const CRect rect);
//进度条指针
std::vector<CProgressCtrl*> m_vpPrgoressCtrl;
int m_nShowCount = 0;
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnSize(UINT nType, int cx, int cy);
};
实现文件:
// DynamicProgressBarsDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "DynamicProgressBars.h"
#include "DynamicProgressBarsDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define IDC_PRG_START 1000
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CDynamicProgressBarsDlg 对话框
CDynamicProgressBarsDlg::CDynamicProgressBarsDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DYNAMICPROGRESSBARS_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDynamicProgressBarsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
CProgressCtrl* CDynamicProgressBarsDlg::CreateProgressBar(const CRect rect)
{
//创建一个进度条
CProgressCtrl* pProgress = new CProgressCtrl;
// 创建进度条
if (!pProgress->Create(WS_CHILD | WS_VISIBLE | PBS_SMOOTH, rect, this, IDC_PRG_START))
{
TRACE(_T("创建进度条失败!\n"));
delete pProgress;
return NULL;
}
// 设置进度条范围和初始位置
pProgress->SetRange(0, 100);
pProgress->SetPos(60);
return pProgress;
}
BEGIN_MESSAGE_MAP(CDynamicProgressBarsDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_SIZE()
END_MESSAGE_MAP()
// CDynamicProgressBarsDlg 消息处理程序
BOOL CDynamicProgressBarsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CDynamicProgressBarsDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CDynamicProgressBarsDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDynamicProgressBarsDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CDynamicProgressBarsDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
CRect rectWindow;
GetWindowRect(&rectWindow);
int nWidth = rectWindow.Width();
int nHeight = rectWindow.Height();
int nNewCount = nHeight / 60;
size_t nProgressBarCount = m_vpPrgoressCtrl.size();
if (nProgressBarCount < nNewCount)
{
for (size_t i = m_nShowCount; i < nNewCount; ++i)
{
CRect rect(0, i * 60, nWidth, i * 60 + 55);
if (CProgressCtrl* pCtrl = CreateProgressBar(rect))
{
m_vpPrgoressCtrl.emplace_back(pCtrl);
}
}
m_nShowCount = nNewCount;
}
else
{
for (size_t i = 0; i < m_nShowCount; ++i)
{
if (CProgressCtrl* pCtrl = m_vpPrgoressCtrl[i])
{
pCtrl->ShowWindow(SW_SHOW);
}
}
for (size_t i = nNewCount; i < nProgressBarCount; ++i)
{
if (CProgressCtrl * pCtrl = m_vpPrgoressCtrl[i])
{
pCtrl->ShowWindow(SW_HIDE);
}
}
}
}
关键代码在OnSize()函数中。