控件简单说就是一个窗口。我们可以建立自己的子窗口控件,也可以用预定义窗口类别来建立标准子窗口控件。如按钮、复选框等。我们不要考虑这些按钮在显示或按下时自动闪烁等操作,这些由win内部完成。我们所要做的只是当按钮被按下时拦截WM_COMMAND消息。 普通窗口建立有注册窗口类等操作,但子窗口控件不要重新定义和注册,我们可以用CreateWindow,窗口样式参数准确定义了各控件的外观和功能。 9.1、按钮类 每一个子窗口控件都有一个在其兄弟窗口中唯一的句柄和ID值。知道其中一个就可以获得另一个。如:可以给窗口发送BM_SETSTATE消息来仿真按钮闪动id=GetWindowLong(hwndChild,GWL_ID); id=GetDlgCtrlID(hwndChild); hwndChile=GetDlgItem(hwndParent,id);//由ID和父窗口句柄获得子窗口句柄
SendMessage(hwndButton,BM_SETSTATE,1,0) //按下 SendMessage(hwndButton,BM_SETSTATE,0,0) //恢复正常
分组方块
![]()
BS_GROUPBOX,按钮中的特例,不处理键鼠输入,也不向其父窗口发送WM_COMMAND消息。标题在顶部显示,常用来包含其他按钮控件。
改变按钮文字WM_CTLCOLORBTN消息按钮和输入焦点SetWindowText(hwnd,pszString); iLength=GetWindowText(hwnd,pszBuffer,iMaxLength);//获取窗口目前文字 iLength = GetWindowTextLength (hwnd) ; //传回复制的字符数 ShowWindow(hwndChild,SW_SHOWNORMAL);//如果没包含WS_VISIBLE在窗口类别中,则呼叫该函数才显示子窗口 ShowWindow(hwndChild,SW_HIDE);
按钮选中时,接收输入焦点。当子窗口获得焦点时,父窗口失去输入焦点(键盘输入进入子窗口控件而不会进入父窗口)。但子窗口控件只能spacebar响应。我们可以用下面方法重新获得焦点。9.2、控件和颜色case WM_KILLFOCUS : for ( i = 0 ; i < NUM ; i++) if (hwndChild [i] == (HWND) wParam) //正在接收窗口焦点的句柄 { SetFocus (hwnd) ; break ; } return 0 ; case WM_KILLFOCUS : if (hwnd == GetParent ((HWND) wParam)) SetFocus(hwnd) ; return 0 ;
按钮按钮显示有一个矩形灰色背景。当我们要与背景颜色一至时,我们要将其背景改成白色。
系统颜色:win保留26种颜色供使用。GetSysColor //获得颜色 SetSysColor //设置颜色 如果想在显示区域表面显示按钮,避免颜色冲突的一种方法是: wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1) ; SetBkColor (hdc, GetSysColor(COLOR_BTNFACE)) ;//TextOut显示使用设备内容的背景色和文字颜色定义的值 SetTextColor (hdc, GetSysColor(COLOR_WINDOWTEXT)) ; //这么操作之后可以保证区域背景、文字背景和文字颜色一至了
这是当子窗口为显示区域着色时,按钮控件发送给其父窗口消息处理程序的一个消息,wParam消息参数是按钮的设备内容句柄, lParam是按钮的窗口句柄。当处理该消息时,要做的事情有:
使用SetTextColor选择设定一种文字颜色。
使用SetBkColor选择设定一种文字背景颜色。
将一个画刷句柄传回给子窗口。
使用WM_CTLCOLORBTN的问题:只有按键和拥有者绘制按钮才给其父窗口发送WM_CTLCOLORBTN,而只有拥有者绘制按钮才会响应父窗口消息处理程序对消息的处理,这明显没有意义,因为无论怎样都是由父窗口来负责绘制拥有者绘制按钮。
拥有者绘制按钮
如果想对按钮可见部分全面控制且不受键鼠干扰。可以建立BS_OWNERDRAW样式的按钮。
该程序绘制两个自定义按钮,用于放大或缩小窗口 OWNERDEAW程序
#define ID_SMALLER 1 //子控件1
#define ID_LAGER 2 //子控件2
#define B_WIDTH (cxChar*8) //控件宽度
#define B_HIGHT (cyChar*4) //控件高度
LRESULT CALLBACK WindowProc( HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//画三角形函数
void Triangle (HDC hdc, POINT pt[])
{
SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ;
Polygon (hdc, pt, 3) ;
SelectObject (hdc, GetStockObject (WHITE_BRUSH)) ;
}
LRESULT CALLBACK WindowProc( HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
static HWND hwndSmall,hwndLage;
static INT cxChar,cyChar;
static INT cxClient,cyClient;
RECT rc;
LPDRAWITEMSTRUCT pdis;
POINT pt[4];
INT cx,cy;
switch (uMsg)
{
case WM_CREATE:
cxChar=LOWORD(GetDialogBaseUnits()); //字体宽
cyChar=HIWORD(GetDialogBaseUnits());//字体高
//创建两个字窗口控件 注意WS_CHILD|WS_VISIBLE|BS_OWNERDRAW
hwndSmall=CreateWindow(_T("button"),NULL,WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,0,0,B_WIDTH,B_HIGHT,
hwnd,(HMENU)ID_SMALLER,NULL,NULL);//注意参数hMenu,对于子窗口,该变量指定了子窗口标识,本例即定义的窗口ID
hwndLage=CreateWindow(_T("button"),NULL,WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,0,0,B_WIDTH,B_HIGHT,
hwnd,(HMENU)ID_LAGER,NULL,NULL);
return 0;
case WM_SIZE:
cxClient=LOWORD(lParam);
cyClient=HIWORD(lParam);
//每次窗口尺寸变化时都移动控件到客户区指定位置
MoveWindow(hwndSmall,cxClient/2-3*B_WIDTH/2,cyClient/2-B_WIDTH/2,B_WIDTH,B_HIGHT,TRUE);
MoveWindow(hwndLage,cxClient/2+3*B_WIDTH/2,cyClient/2-B_WIDTH/2,B_WIDTH,B_HIGHT,TRUE);
return 0;
case WM_COMMAND: //当有子控件被按下,产生这个消息 lParam参数的低字节为控件窗口ID
GetWindowRect(hwnd,&rc);
switch (LOWORD(wParam))
{
case ID_SMALLER :
rc.left += cxClient / 20 ;
rc.right -= cxClient / 20 ;
rc.top += cyClient / 20 ;
rc.bottom -= cyClient / 20 ;
break ;
case ID_LAGER :
rc.left -= cxClient / 20 ;
rc.right += cxClient / 20 ;
rc.top -= cyClient / 20 ;
rc.bottom += cyClient / 20 ;
break ;
default:
break;
}
MoveWindow ( hwnd, rc.left, rc.top, rc.right - rc.left,rc.bottom - rc.top, TRUE) ; //改变窗口的大小和位置
//窗口尺寸大小改变,发送WM_SIZE消息,控件重新定位到中间位置
return 0;
case WM_DRAWITEM:
pdis=(LPDRAWITEMSTRUCT)lParam;
FillRect(pdis->hDC,&(pdis->rcItem),(HBRUSH)GetStockObject(WHITE_BRUSH));// 填充矩形
FrameRect(pdis->hDC,&(pdis->rcItem),(HBRUSH)GetStockObject(BLACK_BRUSH));//填充边框
cx=pdis->rcItem.right-pdis->rcItem.left;
cy=pdis->rcItem.bottom-pdis->rcItem.top;
switch (pdis->CtlID)
{
case ID_SMALLER:
pt[0].x=3*cx/8;pt[0].y=cy/8;
pt[1].x=5*cx/8;pt[1].y=cy/8;
pt[2].x=4*cx/8;pt[2].y=3*cy/8;
Triangle(pdis->hDC,pt);
pt[0].x=5*cx/8;pt[0].y=4*cy/8;
pt[1].x=7*cx/8;pt[1].y=3*cy/8;
pt[2].x=7*cx/8;pt[2].y=5*cy/8;
Triangle(pdis->hDC,pt);
pt[0].x=1*cx/8;pt[0].y=3*cy/8;
pt[1].x=1*cx/8;pt[1].y=5*cy/8;
pt[2].x=3*cx/8;pt[2].y=4*cy/8;
Triangle(pdis->hDC,pt);
pt[0].x=4*cx/8;pt[0].y=5*cy/8;
pt[1].x=3*cx/8;pt[1].y=7*cy/8;
pt[2].x=5*cx/8;pt[2].y=7*cy/8;
Triangle(pdis->hDC,pt);
break;
case ID_LAGER:
pt[0].x = 5 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
pt[1].x = 3 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 1 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 5 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
pt[1].x = 5 * cx / 8 ; pt[1].y = 3 * cy / 8 ;
pt[2].x = 7 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 3 * cx / 8 ; pt[0].y = 5 * cy / 8 ;
pt[1].x = 5 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
pt[2].x = 4 * cx / 8 ; pt[2].y = 7 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
pt[0].x = 3 * cx / 8 ; pt[0].y = 3 * cy / 8 ;
pt[1].x = 3 * cx / 8 ; pt[1].y = 5 * cy / 8 ;
pt[2].x = 1 * cx / 8 ; pt[2].y = 4 * cy / 8 ;
Triangle (pdis->hDC, pt) ;
break;
default:
break;
}
if(pdis->itemState&ODS_SELECTED)//选中后颜色反转
InvertRect(pdis->hDC,&pdis->rcItem);
if(pdis->itemState&ODS_FOCUS)
{
pdis->rcItem.left+=cx/16;
pdis->rcItem.right-=cx/16;
pdis->rcItem.top+=cx/16;
pdis->rcItem.bottom-=cx/16;
DrawFocusRect(pdis->hDC,&pdis->rcItem);//按钮有焦点后,画一个矩形
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
9.3、静态类 CreateWindow函数中指定窗口类别为Static,就可以建立静态文字的子窗口控件(不接收键鼠输入,也不向父窗口发送WM_COMMAND消息) 静态类别包括了三种文字样式:SS_LEFT、 SS_RIGHT和SS_CENTER(居左 居中 居右对齐) 9.4、滚动条类 与按钮控件不同,滚动条控件不向父窗口发送WM_COMMAND消息,而是像滚动条那样发送WM_VSCROLL和WM_HSCROOL消息。(通过lParam参数区分滚动条《0》与滚动条控件《值为滚动条窗口句柄》)。wParam消息参数高低字节含意相同。代码3GetSystemMetric(SM_CYHSCROLL); //取得流动条高度 GetSystemMetrics(SM_CXVSCROLL); //宽度 SetScrollRange (hwndScroll, SB_CTL, iMin,iMax, bRedraw) ; //确定范围和位置 SetScrollPos (hwndScroll, SB_CTL, iPos,bRedraw) ; SetScrollInfo (hwndScroll, SB_CTL, &si,bRedraw) ; //与窗口滚动条区别在于,窗口滚动条将父窗口句柄做为第一参数,以SB_VERT,SB_HORZ为第二参数
滚动条控件也能处理键盘输入,前提是拥有输入焦点。如下
事实上SB_TOP、SB_BUTTON卷动消息只能用键盘产生。 窗口子类别化 什么是窗口子类化? 即创建一个新的窗口函数代替原来的窗口函数。最大的特点是能够截取win消息。用GWL_WINDPROC为参数调用GetWindowLong可以得到窗口消息处理程序的地址。
给背景着色SetWindowLong给滚动条设定一个新的窗口处理函数。 OldScroll[i] = (WNDPROC) SetWindowLong(hwndScroll[i], GWL_WNDPROC,(LONG) ScrollProc)) ;//设定新窗口过程 、取得当前滚动条消息处理程序地址
改变滚动条设定时,必须建立一个画刷。DeleteObject ((HBRUSH) SetClassLong (hwnd, GCL_HBRBACKGROUND,(LONG)CreateSolidBrush (RGB (color[0], color[1], color[2])))) ; InvalidateRect (hwnd, &rcColor, TRUE) ; UpdateWindow (hwnd) ; DeleteObject ((HBRUSH) SetClassLong(hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject (WHITE_BRUSH))) ;