码迷,mamicode.com
首页 > Windows程序 > 详细

《Windows程序设计》读书笔九 子窗口控件

时间:2016-07-10 06:27:41      阅读:494      评论:0      收藏:0      [点我收藏+]

标签:

第九章 子窗口控件

子窗口可以作为控制屏幕图形显示,响应用户输入,以及在有重要输入事件的时候通知另一窗口。


标准子窗口控件,按钮,复选框,编辑框,列表框,组合框,文本字符串和滚动条。


可以使用CreateWindow来创建子窗口控件,或者在程序的资源脚本里编辑好各种属性。


使用预定义控件不需要再注册相应的子窗口类,这些类已经存在于windows中并且已经有了预定义的名称。

在调用CreateWindow时,只需要使用该名称作为窗口类的参数即可。


在窗口表明直接创建子窗口,所涉及的任务比使用对话框内的子窗口控件更底层。对于对话框,对话框管理器在你的程序和控件之间增加了一个隔离层。

比如支持TAB,方向键切换焦点。 子窗口控件可以得到输入焦点,可是一旦得到焦点,它就无法把输入焦点交回给其父窗口。


标准控件, 通用控件。


9.1 按钮类

#include <windows.h>    

struct  
{
	int		iStyle;
	TCHAR * szText;
}
button[] = 
{
	BS_PUSHBUTTON,		TEXT("PUSHBUTTON"),
	BS_DEFPUSHBUTTON,	TEXT("DEFPUSHBUTTON"),
	BS_CHECKBOX,		TEXT("CHECKBOX"),
	BS_AUTOCHECKBOX,	TEXT("AUTOCHECKBOX"),
	BS_RADIOBUTTON,		TEXT("RADIOBUTTON"),
	BS_3STATE,			TEXT("3STATE"),
	BS_AUTO3STATE,		TEXT("AUTO3STATE"),
	BS_GROUPBOX,		TEXT("GROUPBOX"),
	BS_AUTORADIOBUTTON,	TEXT("AUTORADIO"),
	BS_OWNERDRAW,		TEXT("OWNERDRAW")
};

#define NUM (sizeof button / sizeof button[0])

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[] = TEXT("BtnLook");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("Button Look"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND		hwndButton[NUM];
	static RECT		rect;
	static TCHAR	szTop[] =		TEXT("message            wParam       lParam"),
					szUnd[] =		TEXT("_______            ______       ______"),
					szFormat[] =	TEXT("%-16s%04X-%04X    %04X-%04X"),
					szBuffer[50];
	static int		cxChar, cyChar;
	HDC				hdc;
	PAINTSTRUCT		ps;
	int				i;

	switch (message) //get the message    
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());

		for (i = 0; i < NUM; i++)
			hwndButton[i] = CreateWindow(TEXT("button"),
				button[i].szText,
				WS_CHILD | WS_VISIBLE | button[i].iStyle,
				cxChar, cyChar * (1 + 2 * i),
				20 * cxChar, 7 * cyChar / 4,
				hwnd, (HMENU)i,
				((LPCREATESTRUCT)lParam)->hInstance, NULL);
		return 0;

	case WM_SIZE:
		rect.left = 24 * cxChar;
		rect.top = 2 * cyChar;
		rect.right = LOWORD(lParam);
		rect.bottom = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		InvalidateRect(hwnd, &rect, TRUE);

		hdc = BeginPaint(hwnd, &ps);

		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetBkMode(hdc, TRANSPARENT);

		TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop));
		TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd));

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DRAWITEM:
	case WM_COMMAND:
		ScrollWindow(hwnd, 0, -cyChar, &rect, &rect);

		hdc = GetDC(hwnd);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

		TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
			szBuffer,
			wsprintf(szBuffer, szFormat,
				message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") :
										 TEXT("WM_COMMAND"),
				HIWORD(wParam), LOWORD(wParam),
				HIWORD(lParam), LOWORD(lParam)));

		ReleaseDC(hwnd, hdc);
		ValidateRect(hwnd, &rect);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}
技术分享

9.1.1 创建子窗口

技术分享

GetDialogBaseUnits 来获得字符默认的字体的宽度和高度。 低位和高位分别是宽度和高度

与GetTextMetrics返回类似的数据

  1.     GetTextMetrics(hdc, &tm);  
  2.     cxChar = tm.tmAveCharWidth;  
  3.     cyChar = tm.tmHeight + tm.tmExternalLeading;  

每个子窗口的ID是唯一的


在WM_CREATE消息中lParam 是一个指向CREATESTRUCT结构的指针。  hInstance是改结构的成员  即 ( (LPCREATESTRUCT)lParam)->hInstance,

或者

 (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE)

9.1.2 子窗口传递信息给父窗口

点击按钮时,子窗口发送WM_COMMAND 消息给父窗口。

LOWORD(wParam)   子窗口ID

HIWORD(wParam) 通知码

lParam    子窗口句柄


通知码定义

技术分享

通知码6,7 只有当包含 BS_NOTIFY样式时才会启用

子窗口有输入焦点以后,所有的键盘消息都会送到这个子窗口控件,而不是主窗口。按钮控件一旦获得输入焦点,就会忽略所有按键操作,但空格键除外,此时空格键具有和单击鼠标一样的效果。


9.1.3 父窗口传递信息给子窗口

技术分享


例如父窗口发送 BM_GETCHECK BM_CHECK 给子窗口控件,以获得和设置复选框和单选框的选择状态。

在用鼠标或空格点击窗口时,BM_GETSTATE 和 BM_SETATETE消息反应一个窗口的状态是正常的还是被单击了。

BM_SETSTYLE 允许在创建按钮后改变按钮的样式。


id=GetWindowLong(hwndChild, GWL_ID);


id = GetDlgCtrlID (hwndChild);


hwndChild = GetDlgItem(hwndParent, id);


9.1.4 按钮

按钮控件 ,调用CreateWindow 或者MoveWindow可以改变其大小和位置, 是一个矩形。 按钮上显示文字

BS_PUSHBUTTON

BS_DEFPUSHBUTTON 较重的轮廓

按钮最佳视觉高度是字符高度的 7/4, 而宽度需要额外容纳两个文本


给子窗口发送BM_SETSTATE消息可以模拟按钮状态变化

