程序本来是想实现鼠标单击改变背景颜色。可是,程序运行时,为什么没有任何消息触发,背景颜色就一直不断的改变了?WM_PAINT怎么被触发的
#include <windows.h> #include <stdlib.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; void DrawRectangle (HWND) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("SetBackgroundColor") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("注册失败!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("SetBackgroundColor"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static RECT r; HDC hdc; HBRUSH hBrush; switch (iMsg) { case WM_PAINT: hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); hdc=GetDC(hwnd); FillRect (hdc, &r, hBrush) ; ReleaseDC (hwnd, hdc) ; DeleteObject (hBrush) ; return 0; case WM_SIZE: GetClientRect(hwnd,&r); return 0; case WM_LBUTTONDOWN: InvalidateRect(hwnd,&r,true); return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; }
回答:这个基础,看一下《windows程序设计》第三章吧
wm_paint是有无效区域的时候产生的消息,所以应首先恢复该区域,才不会一直循环下去
wm_paint中应该用beginpaint与endpaint这两个api,参数是PaintStruct类型,它们的功能正是使无效区域恢复
而不是getDC与releaseDC
invalidaterect也有使区域无效的功能,可用它手动触发wm_paint事件重绘
http://zhidao.baidu.com/question/112410452.html
--------------------------------------------------------------------------------------
对比Delphi代码:
procedure TWinControl.WMPaint(var Message: TWMPaint); var DC, MemDC: HDC; MemBitmap, OldBitmap: HBITMAP; PS: TPaintStruct; begin if not FDoubleBuffered or (Message.DC <> 0) then // 消息里已经自带DC,用这个绘制就可以了 begin if not (csCustomPaint in ControlState) and (ControlCount = 0) then // 针对Windows自带控件,其包含的子控件数量为0,且没有自绘标记。 inherited // 走向1,主要是针对Windows系统原生控件,微软定义的基本控件,总不会有错,应该内含BeginPaint了吧? else PaintHandler(Message); // 走向2,主要是针对Delphi的自绘控件,内含BeginPaint end else begin DC := GetDC(0); // 走向3,消息里没有自带DC,那么就现取,内含BeginPaint MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom); ReleaseDC(0, DC); MemDC := CreateCompatibleDC(0); OldBitmap := SelectObject(MemDC, MemBitmap); try DC := BeginPaint(Handle, PS); Perform(WM_ERASEBKGND, MemDC, MemDC); Message.DC := MemDC; WMPaint(Message); Message.DC := 0; BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY); EndPaint(Handle, PS); finally SelectObject(MemDC, OldBitmap); DeleteDC(MemDC); DeleteObject(MemBitmap); end; end; end;
procedure TWinControl.PaintHandler(var Message: TWMPaint); var I, Clip, SaveIndex: Integer; DC: HDC; PS: TPaintStruct; begin DC := Message.DC; if DC = 0 then DC := BeginPaint(Handle, PS); try if FControls = nil then PaintWindow(DC) else begin SaveIndex := SaveDC(DC); Clip := SimpleRegion; for I := 0 to FControls.Count - 1 do with TControl(FControls[I]) do if (Visible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and (csOpaque in ControlStyle) then begin Clip := ExcludeClipRect(DC, Left, Top, Left + Width, Top + Height); if Clip = NullRegion then Break; end; if Clip <> NullRegion then PaintWindow(DC); RestoreDC(DC, SaveIndex); end; PaintControls(DC, nil); finally if Message.DC = 0 then EndPaint(Handle, PS); end; end;
就是说,TWinControl无论是否双缓冲,都会调用BeginPaint和EndPaint函数,来消除自绘。整个Controls.pas单元也只有这两处调用BeginPaint。
另外,我没有找到TWinControl的DoubleBuffered被初始化的过程,说明编译器自动把它初始化成了False