WM_PAINT中应该用BeginPaint与EndPaint这两个api,它们的功能正是使无效区域恢复(所以WM_PAINT里即使什么都不做,也必须写上BeginPaint与EndPaint)——Delphi里WM_PAINT消息的三个走向都做到了这一点
2016-02-05 20:21
686 查看
程序本来是想实现鼠标单击改变背景颜色。可是,程序运行时,为什么没有任何消息触发,背景颜色就一直不断的改变了?WM_PAINT怎么被触发的
回答:这个基础,看一下《windows程序设计》第三章吧
wm_paint是有无效区域的时候产生的消息,所以应首先恢复该区域,才不会一直循环下去
wm_paint中应该用beginpaint与endpaint这两个api,参数是PaintStruct类型,它们的功能正是使无效区域恢复
而不是getDC与releaseDC
invalidaterect也有使区域无效的功能,可用它手动触发wm_paint事件重绘
http://zhidao.baidu.com/question/112410452.html
--------------------------------------------------------------------------------------
对比Delphi代码:
就是说,TWinControl无论是否双缓冲,都会调用BeginPaint和EndPaint函数,来消除自绘。整个Controls.pas单元也只有这两处调用BeginPaint。
另外,我没有找到TWinControl的DoubleBuffered被初始化的过程,说明编译器自动把它初始化成了False
#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
相关文章推荐
- Delphi和C++数据类型对照表
- Delphi中如何获得光标
- 利用Delphi编写Socket通信程序
- (delphi)DbgridEh排序
- ListView 百分比进度条(delphi版)
- Delphi的RTTI还分为对类和对象的判断,以及对普通属性的判断——相比之下,C++的RTTI实在太弱!
- 终于懂了:TWinControl主要是Delphi官方用来封装Windows的官方控件,开发者还是应该是有TCustomControl来开发三方控件
- C++能在三个地方创造对象,而Delphi只有一个地方
- delphi 怎么获取工程版本号
- Delphi 10 Seattle不支持intel atom?
- 不是什么时候都可以用栈来声明对象并使用(自动释放)——Delphi里到处都是编译器魔法,并且自动帮助实例化界面元素指针
- Delphi XE7下如何创建一个Android模拟器调试
- DelphiXE Android的所有权限按照分类总结说明
- DelphiXE8怎么使用调试模式(朱建强)
- delphi使用ado连接Excel
- 与java兼容的delphi xe实现des算法单元
- DelphiXE8怎么使用调试模式
- java delphi aes 加密与解密文件兼容算法
- DelphiXE8新建AVD
- Android问题-DelphiXE8新建AVD出现“no system images installed for this target”