SendMessage(hwndButton, BM_SETSTATE, 1, 0);   //按钮被按下

SendMessage(hwndButton, BM_SETSTATE, 0, 0);  //按钮回到正常状态

也可以给按钮发送一个BM_GETSTATE消息,子窗口控件返回当前按钮的状态。如果按钮时按下返回TRUE,否则FALSE.


9.1.5 复选框

checkBox

文本通常出现在复选框右侧,如果包含 BS_LEFTTEXT样式,则出现在左侧; 组合BS_RIGHT样式使文本右对齐

BS_CHECKBOX

必须给控件发送一个BM_SETCHECK消息来设置其选中标记。 wParam 设置1 会选中标记, 0则清楚标记。

发送BM_GETCHECK来获得复选框当前的状态。


在处理WM_COMMAND消息的时候

SendMessage((HWND)lParam , BM_SETCHECK, (WPARAM) 

! SendMessage((HWND)lParam, BM_GETCHECK, 0, 0 ), 0 );


BS_AUTOCHECKBOX

按钮本身负责切换选定和取消标记,可以忽略WM_COMMAND消息

在需要按钮状态时

iCheck = (int) SendMessage(hwndButton, BM_GETCHECK, 0, 0 );      //选中为TRUE ,否则为FALSE


另外 BS_3STATE 和  BS_AUTO3STATE 有3种状态   BM_SETCHECK消息射wParam 为 2 表示灰色

复选框最低高度是一个字符高度,最小宽度是现有字符再加2个字符宽度。


9.1.6 单选按钮

任意时刻只有一个按钮可以被按下。

BS_RADIOBUTTON,  BS_AUTORADIOBUTTON(只用于对话框)

在处理WM_COMMAND消息是应该向其发送消息表明其选中

SendMessage((HWND)lParam, BM_SETCHECK, 1, 0);

发送以下消息表明取消选中

SendMessage((HWND)lParam, BM_SETCHECK, 0, 0);


9.1.7 组合框

GroupBox

通常用来包容其他类型的控件


9.1.8  改变按钮文本

可以调用SetWindowText 来改变按钮的文本

SetWindowText(hwnd, pszString); //包括主窗口和子窗口


iLength = GetWindowText(hwnd, pszBuffer, iMaxLength); //获得窗口当前的文本 Caption属性


可以调用函数使程序对可以接受特定文本长度有所准备

iLength = GetWindowTextLength(hwnd);


9.1.9 可见的按钮和启动的按钮

如果创建子窗口未包含WM_VISIBLE 按钮时不可见的,除非调用

ShowWindow(hwndChild, SW_SHOWNORMAL);

如果窗口可见可以隐藏他

ShowWindow(hwndChild, SW_HIDE);

可以调用一下函数判断窗口是否可见

IsWindowVisible(hwndChild);

启用或禁用子窗口

EnableWindow(hwndChild, FALSE);


EnableWindow(hwndChild, TRUE);


调用一下函数判断子窗口是否被启用

IsWindowEnabled(hwndChild);


9.1.10 按钮和输入焦点

子窗口控件获得输入焦点后,父窗口就会失去输入焦点。之后所有的键盘输入将送到子窗口而不是其父窗口。


Windows把输入焦点从一个窗口切换到另一个窗口时,它首先会像要失去输入焦点的窗口发送一条消息WM_KILLFOCUS. 相应的wParam参数是将要获得输入焦点的窗口的句柄。然后Windows向要接受输入焦点的窗口发送WM_SETFOCUS消息,用wParam指定失去输入焦点的窗口的句柄。

父窗口可以通过WM_KILLFOCUS消息来阻止子窗口控件获得输入焦点。

	case WM_KILLFOCUS:
		for (i = 0; i < NUM; i++)
			if (hwndButton[i] == (HWND)wParam)
			{
				SetFocus(hwnd);
				break;
			}
这样子窗口将不会获得输入焦点

或者

	case WM_KILLFOCUS:
		if (hwnd == GetParent((HWND)wParam))
			SetFocus(hwnd);

单这么做以后按钮将不能再响应消息。因为按钮从未得到输入焦点。

9.2 控件和颜色

9.2.1 系统颜色

windows有29种系统颜色来支持各部分显示。可以使用GetSysColor 和SetSysColor获取并设置这些颜色。

技术分享


技术分享


9.2.2 按钮的颜色

COLOR_BTNFACE  按钮表面

COLOR_BTNSHADOW 用于按钮底部和右侧,复选框方块的内部和单选按钮的圆圈内,用来表示阴影。

COLOR_BTNTEXT 文本颜色

COLOR_WINDOWTEXT 其他控件文本颜色

为了在客户区表面显示按钮,首先选用COLOR_BTNFACE作为客户区的背景颜色


wndClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE+1); //为了防止出现NULL值


TextOut显示文本时,windows使用设备环境中定义的值作为文本背景色,预设是白色背景  黑色文本。

SetTextColor  SetBkColor  改变文办颜色和背景颜色


SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));

SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));

如果用户更改了系统颜色,则需要更改文办的背景颜色和文办颜色

case WM_SYSCOLORCHANGE:

InvalidateRect(hwnd, NULL, TRUE);

        break;


修改后的Button Look 程序

#include <windows.h>    

struct  
{
	int		iStyle;
	TCHAR * szText;
}
button[] = 
{
	BS_PUSHBUTTON,		TEXT("PUSHBUTTON"),
	BS_DEFPUSHBUTTON,	TEXT("DEFPUSHBUTTON"),
	BS_CHECKBOX,		TEXT("CHECKBOX"),
	BS_AUTOCHECKBOX,	TEXT("AUTOCHECKBOX"),
	BS_RADIOBUTTON,		TEXT("RADIOBUTTON"),
	BS_3STATE,			TEXT("3STATE"),
	BS_AUTO3STATE,		TEXT("AUTO3STATE"),
	BS_GROUPBOX,		TEXT("GROUPBOX"),
	BS_AUTORADIOBUTTON,	TEXT("AUTORADIO"),
	BS_OWNERDRAW,		TEXT("OWNERDRAW")
};

