10. 进度条控件¶
计算机在处理任务时,任务的进度经常采用进度条来显示,还可以表示处理任务的速度,完成度,剩余未完成任务量和可能需要处理时间,一般以长方形条状显示。如 图10_1。
10.1. 创建进度条控件¶
10.1.1. 标准消息类型及参数说明¶
本章使用的消息类型常用的有WM_CREATE、WM_TIMER、WM_NOTIFY和WM_CTLCOLOR。WM_CREATE消息用于创建控件;这里使用WM_TIMER,是用了修改进度值。WM_NOTIFY消息用来处理 EXIT按钮消息。使用WM_CTLCOLOR消息来改变控件的外观颜色。下面介绍一个新的消息类型:
WM_CLOSE消息:之前讲过,当用户按下EXIT按钮后,会调用PostCloseMessage发送关闭主窗口消息,这个消息就是WM_CLOSE。默认情况下,是执行emXGUI的关闭操作,这样就会导致进度条的进度值没有清零,计数依然从上一次的计数值开始。所以我需要在关闭主窗口之前做清零操作。利用窗口 关闭时,会接收到WM_CLOSE消息。在该消息中,对变量进行清零。
10.1.2. 进度条配置结构体¶
emXGUI使用PROGRESSBAR_CFG结构体来管理进度条。创建进度条完成后,需要发送PBM_SET_CFG消息,来初始化该结构体所有的参数。相关的成员变量,见 代码清单10_1。
1 2 3 4 5 6 7 | typedef struct{
u16 cbSize; //结构体的大小
u16 fMask; //功能选择
u32 Rangle; //进度值的最大值
u32 Value; //当前的进度值
u32 TextFlag; //文字格式
}PROGRESSBAR_CFG;
|
cbSize:用来存放结构体的大小。用户提供的 PROGRESSBAR_CFG , 必须要将 cbSize 值设置成 sizeof(PROGRESSBAR_CFG)。
fMask:功能选择位。只有相应的 MASK 位置1,才会更新到 进度条控件的内部数据结构体。
Rangle:进度值的最大值。默认是100。
Value:当前的进度值。可以发送PBM_GET_VALUE,来获取当前的进度值。
TextFlag:文字格式。可以是DT_BOTTOM(垂直底部对齐)、DT_RIGHT(水平居右对齐)、DT_SINGLELINE(单行模式)等等。还有其他的参数,可以参考emXGUI.h的注释来使用。
10.1.3. 创建进度条控件函数¶
1 2 3 | HWND CreateWindowEx( U32 dwExStyle, LPCVOID lpClass, LPCWSTR lpWindowName,
U32 dwStyle, int x, int y, int nWidth, int nHeight,
HWND hwndParent, UINT WinId,HINSTANCE hInstance,LPVOID lpParam);
|
lpClass:窗口类。进度条控件,这里选择PROGRESSBAR。
2) dwStyle:进度条的风格。进度条控件支持窗口风格参数,还可以使用以下参数:PBS_TEXT(显示文字)、PBS_3D(立体风格)、PBS_FLAT(平面风格)、PBS_ALIGN_LEFT(从左往右增长)、PBS_ALIGN_RIGHT(从右往左增长)、PBS_ALIGN_TOP(从上往下 增长)和PBS_ALIGN_BOTTOM(从下往上增长)
至于其他的参数,也是同样的用法,这里不作描述。我们创建控件调用的CreateWindow函数,实际上就是CreateWindowEx函数。
10.2. 创建进度条控件实验¶
10.2.2. 代码分析¶
创建父窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | void GUI_DEMO_Progressbar(void)
{
HWND hwnd;
WNDCLASS wcex;
MSG msg;
////第1部分:配置wcex参数。
wcex.Tag = WNDCLASS_TAG;
wcex.Style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = win_proc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = 0;//hInst;
wcex.hIcon = 0;//LoadIcon(hInstance, (LPCTSTR)IDI_WIN32_APP_TEST);
wcex.hCursor = 0;//LoadCursor(NULL, IDC_ARROW);
//第2部分:创建主窗口
hwnd =CreateWindowEx(NULL,
&wcex,
_T("GUI Demo - Progressbar"),
WS_CAPTION| WS_DLGFRAME| WS_BORDER| WS_CLIPCHILDREN,
0,0,GUI_XSIZE,GUI_YSIZE,
NULL,NULL,NULL,NULL);
//第3部分:显示主窗口
ShowWindow(hwnd,SW_SHOW);
//窗口消息循环(窗口关闭并销毁时,GetMessage将返回FALSE,退出本消息循环)。
while(GetMessage(&msg,hwnd))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
|
创建父窗口,标题栏为“GUI Demo - Progressbar”,带有大小边框,设置win_proc作为窗口回调函数。
窗口回调函数
WM_CREATE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | case WM_CREATE:
{
GetClientRect(hwnd,&rc);
if(1)
{
//PROGRESSBAR_CFG结构体的大小
cfg.cbSize = sizeof(PROGRESSBAR_CFG);
//开启所有的功能
cfg.fMask = PB_CFG_ALL;
//文字格式水平,垂直居中
cfg.TextFlag = DT_VCENTER|DT_CENTER;
// 创建自绘制的进度条VProgressbar
wnd = CreateWindow(PROGRESSBAR,L"VProgressbar",
WS_OWNERDRAW|PBS_ALIGN_BOTTOM|WS_VISIBLE,
20,20,48,320,hwnd,ID_PROGBAR1,NULL,NULL);
//初始化PROGRESSBAR_CFG结构体
SendMessage(wnd,PBM_GET_CFG,TRUE,(LPARAM)&cfg);
SendMessage(wnd,PBM_SET_CFG,TRUE,(LPARAM)&cfg);
// 创建从右往左增长的进度条VProgressbar
wnd = CreateWindow(PROGRESSBAR,L"PROGBAR2 & Right align",
PBS_TEXT|PBS_ALIGN_RIGHT|WS_VISIBLE,
100,100,280,48,hwnd,ID_PROGBAR2,NULL,NULL);
SendMessage(wnd,PBM_GET_CFG,TRUE,(LPARAM)&cfg);
SendMessage(wnd,PBM_SET_CFG,TRUE,(LPARAM)&cfg);
//创建从左往右增长的进度条VProgressbar
wnd = CreateWindow(PROGRESSBAR,L"PROGBAR3 & Left align",
PBS_TEXT|PBS_ALIGN_LEFT|WS_VISIBLE,
100,200,280,48,hwnd,ID_PROGBAR3,NULL,NULL);
SendMessage(wnd,PBM_GET_CFG,TRUE,(LPARAM)&cfg);
SendMessage(wnd,PBM_SET_CFG,TRUE,(LPARAM)&cfg);
}
//创建EXIT按钮
CreateWindow(BUTTON,L"EXIT",WS_VISIBLE,rc.w-100,8,80,48,hwnd,ID_EXIT,NULL,NULL);
//创建200ms定时器
SetTimer(hwnd,1,200,TMR_START,NULL);
return TRUE;
}
|
调用CreateWindow函数创建进度条控件,发送PBM_SET_CFG消息来配置进度条。cbSize的值必须为sizeof(PROGRESSBAR_CFG)。创建EXIT按钮,用来关闭窗口以及创建200ms定时器。
WM_TIMER
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | case WM_TIMER:
{
pb1_val +=1;
if(pb1_val > 100)
{
pb1_val =0;
}
wnd =GetDlgItem(hwnd,ID_PROGBAR1);
//设置进度值
SendMessage(wnd,PBM_SET_VALUE,TRUE,pb1_val);
pb2_val +=2;
if(pb2_val > 100)
{
pb2_val =0;
}
//获取窗口句柄
wnd =GetDlgItem(hwnd,ID_PROGBAR2);
SendMessage(wnd,PBM_SET_VALUE,TRUE,pb2_val);
wnd =GetDlgItem(hwnd,ID_PROGBAR3);
SendMessage(wnd,PBM_SET_VALUE,TRUE,pb2_val);
return TRUE;
}
|
每当200ms计时结束时,就会将pb1_val值加1,pb2_val的值加2。调用GetDlgItem获取进度条控件的窗口句柄,同时将进度值发送给窗口,来实现实时更新进度条的进度值。
WM_CLOSE
1 2 3 4 5 6 | case WM_CLOSE:
{
pb1_val=0;
pb2_val=0;
break;
}
|
在WM_CLOSE中,对两个变量的值进行清零操作。
WM_CTLCOLOR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | case WM_CTLCOLOR:
{
u16 id;
CTLCOLOR *cr;
id =wParam;
cr =(CTLCOLOR*)lParam;
if(id==ID_PROGBAR3)
{
cr->TextColor =RGB888(250,250,250);
cr->BackColor =RGB888(0,0,0);
cr->BorderColor =RGB888(130,30,130);
cr->ForeColor =RGB888(200,0,200);
return TRUE;
}
return FALSE;
}
|
在WM_CTLCOLOR消息中,改变进度条控件PROGBAR3 的外观颜色。
WM_NOTIFY
1 2 3 4 5 6 7 8 9 10 | case WM_NOTIFY:
{
u16 code,id;
id =LOWORD(wParam);
code=HIWORD(wParam);
if(id== ID_EXIT && code==BN_CLICKED)
{ // EXIT按钮弹起
PostCloseMessage(hwnd); //产生WM_CLOSE消息关闭主窗口
}
}
|
按下EXIT按键,产生WM_CLOSE消息关闭主窗口。
WM_DRAWITEM
1 2 3 4 5 6 7 8 | case WM_DRAWITEM:
{
DRAWITEM_HDR *ds;
ds =(DRAWITEM_HDR*)lParam;
progressbar_owner_draw(ds);
return TRUE;
}
|
进度条控件PROGBAR1拥有自定义绘制属性WS_OWNERDRAW,在绘制前都会给父窗口发送WM_DRAWITEM消息。在WM_DRAWITEM消息中,调用函数progressbar_owner_draw实现控件自定义,最后返回TURE。这里必须返回TURE,否则会按照默认方案进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | static void progressbar_owner_draw(DRAWITEM_HDR *ds)
{
HWND hwnd;
HDC hdc;
RECT rc,m_rc[2];
// int range,val;
WCHAR wbuf[128];
PROGRESSBAR_CFG cfg;
hwnd =ds->hwnd;
hdc =ds->hDC;
/* 第一步 */
//获取客户区矩形位置,大小
GetClientRect(hwnd,&rc);
//设置进度条的背景颜色
SetBrushColor(hdc,MapRGB(hdc,150,200,250));
//填充进度条的背景
FillRect(hdc,&ds->rc);
//绘制进度条的背景边框
DrawRect(hdc,&rc);
/* 第二步 */
cfg.cbSize =sizeof(cfg);
cfg.fMask =PB_CFG_ALL;
SendMessage(hwnd,PBM_GET_CFG,0,(LPARAM)&cfg);
//生成进度条矩形
MakeProgressRect(m_rc,&rc,cfg.Rangle,cfg.Value,PB_ORG_BOTTOM);
//设置进度条的颜色
SetBrushColor(hdc,MapRGB(hdc,250,10,10));
//填充进度条
FillRoundRect(hdc,&m_rc[0],2);
//设置画笔颜色
SetPenColor(hdc,MapRGB(hdc,100,10,10));
//绘制进度条的边框,采用圆角边框
DrawRoundRect(hdc,&m_rc[0],2);
/* 显示进度值 */
x_wsprintf(wbuf,L"%d",cfg.Value);
//InflateRect(&rc,40,0);
DrawText(hdc,wbuf,-1,&rc,DT_VCENTER|DT_CENTER);
}
|
进度条,其实是由两个部分构成,如 图10_3 ,是进度度条的背景,是已经完成的进度。只要我们可以绘制这样的两个矩形,同时将它俩叠加到一块的话,就可以得到处的进度条了。
图 10‑3 原理绘制原理
代码清单10_10 中的第一步,就是绘制一个处的矩形框框。调用GetClientRect函数获取子控件的位置,大小。使用SetBrushColor和SetPenColor来设置背景和边框的颜色,最后调用FillRect和DrawRect来绘制成。
代码清单10_10 中的第二步,对应上图的处。发送消息PBM_GET_CFG,来获得进度条的配置参数,主要是当前的进度值。发送消息前,必须要将cbSize值设置成 sizeof(PROGRESSBAR_CFG),同时将fMask设置为PB_CFG_ALL,才可以得到正确的参数值。调用MakeProgressRect函数,最终输出时,m_rc[0]为已完成的进度矩形 。调用SetBrushColor、FillRoundRect和DrawRoundRect来绘制的 矩形。处的叠加过程,实际上在调用MakeProgressRect函数的时候,就已经完成了。因为MakeProgressRect函数的第二个参数的作用是,使输出m_rc的矩形位置和大小限定在控件的范围中。关于MakeProgressRect函数的具体说明,请参考《emXGUI API编程手册》的章节:位置及区域操作运算API。
最后,将GUI_DEMO_Progressbar加入到GUI_AppMain中,见 代码清单10_11。
1 2 3 4 5 6 7 8 9 10 11 12 13 | void GUI_AppMain(void)
{
while(1)
{
GUI_DEMO_Hello();
GUI_DEMO_Button();
GUI_DEMO_Checkbox();
GUI_DEMO_Radiobox();
GUI_DEMO_Textbox();
GUI_DEMO_Progressbar();
}
}
|
10.2.3. 实验现象¶
实验结果,如图 10‑4所示。最左边的是PROGBAR1,是在progressbar_owner_draw函数绘制的进度条。PROGBAR2和PROGBAR3是系统自带的进度条,它们的增长方式相反,同时我们还通过WM_CTLCOLOR消息改变了PROGBAR3的外观颜色。