創建窗口步驟:
(1)注冊窗口類(RegisterClassEx)
(2)創建窗口(CreateWindowEx)
(3)在桌面顯示窗口(ShowWindow)
(4)更新窗口客戶區(UpdateWindow)
(5)進入無限的消息獲取和處理的循環:獲取消息(GetMessage);分派消息至窗口函數處理(DisPatchMessage);
如果是WM_QUIT,函數(GetMessage)返回False,消息循環結束,程序退出。
注冊窗口類需要初始化一個窗口類結構,將其寫成一個函數如下:
ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; //窗口類結構 wcex.cbSize = sizeof(WNDCLASSEX); //結構大小 wcex.style = CS_HREDRAW | CS_VREDRAW|CS_OWNDC;//CS:class style此窗口類派生的窗口具有的風格 //CS_OWNDC使得windows將每次對DC的設置保存下來,CS_HREDRAW | CS_VREDRAW窗口大小變化就刷新 wcex.lpfnWndProc = WndProc; //窗口函數指針 wcex.cbClsExtra = 0; //緊跟在窗口類結構后的附加字節數 wcex.cbWndExtra = 0; //緊跟在窗口事例后的附加字節數 wcex.hInstance = hInstance;//本模塊的實例句柄 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLOCKWINDOW));//窗口左上角圖標句柄 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);//光標句柄 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW); //背景畫刷句柄 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLOCKWINDOW);//菜單名 //wcex.lpszMenuName =NULL; //無菜單 wcex.lpszClassName = szWindowClass; //此窗口類名 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//小圖標句柄 return RegisterClassExW(&wcex); //注冊此窗口類 }
創建窗口、顯示窗口、刷新客戶區寫成一個函數如下:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 將實例句柄存儲在全局變量中 HWND hWnd = CreateWindowW(szWindowClass, //窗口類名 szTitle, //窗口標題 WS_OVERLAPPEDWINDOW, //窗口風格 WS:window style CW_USEDEFAULT, //初始位置X坐標 CW_USEDEFAULT, //初始位置Y坐標 500, //寬度 500, //高度 nullptr, //父窗口句柄 nullptr, //菜單句柄 hInstance, //程序實例句柄 nullptr); //用戶數據 if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); //顯示窗口 UpdateWindow(hWnd); //刷新客戶區 return TRUE; }
因此主函數可以寫為:
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // 初始化全局字符串 LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_CLOCKWINDOW, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 執行應用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLOCKWINDOW)); MSG msg; // 主消息循環: while (GetMessage(&msg, nullptr, 0, 0))//GetMessage返回False程序結束 { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; //程序結束時返回最后一個MSG的wParam。 }
在窗口作圖
測試一:先打開notepad,之后運行此代碼
HWND hWnd; HDC hDC; hWnd = ::FindWindow(_TEXT("Notepad"), NULL); hDC = ::GetDC(hWnd); while (::IsWindow(hWnd)) { ::SetTextColor(hDC, RGB(255, 0, 0)); ::SetBkColor(hDC, RGB(0, 0, 0)); ::TextOut(hDC, 10, 10, _T("this is a test."), sizeof("this is a test.") - 2); HPEN hPen = CreatePen(NULL,4,RGB(255,255,255)); ::SelectObject(hDC, hPen); ::SelectObject(hDC, ::GetStockObject(BLACK_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); ::SelectObject(hDC, ::GetStockObject(WHITE_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); ::SelectObject(hDC, ::GetStockObject(LTGRAY_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); ::SelectObject(hDC, ::GetStockObject(GRAY_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); ::SelectObject(hDC, ::GetStockObject(DKGRAY_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); ::SelectObject(hDC, ::GetStockObject(DKGRAY_BRUSH)); Ellipse(hDC, 0, 0, 500, 500); Sleep(1000); } ReleaseDC(hWnd, hDC);
繪圖首先要取得設備環境句柄
PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); //取得(窗口客戶區無效區域)設備環境句柄 //繪圖代碼 //EndPaint(hWnd, &ps); HDC hdcW = ::GetWindowDC(hWnd); //取得整個窗口設備環境句柄 HDC hdcD = ::GetDC(hWnd); //取得窗口客戶區設備環境句柄(函數可用在任何地方)
接着有時為了作圖方便需要修改默認坐標系
void SetIsotropic(HWND hWnd,HDC hdc) { RECT rect; GetClientRect(hWnd, &rect); //取得客戶區大小 ::SetMapMode(hdc, MM_ANISOTROPIC); //自定義坐標系MM_ANISOTROPIC:X!=Y,MM_ISOTROPIC:X=Y ::SetWindowExtEx(hdc, 500, 500, NULL); //邏輯刻度,第四個參數可用於返回原來的大小 ::SetViewportExtEx(hdc, rect.right, -rect.bottom, NULL); //像素刻度(坐標方向),第四個參數可用於返回原來的大小 ::SetViewportOrgEx(hdc, rect.right / 2, rect.bottom / 2, NULL); //修改坐標系(參照之前坐標系的位置坐標),第四個參數可用於返回原來的原點 }
修改完坐標系后開始繪制時鍾表面
void paintClock(HWND hWnd,HDC hdc) { HPEN hPenTri, hPenTime, hPenCent; HBRUSH hBrushTr, hBrushTime, hBrushCent; int colorUseR = (rand() % (255 - 0 + 1)) + 0; //(rand() % (b-a+1))+ a; a~b隨機數 int colorUseG = (rand() % (255 - 0 + 1)) + 0; int colorUseB = (rand() % (255 - 0 + 1)) + 0; hPenTri = CreatePen(NULL, 4, RGB(255, colorUseG, 240)); hBrushTr = ::CreateSolidBrush(RGB(255, colorUseG, 240)); bankColor = RGB(255, colorUseG, 240); hPenTime = CreatePen(NULL, 4, RGB(colorUseR, 240, 240)); hBrushTime = ::CreateSolidBrush(RGB(colorUseR, 240, 240)); hPenCent = ::CreatePen(NULL, 4, RGB(240, 240, colorUseB)); hBrushCent = ::CreateSolidBrush(RGB(240, 240, colorUseB)); SetIsotropic(hWnd,hdc); ///三角形//////////////////////////////////////////////////////////////////////////////////// //SetDCBrushColor(hdc, RGB(255, 255, 255));//(無效) ::SelectObject(hdc, hPenTri); ::SelectObject(hdc, hBrushTr); //POINT Mypoint[12] = { (250,10),(375,33.5),(466.5,125),(490,250),(466.5,375),(375,466.5),(250,490),(125,466.5),(33.5,375),(10,250),(33.5,125),(125,33.5) };//寫法錯誤 POINT Mypoint[12] = { 0,240,120,207.84,207.84,120,240,0,207.84,-120,120,-207.84,0,-240,-120,-207.84,-207.84,-120,-240,0,-207.84,120,-120,207.84 }; //::ClientToScreen(hWnd, point); //投射到屏幕坐標,返回至point POINT Mypoint1[3] = { Mypoint[0],Mypoint[4],Mypoint[8] }; POINT Mypoint2[3] = { Mypoint[1],Mypoint[5],Mypoint[9] }; POINT Mypoint3[3] = { Mypoint[2],Mypoint[6],Mypoint[10] }; POINT Mypoint4[3] = { Mypoint[3],Mypoint[7],Mypoint[11] }; //POINT Mypoint5[4]={ Mypoint1 ,Mypoint2,Mypoint3,Mypoint4}//不好用for循環簡化操作,二維數組用不了?? Polygon(hdc, Mypoint1, 3); Polygon(hdc, Mypoint2, 3); Polygon(hdc, Mypoint3, 3); Polygon(hdc, Mypoint4, 3); //圓形=============================================================== ::SelectObject(hdc, hPenTime); ::SelectObject(hdc, hBrushTime);//圖形填充色 for (int i = 0; i < 12; i++) { Ellipse(hdc, Mypoint[i].x - 10, Mypoint[i].y - 10, Mypoint[i].x + 10, Mypoint[i].y + 10); } //中心點加文本======================================================= ::SelectObject(hdc, hPenCent); ::SelectObject(hdc, hBrushCent); ::Ellipse(hdc, -5, 5, 5, -5); ::SetTextColor(hdc, RGB(0, 0, 0)); //文字顏色 //::SetBkColor(hdc, TRANSPARENT); //文字背景色,設置為透明無效 ::SetBkMode(hdc, TRANSPARENT); //文字背景透明 CString strText = _T("時鍾"); TextOut(hdc, -240, 240, (LPCWSTR)strText, sizeof(strText) - 2); }
繪制時鍾的指針函數
void DrawHand(HDC hdc, int nLength, int nWidth, int nDegrees, COLORREF clrColor) { double nRadians = (double)nDegrees * 2 * 3.1415926 / 360; POINT pt[2]; pt[0].x = (int)(nLength*sin(nRadians)); pt[0].y = (int)(nLength*cos(nRadians)); pt[1].x = pt[0].x /-5; pt[1].y = pt[0].y /-5; HPEN hPen = ::CreatePen(PS_SOLID, nWidth, clrColor); HPEN oldPen = (HPEN)::SelectObject(hdc, hPen); ::MoveToEx(hdc, pt[0].x, pt[0].y, NULL); LineTo(hdc, pt[1].x, pt[1].y); ::SelectObject(hdc, oldPen); ::DeleteObject(hPen); }
在窗口函數WM_CREATE下設置定時器
case WM_CREATE: //當一個應用程序通過CreateWindowEx函數或者CreateWindow函數請求創建窗口時發送此消息,(此消息在函數返回之前發送)。 SYSTEMTIME time; ::GetLocalTime(&time); s_PreHour = time.wHour%12;//取余 s_PreMinute = time.wMinute; s_PreSecond = time.wSecond; SetTimer(hWnd, //窗口句柄 IDT_CLOCK,//TimerID 1000, //間隔 NULL); //計時器函數指針 return 0; //它應當返回0以使得窗口的創建過程得以繼續。如果對於這個消息程序返回-1,窗口將會被銷毀。 //並且CreateWindowEx或者CreateWindow函數將會返回一個值為NULL的句柄。
在WM_TIMER下控制指針的變化與重畫
case WM_TIMER: paintClock(hWnd, hDC); //paintClock(hWnd, hDC);重畫了時鍾面板,使得覆蓋了舊的時鍾指針 if (::IsIconic(hWnd)) //如果窗口是最小化 { return 0; } ::GetLocalTime(&time); //HDC hDC = ::GetDC(hWnd);//(無法寫在這里) SetIsotropic(hWnd, hDC);//建立坐標系 if (time.wMinute != s_PreMinute) { //DrawHand(hDC, 140, 8, s_PreHour * 30 + s_PreMinute / 2, bankColor);//使用時鍾面板顏色覆蓋原指針 //DrawHand(hDC, 170, 6, s_PreMinute * 6, bankColor); //覆蓋失敗(不能使得指針顏色完全消失,如同在相同的兩點好幾條畫好幾條線,看起來線條變粗並不像一條直線) s_PreHour = time.wHour; s_PreMinute = time.wMinute; } if (time.wSecond != s_PreSecond) { //DrawHand(hDC, 170, 1, s_PreSecond * 6, bankColor);//使用時鍾面板顏色覆蓋原指針 DrawHand(hDC, 80, 6, s_PreHour * 30 + s_PreMinute / 2, RGB(0,0,0)); DrawHand(hDC, 120, 4, s_PreMinute * 6, RGB(0, 0, 0)); DrawHand(hDC, 170, 2, s_PreSecond * 6, RGB(0, 0, 0)); s_PreSecond = time.wSecond; } //::MessageBeep(MB_ICONASTERISK);//發出“嘟嘟”聲 break;
效果圖