#define NUM (sizeof button / sizeof button[0])

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[] = TEXT("BtnLook");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("Button Look"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND		hwndButton[NUM];
	static RECT		rect;
	static TCHAR	szTop[] =		TEXT("message            wParam       lParam"),
					szUnd[] =		TEXT("_______            ______       ______"),
					szFormat[] =	TEXT("%-16s%04X-%04X    %04X-%04X"),
					szBuffer[50];
	static int		cxChar, cyChar;
	HDC				hdc;
	PAINTSTRUCT		ps;
	int				i;

	switch (message) //get the message    
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());

		for (i = 0; i < NUM; i++)
			hwndButton[i] = CreateWindow(TEXT("button"),
				button[i].szText,
				WS_CHILD | WS_VISIBLE | button[i].iStyle,
				cxChar, cyChar * (1 + 2 * i),
				20 * cxChar, 7 * cyChar / 4,
				hwnd, (HMENU)i,
				(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE)/*((LPCREATESTRUCT)lParam)->hInstance*/, NULL);
		return 0;

	case WM_SIZE:
		rect.left = 24 * cxChar;
		rect.top = 2 * cyChar;
		rect.right = LOWORD(lParam);
		rect.bottom = HIWORD(lParam);
		return 0;

	case WM_SYSCOLORCHANGE:
		InvalidateRect(hwnd, NULL, TRUE);
		break;

	case WM_PAINT:
		InvalidateRect(hwnd, &rect, TRUE);

		hdc = BeginPaint(hwnd, &ps);

		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetBkMode(hdc, TRANSPARENT);

		SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
		SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));

		TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop));
		TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd));

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DRAWITEM:
	case WM_COMMAND:
		ScrollWindow(hwnd, 0, -cyChar, &rect, &rect);

		hdc = GetDC(hwnd);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

		SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
		SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));

		TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
			szBuffer,
			wsprintf(szBuffer, szFormat,
				message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") :
										 TEXT("WM_COMMAND"),
				HIWORD(wParam), LOWORD(wParam),
				HIWORD(lParam), LOWORD(lParam)));
		switch(LOWORD(wParam))	
		{
		case 0:
			EnableWindow(hwndButton[1], FALSE);
			break;
		case 2://checkbox
			SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM)
				!SendMessage((HWND)lParam, BM_GETCHECK, 0, 0), 0);
			break;
		case 4://Radiobutton
			SendMessage((HWND)lParam, BM_SETCHECK, 1, 0);
			break;

		}

		ReleaseDC(hwnd, hdc);
		ValidateRect(hwnd, &rect);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

技术分享


9.2.3 WM_CTLCOLORBTN 消息

最好不要用SetSysColors改变按钮外观,这将改变windows环境下正在运行的所有程序

或者重载WM_CTLCOLORBTN消息,当子窗口需要重绘时按钮会把这个消息发给父窗口。可以利用这个机会来改变子窗口颜色

当父窗口收到WM_CTLCOLORBTN消息时,wParam是子窗口的设备环境句柄,lParam是子窗口句柄。

SetTextColor 设置文本颜色

SetBkColor 设置文本背景色

返回子窗口的画刷句柄 子窗口使用这个画刷来着色背景。在不需要画刷时,你需要负责销毁画刷。

9.2.4 自绘按钮

#include <windows.h>    

#define ID_SMALLER	1
#define ID_LARGER	2
#define BTN_WIDTH	(8 * cxChar)
#define BTN_HEIGHT	(4 * cyChar)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

HINSTANCE hInst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[] = TEXT("OwnDraw");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	hInst = hInstance;

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = szAppName;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("Owner-Draw Button Demo"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}


void Triangle(HDC hdc, POINT pt[])
{
	SelectObject(hdc, GetStockObject(BLACK_BRUSH));
	Polygon(hdc, pt, 3);
	SelectObject(hdc, GetStockObject(WHITE_BRUSH));
}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND			hwndSmaller, hwndLarger;
	static int			cxClient, cyClient, cxChar, cyChar;
	int					cx, cy;
	LPDRAWITEMSTRUCT	pdis;
	POINT				pt[3];
	RECT				rc;


	switch (message) //get the message    
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());

		//Create the owner-draw pushbuttons

		hwndSmaller = CreateWindow(TEXT("button"), TEXT(""),
			WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
			0, 0, BTN_WIDTH, BTN_HEIGHT,
			hwnd, (HMENU) ID_SMALLER,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);

		hwndLarger = CreateWindow(TEXT("button"), TEXT(""),
			WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
			0, 0, BTN_WIDTH, BTN_HEIGHT,
			hwnd, (HMENU)ID_LARGER,
			((LPCREATESTRUCT)lParam)->hInstance, NULL);
			
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);

		//Move the buttons to the new center

		MoveWindow(hwndSmaller, cxClient / 2 - 3 * BTN_WIDTH / 2,
								cyClient / 2 - BTN_HEIGHT / 2,
								BTN_WIDTH, BTN_HEIGHT, TRUE);
		MoveWindow(hwndLarger, cxClient / 2 + BTN_WIDTH / 2,
							   cyClient / 2 - BTN_HEIGHT / 2,
							   BTN_WIDTH, BTN_HEIGHT, TRUE);

		return 0;

	case WM_COMMAND:
		GetWindowRect(hwnd, &rc);

		//Make the wnidow 10% smaller or larger
		switch (wParam)
		{
		case ID_SMALLER:
			rc.left += cxClient / 20;
			rc.right -= cxClient / 20;
			rc.top += cyClient / 20;
			rc.bottom -= cyClient / 20;
			break;

		case ID_LARGER:
			rc.left -= cxClient / 20;
			rc.right += cxClient / 20;
			rc.top -= cyClient / 20;
			rc.bottom += cyClient / 20;
		}

		MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);

		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));

		//Draw inward and outward black triangles
		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 = 1 * cy / 8;
			pt[1].x = 5 * cx / 8;  pt[1].y = 1 * cy / 8;
			pt[2].x = 4 * cx / 8;  pt[2].y = 3 * cy / 8;

			Triangle(pdis->hDC, pt);

			pt[0].x = 7 * cx / 8;  pt[0].y = 3 * cy / 8;
			pt[1].x = 7 * cx / 8;  pt[1].y = 5 * cy / 8;
			pt[2].x = 5 * cx / 8;  pt[2].y = 4 * cy / 8;

			Triangle(pdis->hDC, pt);

			pt[0].x = 5 * cx / 8;  pt[0].y = 7 * cy / 8;
			pt[1].x = 3 * cx / 8;  pt[1].y = 7 * cy / 8;
			pt[2].x = 4 * cx / 8;  pt[2].y = 5 * cy / 8;

			Triangle(pdis->hDC, pt);

			pt[0].x = 1 * cx / 8;  pt[0].y = 5 * cy / 8;
			pt[1].x = 1 * cx / 8;  pt[1].y = 3 * cy / 8;
			pt[2].x = 3 * cx / 8;  pt[2].y = 4 * cy / 8;

			Triangle(pdis->hDC, pt);
			break;
		case ID_LARGER:
			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;
		}
		
		//Invert the rectangle if the button is selected
		if (pdis->itemState & ODS_SELECTED)
			InvertRect(pdis->hDC, &pdis->rcItem);

		//Draw a focus rectangle if the button has the focus

		if (pdis->itemState & ODS_FOCUS)
		{
			pdis->rcItem.left += cx / 16;
			pdis->rcItem.top += cy / 16;
			pdis->rcItem.right -= cx / 16;
			pdis->rcItem.bottom -= cy / 16;

			DrawFocusRect(pdis->hDC, &pdis->rcItem);
		}

		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

