MFC中多线程进度条的简单代码实现
先看下效果,MFC对话框中实现多线程进度条,对话框支持拖拽不卡死。
直接上代码,此代码参考了文库代码:
我这里提供完整的对话框代码:
// MultiThreadProgressDlg.h: 头文件
//#pragma once// CMultiThreadProgressDlg 对话框
class CMultiThreadProgressDlg : public CDialogEx
{
// 构造
public:CMultiThreadProgressDlg(CWnd* pParent = nullptr); // 标准构造函数// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_MULTITHREADPROGRESS_DIALOG };
#endifprotected: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_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected: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_TIMEenum { IDD = IDD_DYNAMICPROGRESSBARS_DIALOG };
#endifprotected: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_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected: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()函数中。