zoukankan      html  css  js  c++  java
  • 终于懂了:WM_PAINT中应该用BeginPaint与EndPaint这两个api,它们的功能正是使无效区域恢复(所以WM_PAINT里即使什么都不做,也必须写上BeginPaint与EndPaint)——Delphi里WM_PAINT消息的三个走向都做到了这一点 good

    程序本来是想实现鼠标单击改变背景颜色。可是,程序运行时,为什么没有任何消息触发,背景颜色就一直不断的改变了?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

  • 相关阅读:
    ios NSString format 保留小数点 float double
    IOS中延时执行的几种方式的比较和汇总
    ioss使用xcode常用快捷键
    iphone 6plus 下app里的状态栏和界面会被放大的问题//以及设置APP闪屏页/APP图标流程
    iostbleView刷新后显示指定cell
    iOS-打包成ipa的4种方法
    iosttableViewCell右侧的箭头,圆形等
    Linux学习之CentOS(二十)------vi/vim 按键说明
    gzip
    bzip2
  • 原文地址:https://www.cnblogs.com/findumars/p/5183564.html
Copyright © 2011-2022 走看看