该例子绘制了两个按钮,点击能分别扩大和缩小窗口客户区的尺寸

技术分享

在按钮上显示图标或位图,可以使用BS_ICON 或者BS_BITMAP样式。 设置位图可以使用BM_SETIMAGE消息。

但BS_OWNERDRAW允许你完全自己绘制按钮。

GetWindowRect存储整个客户区的矩形区域,位置是相对屏幕的坐标


DRAWITEMSTRUCT结构中与绘制按钮相关的字段

hDC 按钮设备环境

rcItem RECT结构

CtlID 控件的ID

itemState 按钮是否按下或者有输入焦点


InvertRect 用来颠倒矩形区域的颜色  ODS_SELECTED

DrawFocusRect   绘制焦点框  ODS_FOCUS


9.3  静态类

使用CreateWindow函数和静态窗口类来创建静态子窗口控件。

当单机静态子窗口时,会获得WM_NCHITTEST消息, 并向windows返回HITTRANSPARENT值。 通常交给DefWindowProc处理

静态窗口样式

SS_BLACKRECT SS_BLACKFRAME

SS_GRAYRECT SS_GRAYFRAME

SS_WHITERECT SS_WHITEFRAME

基于下表所显示的系统颜色

技术分享

CreateWindow 调用的窗口文本字段将被忽略。 矩形左上角坐标是相对父窗口。 可以使用SS_ETCHEDHORZ, SS_ETCHEDVERT  或者SS_ETCHEDFRAME样式来用白色和灰色创建一个阴影框架

静态类包含三个文本样式: SS_LEFT, SS_RIGHT 和 SS_CENTER。 这样会创建左对齐,右对齐和居中的文本。 文本右CreateWindow函数窗口文本参数确定

可以使用SetWindowText修改。  当静态类显示文本时,会使用DT_WORDBREAK, DT_NOCLIP 和  DT_EXPANDTABS等参数来调用DrawText函数。

子窗口矩形框具有文本自动换行功能。

这三种样式的背景颜色通常是 COLOR_BTNFACE,   文本颜色是  COLOR_WINDOWTEXT.  

可以复WM_COLORSTATIC消息,调用SetTextColor 和SetBkColor 分别改变文本颜色和背景颜色,同时返回背景画刷句柄。

静态类还有两个样式  SS_ICON  SS_USERITEM 然后当它用作子窗口控件,这些样式时无效的。


9.4  滚动条类

滚动条不发送WM_COMMAND消息到父窗口。  他发送WM_VSCROLL 和  WM_HSCROLL  lParam参数区分窗口滚动条和滚动条控件。   lParam为0  就是窗口滚动条

如果等于滚动条窗口句柄,就是滚动条空降 wParam  高位和低位对于窗口滚动条和滚动条控件含义是一样的

可以使用MoveWindow 或者CreateWindow来设定滚动条控件的大小。

可以使用GetSystemMetrics(SM_CYHSCROLL)   水平滚动条的高度

GetSystemMetrics(SM_CXVSCROLL); 垂直滚动条的宽度

滚动条窗口样式标识符  SBS_LEFTALIGN,  SBS_RIGHTALIGN,  SBS_TOPALIGN 和  SBS_BOTTOMALIGN  都为滚动条提供标准尺寸。

SetScrollRange(hwndScroll, SB_CTL, iMin, iMax, bRedraw);

SetScrollPos(hwndScroll, SB_CTL, iPos, bRedraw);

SetScrollInfo(hwndScroll, SB_CTL, &si, bRedraw);

滚动条两端按钮颜色基于 COLOR_BTNFACE, COLOR_BTNHILIGHT,  COLOR_BTNSHADOW, COLOR_BTNTEXT(给小箭头使用),COLOR_DKSHADOW 以及COLOR_BTNLIGHT.

两端按钮之间的大片区域基于  COLOR_BTNFACE和 COLOR_BTNHILIGHT的某种组合。

如果俘获了WM_CTLCOLORSCROLLBAR消息,就可以从这个消息返回一个画刷来改变原理的颜色。


9.4.1 COLORS1 程序


#include <windows.h>    

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.  
LRESULT CALLBACK ScrollProc(HWND, UINT, WPARAM, LPARAM);

int		idFocus;
WNDPROC OldScroll[3];

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[] = TEXT("Colors1");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("Color Scroll"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static COLORREF	crPrim[3] = { RGB(255, 0, 0), RGB(0, 255, 0), RGB(0, 0, 255) };
	static HBRUSH	hBrush[3], hBrushStatic;
	static HWND		hwndScroll[3], hwndLabel[3], hwndValue[3], hwndRect;
	static int		color[3], cyChar;
	static RECT		rcColor;
	static TCHAR *	szColorLabel[] = { TEXT("Red"), TEXT("Green"), TEXT("Blue") };
	HINSTANCE		hInstance;
	int				i, cxClient, cyClient;
	TCHAR			szBuffer[10];


	switch (message) //get the message    
	{
	case WM_CREATE:
		hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);

		//Create the white-rectangle window against which the
		//scroll bars will be positioned. The child window ID is 9.

		hwndRect = CreateWindow(TEXT("static"), NULL,
			WS_CHILD | WS_VISIBLE | SS_WHITERECT,
			0, 0, 0, 0,
			hwnd, (HMENU) 9,
			hInstance, NULL);

		for (i = 0; i < 3; i++)
		{
			//The three scroll bars have IDs 0, 1, and 2, with
			//scroll bar ranges from 0 through 255.
			hwndScroll[i] = CreateWindow(TEXT("scrollbar"), NULL,
				WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT,
				0, 0, 0, 0,
				hwnd, (HMENU) i,
				hInstance, NULL);
			SetScrollRange(hwndScroll[i], SB_CTL, 0, 255, FALSE);
			SetScrollPos  (hwndScroll[i], SB_CTL, 0, FALSE);

			//The three color-name labels have IDs 3, 4, and 5,
			//and text strings "Red", "Green", "Blue".

			hwndLabel[i] = CreateWindow(TEXT("static"), szColorLabel[i],
				WS_CHILD | WS_VISIBLE | SS_CENTER,
				0, 0, 0, 0,
				hwnd, (HMENU) (i + 3),
				hInstance, NULL);

			//The three color-value text fields have Ids 6, 7,
			//and 8, and initial text strings of "0".

			hwndValue[i] = CreateWindow(TEXT("static"), TEXT("0"),
				WS_CHILD | WS_VISIBLE | SS_CENTER,
				0, 0, 0, 0,
				hwnd, (HMENU)(i + 6),
				hInstance, NULL);

			OldScroll[i] = (WNDPROC)SetWindowLong(hwndScroll[i], GWL_WNDPROC, (LONG)ScrollProc); //Register the windowproc to sub windows.

			hBrush[i] = CreateSolidBrush(crPrim[i]);
		}

		hBrushStatic = CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT));

		cyChar = HIWORD(GetDialogBaseUnits());
			
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		SetRect(&rcColor, cxClient / 2, 0, cxClient, cyClient);

		MoveWindow(hwndRect, 0, 0, cxClient / 2, cyClient, TRUE);

		for (i = 0; i < 3; i++)
		{
			MoveWindow(hwndScroll[i],
				(2 * i + 1) * cxClient / 14, 2 * cyChar,
				cxClient / 14, cyClient - 4 * cyChar, TRUE);

			MoveWindow(hwndLabel[i],
				(4 * i + 1) * cxClient / 28, cyChar / 2,
				cxClient / 7, cyChar, TRUE);

			MoveWindow(hwndValue[i],
				(4 * i + 1) * cxClient / 28, cyClient - 3 * cyChar / 2,
				cxClient / 7, cyChar, TRUE);
		}

		SetFocus(hwnd);

		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndScroll[idFocus]);
		return 0;

	case WM_VSCROLL:
		i = GetWindowLong((HWND)lParam, GWL_ID);

		switch (LOWORD(wParam))
		{
		case SB_PAGEDOWN:
			color[i] += 15;
			//fall through
		case SB_LINEDOWN:
			color[i] = min(255, color[i] + 1);
			break;

		case SB_PAGEUP:
			color[i] -= 15;
			//fall through
		case SB_LINEUP:
			color[i] = max(0, color[i] - 1);
			break;

		case SB_TOP:
			color[i] = 0;
			break;

		case SB_BOTTOM:
			color[i] = 255;
			break;

		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			color[i] = HIWORD(wParam);
			break;
		default:
			break;
		}
		SetScrollPos(hwndScroll[i], SB_CTL, color[i], TRUE);
		wsprintf(szBuffer, TEXT("%i"), color[i]);
		SetWindowText(hwndValue[i], szBuffer);
		DeleteObject((HBRUSH)
			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				CreateSolidBrush(RGB(color[0], color[1], color[2]))));

		InvalidateRect(hwnd, &rcColor, TRUE);
		return 0;

	case WM_CTLCOLORSCROLLBAR:
		i = GetWindowLong((HWND)lParam, GWL_ID);
		return (LRESULT)hBrush[i];

	case WM_CTLCOLORSTATIC:
		i = GetWindowLong((HWND)lParam, GWL_ID);

		if (i >= 3 && i <= 8) // static text controls
		{
			SetTextColor((HDC)wParam, crPrim[i % 3]);
			SetBkColor((HDC)wParam, GetSysColor(COLOR_BTNHIGHLIGHT));
			return (LRESULT)hBrushStatic;
		}
		break;

	case WM_SYSCOLORCHANGE:
		DeleteObject(hBrushStatic);
		hBrushStatic = CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT));
		return 0;

	case WM_DESTROY:
		DeleteObject((HBRUSH)
			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				GetStockObject((WHITE_BRUSH))));

		for (i = 0; i < 3; i++)
			DeleteObject(hBrush[i]);
		DeleteObject(hBrushStatic);
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK ScrollProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int id = GetWindowLong(hwnd, GWL_ID);
	switch (message)
	{
	case WM_KEYDOWN:
		if (wParam == VK_TAB)
			SetFocus(GetDlgItem(GetParent(hwnd),
			(id + (GetKeyState(VK_SHIFT) < 0 ? 2 : 1)) % 3));
		break;

	case WM_SETFOCUS:
		idFocus = id;
		break;
	}
	return CallWindowProc(OldScroll[id], hwnd, message, wParam, lParam);
}
运行结果

技术分享


9.4.2  自动键盘接口


在CreateWindow 中加入WS_TABSTOP 标识符。当滚动条获得输入焦点时,滚动条上会显示一个闪烁的灰色快。

父窗口在获得焦点以后要捕获WM_SETFOCUS消息,并传递给子窗口  

SetFocus(hwndScroll[idFocus]);


9.4.3 窗口子类

滚动条类的窗口过程在windows内部

。 但是可以调用GetWindowLong来获取地址使用GWL_WNDPROC标识符作为参数即可。还可以调用SetWindowLong 为滚动条设置一个新的窗口过程,  称为窗口子类。

OldScroll[i] = (WNDPROC)SetWindowLong(hwndScroll[i], GWL_WNDPROC, (LONG)ScrollProc); //Register the windowproc to sub windows.

设置新的滚动条窗口过程,并且保留现有滚动条窗口过程的地址。


9.4.4 背景颜色

使用GetClassWord 和SetClassWord来获取和设置窗口背景画刷的句柄。

你可以创建新的画刷,把句柄存入到窗口类的结构中,然后删除旧的画刷

		DeleteObject((HBRUSH)
			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				CreateSolidBrush(RGB(color[0], color[1], color[2]))));

下次windows重绘窗口时,就会使用这个新的画刷


可以强制让windows擦除背景

InvalidateRect(hwnd, &rcColor, TRUE);


UpdateWindow(hwnd); 立即更新  不过可能会拖慢键盘和鼠标处理速度。


在处理WM_DESTORY消息时, 删除当前创建的画刷

		DeleteObject((HBRUSH)
			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				GetStockObject((WHITE_BRUSH))));

9.4.5 给滚动条和静态文本着色

滚动条着色是通过处理  WM_CTLCOLORSCROLLBAR消息来完成的。

返回一个已经创建的画刷   return (LRESULT) hBrush[i];

这些画刷必须在WM_DESTROY消息期间被销毁

for(i = 0; i < 3; i++)

    DeleteObject(hBrush[i]);


静态文本框颜色是通通过WM_CTLCOLORSTATIC消息和调用SetTextColor完成。 文本背景调用SetBkColor设置为  COLOR_BTNHIGHLIGHT,但该颜色仅限制于显示文字的区域。同时返回一个hBrushStatic 设置静态文本窗口的整个背景颜色。


为了防止系统颜色改变

在WM_SYSCOLORCHANGE消息时用心颜色重新创建hBrushStatic.


9.5 编辑类


POPPAD1

#include <windows.h>    

#define ID_EDIT	1

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.  

TCHAR	szAppName[] = TEXT("PopPad1");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	//static      TCHAR szAppName[] = TEXT("Colors1");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("PopPad1"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND	hwndEdit;

	switch (message) //get the message    
	{
	case WM_CREATE:
		hwndEdit = CreateWindow(TEXT("edit"), NULL,
			WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
			WS_BORDER | ES_LEFT | ES_MULTILINE |
			ES_AUTOHSCROLL | ES_AUTOVSCROLL,
			0, 0, 0, 0,
			hwnd, (HMENU)ID_EDIT,
			((LPCREATESTRUCT)lParam)->hInstance, NULL);
			
		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndEdit);
		return 0;

	case WM_SIZE:
		MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_EDIT)
			if (HIWORD(wParam) == EN_ERRSPACE ||
				HIWORD(wParam) == EN_MAXTEXT)
				MessageBox(hwnd, TEXT("Edit control out of spae."),
					szAppName, MB_OK | MB_ICONSTOP);
		return 0;

	case WM_DESTROY:

		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}
运行结果如下


技术分享

9.5.1 编辑类的样式

在失去焦点依然保持高亮要选用 ES_NOHIDESEL样式

在POPPAD1 程序创建编辑控件时,相应的样式时在CreateWindow调用中指定的

WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
WS_BORDER | ES_LEFT | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL


编辑控件的尺寸是在收到WM_SIZE以后调用MoveWindow设置的

MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);


9.5.2 编辑控件的通知消息

技术分享

9.5.3 使用编辑控件

如果使用了多个编辑控件,需要拦截TAB消息或者SHIFT TAB来实现焦点切换。

使用SetWindowText 像编辑框插入内容。 从编辑控件读取文本使用GetWindowTextLength 和GetWindowText


9.5.4 传递给编辑控件的消息

SendMessage(hwndEdit, WM_CUT, 0, 0 );

SendMessage(hwndEdit, WM_COPY, 0, 0 );

SendMessage(hwndEdit, WM_CLEAR, 0, 0);


WM_CUT消息把当前选择从编辑控件中移走并发送给剪贴板。

WM_COPY 把当前选择从编辑框控件拷贝到剪贴板

WM_CLEAR 把当前选择从编辑控件删除并且不传递给剪贴板


SendMessage(hwndEdit, WM_PASTE, 0, 0);

把剪贴板上的文本插入到编辑控件的当前位置

使用如下函数获得当前选择文本的出师位置和终止位置

SendMesssage(hwndEdit, EM_GETSEL, (WPARAM)&iSart, (LPARAM) &iEnd);

终止位置是你选择最后一个字符的位置加上1

可以如下选择文本

SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd);

使用其他文本取代当前选择的文本

SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szString);


获得文本编辑器的行数

iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0);

获得从编辑器缓冲区的起点到这行的位移量

iOffset = SendMessage(hwndEdit, EM_LINEINDEX, iLine, 0 );

行的长度

iLength = SendMessage(hwndEdit, EM_LINELENGTH, iLine, 0);

复制某一行到缓冲区

iLength = SendMessage(hwndEdit, EM_GETLINE, iLine, (LPARAM)szBuffer);


9.6 列表框控件

支持单选或者多选 当有项目被选择会发送WM_COMMAND消息到其父窗口

被选中的项目会高亮显示

在单选列表框中,空格键选择项目。方向键可以移动光标和当前选择,可以滚动列表框的内容。 上下翻页也可以移动光标来滚动列表框,但不会移动项目,单击或者双击也可以选中项

多选列表框中,空格键用于切换光标所在项目的状态。方向键取消所有一切选定的项目,并移动光标和选中项。

9.6.1 列表框样式

WS_CHILD 子窗口控件

LBS_NOTIFY 使父窗口能够接受从列表框发来的WM_COMMAND信息

LBS_SORT 对项目进行排序

LBS_MULTIPLESEL 默认是单选 多选使用该样式

LBS_NOREDRAW 防止列表框自动更新

使用WM_SETREDRAW 消息

WS_BORDER 显示边框

WS_VSCROLL 显示垂直滚动条


在Windows头文件中定义了一个最标准的列表框样式

LBS_STANDARD  包含  (LBS_NOFITY | LBS_SORT | WS_VSCROLL | WS_BORDER)

WS_SIZEBOX 和 WS_CAPTION 允许用户调整列表框的尺寸,以及在父窗口的客户区移动它

GetSystemMetrics(SM_CXVSCROLL) 垂直滚动条的宽度

列表框的宽度 =  最长字符串的长度 + 滚动条的宽度。


9.6.2 向列表框添加字符

SendMessage 和索引来引用 最上方索引为0 

hwndList   iIndex

SendMessage传入文本字符串的时候 ,lParam参数是一个以空字符结尾的字符串的指针。

如果列表框用完了存储空间,SendMessage调用可能返回  LB_ERRSPACE值 ,如果其他错误则返回LB_ERR, 如果成功返回LB_OKAY

如果设定了LBS_SORT样式则 使用LB_ADDSTRING来添加字符串

SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);

如果未使用LBS_SORT则使用

SendMessage(hwndList, LB_INSERTSTRING, iIndex, (LPARAM)szString);  //不排序


SendMessage(hwndList, LB_DELETESTRING, iIndex, 0 ) 删除列表框的指定位置处删除一字符串


SendMessage(hwndList, LB_RESETCONTENT, 0, 0); 删除所有项目


SendMessage(hwndList, WM_SETREDRAW, FALSE, 0 ); 关掉列表框重绘


SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); 开启重绘


LBS_NOREDRAW样式的列表框最初开始会关闭重绘标志


9.6.3 项目的选取和提取


返回值LB_ERR


iCount = SendMessage(hwndList, LB_GETCOUNT, 0, 0 );  了解列表框有多少项目


对于单选

SendMessage(hwndList, LB_SETCURSEL, iIndex, 0);  设定选中项目

SendMessage(hwndList, LB_SETCURSEL, iIndex, -1);  取消选中项目


iIndex = SendMessage(hwndList, LB_SELECTSTRING, iIndex, (LPARAM)szSearchString); 根据起始字符选择

在收到来自列表框的WM_COMMAND消息时,可以使用LB_GETCURSEL获得当前选择的索引值

iIndex = SendMessage(hwndList, LB_GETCURSEL, 0, 0 );

如果未选择则返回  LB_ERR

iLength = SendMessage(hwndList, LB_GETTEXTLEN, iIndex, 0);  获得列表框中任何字符串的长度

并把改项目复制到文本缓冲区

iLength = SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)szBuffer);

可以利用GETTEXTLEN来为字符串存储预先分配空间。


对于多选框

不可使用  LB_SETCURSEL, LB_GETCURSEL 或者  LB_SELECTSTRING

应该使用LB_SETSEL

SendMessage(hwndList, LB_SETSEL, wParam, iIndex); 设定某个特定项目的选择状态。  wParam非0  选择并高亮显示, 如果是0. 取消选择。如果lParam则选择全部或者全部取消选择

iSelect = SendMessage(hwndList, LB_GETSEL, iIndex, 0); 判断指定项目是否被选择


9.6.4 接收来自列表框的消息

SetFocus(hwndList) 把焦点传给列表框


技术分享

只有包含了LBS_NOFITY 列表框控件才会向父窗口发送LBN_SELCHANGE 和  LBN_DBLCLK.

9.6.5 简单的列表框程序


#include <windows.h>    

#define ID_LIST		1
#define ID_TEXT		2

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.  

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{	
	static      TCHAR szAppName[] = TEXT("Environ");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("Environment List Box"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

void FillListBox(HWND hwndList)
{
	int		iLength;
	TCHAR *	pVarEnv, * pVarBlock, *pVarBeg, *pVarEnd, *pVarName;

	pVarEnv = GetEnvironmentStrings(); // Get pointer to environment block.
	pVarBlock = pVarEnv;

	while (*pVarBlock)
	{
		if (*pVarBlock != TEXT('=')) //Skip variable names beginning with '='
		{
			pVarBeg = pVarBlock;			//Beginning of variable name
			while (*pVarBlock++ != TEXT('='));	//Scan until '='
			pVarEnd = pVarBlock - 1;		//Points to '=' sign
			iLength = pVarEnd - pVarBeg;	//Length of variable name

			//Allocate memory for the variable name and terminating
			//zero. Copy the variable name and append a zero.

			pVarName = (TCHAR*)calloc(iLength + 1, sizeof(TCHAR));
			CopyMemory(pVarName, pVarBeg, iLength * sizeof(TCHAR));
			pVarName[iLength] = TEXT('\0');

			//Put the variable name in the list box and free memory.
			SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)pVarName);
			free(pVarName);
		}
		while (*pVarBlock++ != TEXT('\0'));	//Scan until terminating zero
	}

	FreeEnvironmentStrings(pVarEnv);
}


//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND	hwndList, hwndText;
	int			iIndex, iLength, cxChar, cyChar;
	TCHAR	  *	pVarName, *pVarValue;

	switch (message) //get the message    
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());

		//Create listbox and static text windows.

		hwndList = CreateWindow(TEXT("listbox"), NULL,
			WS_CHILD | WS_VISIBLE | LBS_STANDARD,
			cxChar, cyChar * 3,
			cxChar * 16 + GetSystemMetrics(SM_CXVSCROLL),
			cyChar * 6,
			hwnd, (HMENU)ID_LIST,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
			NULL);

		hwndText = CreateWindow(TEXT("static"), NULL,
			WS_CHILD | WS_VISIBLE | SS_LEFT,
			cxChar, cyChar,
			GetSystemMetrics(SM_CXSCREEN),
			cyChar,
			hwnd, (HMENU)ID_TEXT,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
			NULL);

		FillListBox(hwndList);
			
		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndList);
		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_LIST && HIWORD(wParam) == LBN_SELCHANGE)
		{
			//Get current selection.

			iIndex = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
			iLength = SendMessage(hwndList, LB_GETTEXTLEN, iIndex, 0) + 1;
			pVarName = (TCHAR*)calloc(iLength, sizeof(TCHAR));
			SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)pVarName);

			//Get environment string.
			iLength = GetEnvironmentVariable(pVarName, NULL, 0);
			pVarValue = (TCHAR*)calloc(iLength, sizeof(TCHAR));
			GetEnvironmentVariable(pVarName, pVarValue, iLength);

			//Show it in window
			SetWindowText(hwndText, pVarValue);
			free(pVarName);
			free(pVarValue);
		}
		return 0;

	case WM_DESTROY:

		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}
运行结果


技术分享


9.6.6 列出文件

SendMessage(hwndList, LB_DIR, iAttr, (LPARAM) szFileSpec);

将文件目录列表写入列表框中,包括子目录和有效的磁盘驱动器。

技术分享

lParam 是一个指向文件限定字符(*.*)的指针。该文件限定不影响列表框包含的子目录

实例代码


		hwndListDir = CreateWindow(TEXT("listbox"), NULL,
			WS_CHILD | WS_VISIBLE | LBS_STANDARD ,
			cxChar, cyChar * 10,
			cxChar * 16 + GetSystemMetrics(SM_CXVSCROLL),
			cyChar * 6,
			hwnd, (HMENU)ID_LIST,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
			NULL);

SendMessage(hwndListDir, LB_DIR, DDL_READWRITE | DDL_DRIVES | DDL_DIRECTORY,
(LPARAM)(TEXT("*.*")));

9.6.7 Windows的HEAD程序

#include <windows.h>    

#define ID_LIST		1
#define ID_TEXT		2
#define MAXREAD		8192
#define DIRATTR		(DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | 					DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES)
#define DTFLAGS		(DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX)


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.  
LRESULT CALLBACK ListProc(HWND, UINT, WPARAM, LPARAM);

WNDPROC OldList;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{	
	static      TCHAR szAppName[] = TEXT("head");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class    

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.    
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.     
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.    
	hwnd = CreateWindow(szAppName,      //Window class name    
		TEXT("head"),      //Window caption    
		WS_OVERLAPPEDWINDOW,            //Window Style    
		CW_USEDEFAULT,                  //initial x position    
		CW_USEDEFAULT,                  //initial y position    
		CW_USEDEFAULT,                  //initial x size    
		CW_USEDEFAULT,                  //initial y size    
		NULL,                           //parent window handle    
		NULL,                           //window menu handle    
		hInstance,                      //program instance handle    
		NULL);                          //creation parameters    

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.    

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc    
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BOOL		bValidFile;
	static BYTE		buffer[MAXREAD];
	static HWND		hwndList, hwndText;
	static RECT		rect;
	static TCHAR	szFile[MAX_PATH + 1];
	HANDLE			hFile;
	HDC				hdc;
	int				i, cxChar, cyChar;
	PAINTSTRUCT		ps;
	TCHAR			szBuffer[MAX_PATH + 1];

	switch (message) //get the message    
	{
	case WM_CREATE:
		cxChar = LOWORD(GetDialogBaseUnits());
		cyChar = HIWORD(GetDialogBaseUnits());

		rect.left = 20 * cxChar;
		rect.top = 3 * cyChar;

		//Create listbox and static text windows.

		hwndList = CreateWindow(TEXT("listbox"), NULL,
			WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,
			cxChar, cyChar * 3,
			cxChar * 13 + GetSystemMetrics(SM_CXVSCROLL),
			cyChar * 10,
			hwnd, (HMENU)ID_LIST,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
			NULL);

		GetCurrentDirectory(MAX_PATH + 1, szBuffer);

		hwndText = CreateWindow(TEXT("static"), szBuffer,
			WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,
			cxChar, cyChar,
			cxChar * MAX_PATH,
			cyChar,
			hwnd, (HMENU)ID_TEXT,
			(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
			NULL);
		
		OldList = (WNDPROC)SetWindowLong(hwndList, GWL_WNDPROC, (LPARAM)ListProc);

		SendMessage(hwndList, LB_DIR, DIRATTR, (LPARAM)TEXT("*.*"));		
		return 0;

	case WM_SIZE:
		rect.right	= LOWORD(lParam);
		rect.bottom = HIWORD(lParam);
		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndList);
		return 0;

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_LIST && HIWORD(wParam) == LBN_DBLCLK)
		{
			i = SendMessage(hwndList, LB_GETCURSEL, 0, 0);
			if (i == LB_ERR)
				break;
			SendMessage(hwndList, LB_GETTEXT, i, (LPARAM)szBuffer);

			if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szBuffer,
				GENERIC_READ, FILE_SHARE_READ, NULL,
				OPEN_EXISTING, 0, NULL)))
			{
				CloseHandle(hFile);
				bValidFile = TRUE;
				lstrcpy(szFile, szBuffer);
				GetCurrentDirectory(MAX_PATH + 1, szBuffer);

				if (szBuffer[lstrlen(szBuffer) - 1] != TEXT('\\'))
					lstrcat(szBuffer, TEXT("\\"));
				SetWindowText(hwndText, lstrcat(szBuffer, szFile));
			}
			else
			{
				bValidFile = FALSE;
				szBuffer[lstrlen(szBuffer) - 1] = TEXT('\0');

				//if setting the directory doesn't work, maybe it's
				//a drive change, so try that.

				if (!SetCurrentDirectory(szBuffer + 1))
				{
					szBuffer[3] = TEXT(':');
					szBuffer[4] = TEXT('\0');
					SetCurrentDirectory(szBuffer + 2);
				}

				//Get the new directory name and fill the list box.
				GetCurrentDirectory(MAX_PATH + 1, szBuffer);
				SetWindowText(hwndText, szBuffer);
				SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
				SendMessage(hwndList, LB_DIR, DIRATTR,
					(LPARAM)TEXT("*.*"));
			}
			InvalidateRect(hwnd, NULL, TRUE);
		}
		return 0;

	case WM_PAINT:
		if (!bValidFile)
			break;

		if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szFile,
			GENERIC_READ, FILE_SHARE_READ, NULL,
			OPEN_EXISTING, 0, NULL)))
		{
			bValidFile = FALSE;
		}

		ReadFile(hFile, buffer, MAXREAD, (LPDWORD)&i, NULL);
		CloseHandle(hFile);

		//i now equals the number of bytes in buffer.
		//Commence getting a device context for displaying text.

		hdc = BeginPaint(hwnd, &ps);
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
		SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));

		//Assume the file is ASCII

		DrawTextA(hdc, (LPCSTR)buffer, i, &rect, DTFLAGS);

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:

		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK ListProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if(message == WM_KEYDOWN && wParam == VK_RETURN)
		SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(1, LBN_DBLCLK), (LPARAM)hwnd);

	return CallWindowProc(OldList, hwnd, message, wParam, lParam);
}

技术分享

《Windows程序设计》读书笔九 子窗口控件

标签:

原文地址:http://blog.csdn.net/sesiria/article/details/51817740

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!