WM_ERASEBKGND 공식 설명 및 Delphi의 모든 사용 상황(창 컨트롤의 배경색을 그리고 메시지 전달을 막는 것)

50128 단어
#define WM_ERASEBKGND                   0x0014
 
Parameters
wParam
A handle to the device context.//장치 컨텍스트 핸들
lParam
This parameter is not used.
Return value
Type: LRESULT
An application should return nonzero if it erases the background; otherwise, it should return zero.
배경을 지우면 함수가 0이 아닌 값을 되돌려줍니다.그렇지 않으면 함수가 0을 반환합니다.
Remarks
The DefWindowProc function erases the background by using the class background brush specified by the hbrBackgroundmember of the WNDCLASS structure. If hbrBackground is NULL, the application should process the WM_ERASEBKGNDmessage and erase the background.
DefWindowProc 함수는 WNDCLASS 구조에서 지정한 hbrBackground 배경 붓을 사용하여 배경을 지웁니다.만약 hbrBackground가 비어 있다면 프로그램은 WM 을 처리해야 합니다ERASEBKGND 메시지 배경을 지웁니다.
An application should return nonzero in response to WM_ERASEBKGND if it processes the message and erases the background; this indicates that no further erasing is required. If the application returns zero, the window will remain marked for erasing. (Typically, this indicates that the fErase member of the PAINTSTRUCT structure will be TRUE.)
응용 프로그램 응답 처리 WMERASEBKGND 메시지 및 배경을 지우고 0이 아닌 값을 반환해야 합니다.Windows에서 더 이상 지울 필요가 없음을 알려줍니다.애플리케이션이 0을 반환해도 Windows는 삭제로 표시됩니다.(일반적으로 이것은 PAINTSTRUCT 구조의 fErase가 진짜임을 나타낸다.)
번역:http://baike.baidu.com/view/9801207.htm
------------------------------------------------------------------------------------------------------
이 메시지는 Delphi에서 재정의됩니다.
  TWMEraseBkgnd = packed record
    Msg: Cardinal;
    DC: HDC;
    Unused: Longint;
    Result: Longint;
  end;

Controls에 있습니다.pas 단원의 관련 코드(1곳 정의, 2곳 사용):
procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  with ThemeServices do
  if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
    begin
      { Get the parent to draw its background into the control's background. }
      DrawParentBackground(Handle, Message.DC, nil, False);
    end
    else
    begin
      { Only erase background if we're not doublebuffering or painting to memory. }
      if not FDoubleBuffered or
         (TMessage(Message).wParam = TMessage(Message).lParam) then
        FillRect(Message.DC, ClientRect, FBrush.Handle);
    end;

  Message.Result := 1; //     ,WM_ERASEBKGND       ,             end;
procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited
    else
      PaintHandler(Message);
  end
  else
  begin
    DC := GetDC(0); //      DC,      
    MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom); //       ,             
    ReleaseDC(0, DC); //    
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      DC := BeginPaint(Handle, PS); //          DC
      Perform(WM_ERASEBKGND, MemDC, MemDC); //      TWinControl.WMEraseBkgnd,     ,        ,           ,      
      Message.DC := MemDC; //       , MemDC  ,          MemDC  ,     DC    
      WMPaint(Message); //     ,   DC<>0   ,           (            ),                 
      Message.DC := 0;  //        ,           ,        DC      
      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY); //  MemDC       DC 
      EndPaint(Handle, PS);
    finally
      SelectObject(MemDC, OldBitmap); //   
      DeleteDC(MemDC); //   
      DeleteObject(MemBitmap); //    end;
  end;
end;

또한 컨트롤을 다른 DC 장치에 인쇄할 수도 있습니다.
// Draws the windowed control to a device context.
procedure TWinControl.PaintTo(DC: HDC; X, Y: Integer);
var
  I, EdgeFlags, BorderFlags, SaveIndex: Integer;
  R: TRect;
begin
  Include(FControlState, csPaintCopy);
  SaveIndex := SaveDC(DC);
  MoveWindowOrg(DC, X, Y);
  IntersectClipRect(DC, 0, 0, Width, Height);
  BorderFlags := 0;
  EdgeFlags := 0;
  if GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_CLIENTEDGE <> 0 then
  begin
    EdgeFlags := EDGE_SUNKEN;
    BorderFlags := BF_RECT or BF_ADJUST
  end else
  if GetWindowLong(Handle, GWL_STYLE) and WS_BORDER <> 0 then
  begin
    EdgeFlags := BDR_OUTER;
    BorderFlags := BF_RECT or BF_ADJUST or BF_MONO;
  end;
  if BorderFlags <> 0 then
  begin
    SetRect(R, 0, 0, Width, Height);
    DrawEdge(DC, R, EdgeFlags, BorderFlags);
    MoveWindowOrg(DC, R.Left, R.Top);
    IntersectClipRect(DC, 0, 0, R.Right - R.Left, R.Bottom - R.Top);
  end;
  Perform(WM_ERASEBKGND, DC, 0);
  Perform(WM_PAINT, DC, 0);
  if FWinControls <> nil then
    for I := 0 to FWinControls.Count - 1 do
      with TWinControl(FWinControls[I]) do
        if Visible then PaintTo(DC, Left, Top);
  RestoreDC(DC, SaveIndex);
  Exclude(FControlState, csPaintCopy);
end;

------------------------------------------------------------------------------------------------------
Controls.pas에는 다음 정의가 있지만 Tspeed Button에서만 사용됩니다.
procedure PerformEraseBackground(Control: TControl; DC: HDC);
var
  LastOrigin: TPoint;
begin
  GetWindowOrgEx(DC, LastOrigin);
  SetWindowOrgEx(DC, LastOrigin.X + Control.Left, LastOrigin.Y + Control.Top, nil);
  Control.Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC)); // TSpeedButton    Windows  ,      
  SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
end;

procedure TSpeedButton.Paint;
const
  DownStyles: array[Boolean] of Integer = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
  FillStyles: array[Boolean] of Integer = (BF_MIDDLE, 0);
var
  PaintRect: TRect;
  DrawFlags: Integer;
  Offset: TPoint;
  Button: TThemedButton;
  ToolButton: TThemedToolBar;
  Details: TThemedElementDetails;
begin
  if not Enabled then
  begin
    FState := bsDisabled;
    FDragging := False;
  end
  else if FState = bsDisabled then
    if FDown and (GroupIndex <> 0) then
      FState := bsExclusive
    else
      FState := bsUp;
  Canvas.Font := Self.Font;

  if ThemeServices.ThemesEnabled then
  begin
    PerformEraseBackground(Self, Canvas.Handle);
    //     
  end;
end;

------------------------------------------------------------------------------------------------------
Forms.pas 단원의 정의 및 사용:
procedure TCustomForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if not IsIconic(Handle) then inherited else
  begin
    Message.Msg := WM_ICONERASEBKGND; //              ,     
    DefaultHandler(Message); //     MDI ,            ,         end;
end;

procedure TCustomForm.ClientWndProc(var Message: TMessage);

  procedure Default;
  begin
    with Message do
      Result := CallWindowProc(FDefClientProc, ClientHandle, Msg, wParam, lParam);
  end;

  function MaximizedChildren: Boolean;
  var
    I: Integer;
  begin
    for I := 0 to MDIChildCount - 1 do
      if MDIChildren[I].WindowState = wsMaximized then
      begin
        Result := True;
        Exit;
      end;
    Result := False;
  end;

var
  DC: HDC;
  PS: TPaintStruct;
  R: TRect;
begin
  with Message do
    case Msg of
      WM_NCHITTEST:
        begin
          Default;
          if Result = HTCLIENT then Result := HTTRANSPARENT;
        end;
      WM_ERASEBKGND: begin
          FillRect(TWMEraseBkGnd(Message).DC, ClientRect, Brush.Handle);
          { Erase the background at the location of an MDI client window }
          if (FormStyle = fsMDIForm) and (FClientHandle <> 0) then
          begin
            Windows.GetClientRect(FClientHandle, R);
            FillRect(TWMEraseBkGnd(Message).DC, R, Brush.Handle);
          end;
          Result := 1;
        end;
      $3F://!
        begin
          Default;
          if FFormStyle = fsMDIForm then
            ShowMDIClientEdge(FClientHandle, (MDIChildCount = 0) or
              not MaximizedChildren);
        end;
      WM_PAINT:
        begin
          DC := TWMPaint(Message).DC;
          if DC = 0 then
            TWMPaint(Message).DC := BeginPaint(ClientHandle, PS);
          try
            if DC = 0 then
            begin
              GetWindowRect(FClientHandle, R);
              R.TopLeft := ScreenToClient(R.TopLeft);
              MoveWindowOrg(TWMPaint(Message).DC, -R.Left, -R.Top);
            end;
            PaintHandler(TWMPaint(Message));
          finally
            if DC = 0 then
              EndPaint(ClientHandle, PS);
          end;
        end;
    else
      Default;
    end;
end;

TCustomForm에 대한 추가 설명WMeraseBkgnd에서 전달된 메시지:
procedure TCustomForm.DefaultHandler(var Message);
begin
  if ClientHandle <> 0 then
    with TMessage(Message) do
      if Msg = WM_SIZE then
        Result := DefWindowProc(Handle, Msg, wParam, lParam) else
        Result := DefFrameProc(Handle, ClientHandle, Msg, wParam, lParam)
  else
    inherited DefaultHandler(Message)
end;

//        message WM_ICONERASEBKGND
procedure TCustomForm.WMIconEraseBkgnd(var Message: TWMIconEraseBkgnd); 
begin
  if FormStyle = fsMDIChild then
  if (FormStyle = fsMDIChild) and not (csDesigning in ComponentState) then
    FillRect(Message.DC, ClientRect, Application.MainForm.Brush.Handle)
  else inherited;
end;

이 메시지에 대한 TApplication 사용:
procedure TApplication.WndProc(var Message: TMessage);
var
  procedure Default;
  begin
    with Message do
      Result := DefWindowProc(FHandle, Msg, WParam, LParam);
  end;

begin
  try
    Message.Result := 0;
    for I := 0 to FWindowHooks.Count - 1 do
      if TWindowHook(FWindowHooks[I]^)(Message) then Exit;
    CheckIniChange(Message);
    with Message do
      case Msg of
        WM_SYSCOMMAND:
          case WParam and $FFF0 of
            SC_MINIMIZE: Minimize;
            SC_RESTORE: Restore;
          else
            Default;
          end;
        WM_CLOSE:
          if MainForm <> nil then MainForm.Close;
        WM_PAINT:
          if IsIconic(FHandle) then DrawAppIcon else Default;
        WM_ERASEBKGND: //     ,     begin
            Message.Msg := WM_ICONERASEBKGND;
            Default;
          end;
        WM_NULL:
          CheckSynchronize;
      else
        Default;
      end;
  except
    HandleException(Self);
  end;
end;

------------------------------------------------------------------------------------------------------
StdCtrls 다시 봐.pas 단원의 정의 및 사용:
procedure TCustomComboBox.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if Style = csSimple then
  begin
    FillRect(Message.DC, ClientRect, Parent.Brush.Handle);
    Message.Result := 1;
  end
  else
    DefaultHandler(Message);
end;

procedure TButtonControl.WMEraseBkGnd(var Message: TWMEraseBkGnd);
begin
  { Under theme services the background is drawn in CN_CTLCOLORSTATIC. }
  if ThemeServices.ThemesEnabled then
    Message.Result := 1
  else
    inherited;
end;

procedure TButton.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if ThemeServices.ThemesEnabled then
    Message.Result := 1
  else
    DefaultHandler(Message);
end;

procedure TScrollBar.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  DefaultHandler(Message);
end;

------------------------------------------------------------------------------------------------------
그리고 Ext Ctrls.pas 단원의:
procedure TCustomControlBar.WMEraseBkgnd(var Message: TWmEraseBkgnd);
var
  R: TRect;
  I, J: Integer;
  Save: Boolean;
begin
  if Message.DC <> 0 then
    Canvas.Handle := Message.DC;
  if Picture.Graphic <> nil then
  begin

    try
      R := ClientRect;
      Save := FDrawing;
      FDrawing := True;
      try
        { Tile image across client area }
        for I := 0 to (R.Right - R.Left) div Picture.Width do
          for J := 0 to (R.Bottom - R.Top) div Picture.Height do
            Canvas.Draw(I * Picture.Width, J * Picture.Height, Picture.Graphic);
      finally
        FDrawing := Save;
      end
    finally
      if Message.DC <> 0 then
        Canvas.Handle := 0;
      Message.Result := 1;
    end;
  end
  else
  begin
    Canvas.Brush.Color := Color;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(ClientRect);
    inherited;
  end;
end;

------------------------------------------------------------------------------------------------------
그리고dbcgrids.pas 및 DbCtrls.pas 단원의:
procedure TDBCtrlPanel.WMEraseBkgnd(var Message: TMessage);
begin
  Message.Result := 1;
end;

procedure TDBCtrlGrid.WMEraseBkgnd(var Message: TMessage);
begin
  Message.Result := 1;
end;procedure TDBMemo.WMPaint(var Message: TWMPaint);
var
  S: string;
begin
  if not (csPaintCopy in ControlState) then inherited else
  begin
    if FDataLink.Field <> nil then
      if FDataLink.Field.IsBlob then
      begin
        if FAutoDisplay then
          S := AdjustLineBreaks(FDataLink.Field.AsString) else
          S := Format('(%s)', [FDataLink.Field.DisplayLabel]);
      end else
        S := FDataLink.Field.DisplayText;
    SendMessage(FPaintControl.Handle, WM_SETTEXT, 0, Integer(PChar(S)));
    SendMessage(FPaintControl.Handle, WM_ERASEBKGND, Message.DC, 0);
    SendMessage(FPaintControl.Handle, WM_PAINT, Message.DC, 0);
  end;
end;

------------------------------------------------------------------------------------------------------
그리고 섀도우 Wnd.pas 단원의:
procedure TShadowWindow.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  Message.Result := 0;
end;

------------------------------------------------------------------------------------------------------
그리고Tabs.pas 단원의:
procedure TTabSet.Paint;
var
  TabStart, LastTabPos: Integer;
  TabPos: TTabPos;
  Tab: Integer;
  Leading: TEdgeType;
  Trailing: TEdgeType;
  isFirst, isLast, isSelected, isPrevSelected: Boolean;
  R: TRect;
begin
  if not HandleAllocated then Exit;

  MemBitmap.Canvas.Font := Self.Canvas.Font;

  { draw background of tab area }
  with MemBitmap.Canvas do
  begin
    Brush.Bitmap := BrushBitmap;
    if ThemeServices.ThemesEnabled and ParentBackground then
      Perform(WM_ERASEBKGND, MemBitmap.Canvas.Handle, 0)
    else
      FillRect(Rect(0, 0, MemBitmap.Width, MemBitmap.Height));

  end;
end;

------------------------------------------------------------------------------------------------------
그리고 OleCtrls.pas 단원의:
procedure TOleControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if FMiscStatus and OLEMISC_INVISIBLEATRUNTIME = 0 then
    DefaultHandler(Message) else
    inherited;
end;

------------------------------------------------------------------------------------------------------
그리고ComCtrls.pas 단원의 5곳 정의와 2곳 사용:
procedure TPageControl.WMEraseBkGnd(var Message: TWMEraseBkGnd);
begin
  if (not ThemeServices.ThemesEnabled) or (not ParentBackground) then
    inherited
  else
    Message.Result := 1;
end;

procedure TCustomStatusBar.WMEraseBkGnd(var Message: TWMEraseBkGnd);
var
  Details: TThemedElementDetails;
begin
  if ThemeServices.ThemesEnabled then
  begin
    Details := ThemeServices.GetElementDetails(tsStatusRoot);
    ThemeServices.DrawElement(Message.DC, Details, ClientRect, nil);
    Message.Result := 1;
  end
  else
    inherited;
end;

procedure TTrackBar.WMEraseBkGnd(var Message: TWMEraseBkGnd);
var
  R: TRect;
begin
  if ThemeServices.ThemesEnabled then
  begin
    R := ClientRect;
    if Focused and ((Perform(WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS) = 0) then
      InflateRect(R, -1, -1);
    ThemeServices.DrawParentBackground(Handle, Message.DC, nil, False, @R);
    Message.Result := 1;
  end
  else
    inherited;
end;

procedure TToolBar.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if not Transparent then
    inherited else
    DefaultHandler(Message);
end;

procedure TCoolBar.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  if not ThemeServices.ThemesEnabled and (IsBackgroundDirty or (IsAutoSized and (Bands.Count = 0))) then
    inherited;
  DefaultHandler(Message);
end;

procedure TCustomListView.WndProc(var Message: TMessage);
begin
  if not (csDesigning in ComponentState) and ((Message.Msg = WM_LBUTTONDOWN) or
    (Message.Msg = WM_LBUTTONDBLCLK)) and not Dragging and (DragMode = dmAutomatic) then
  begin
    if not IsControlMouseMsg(TWMMouse(Message)) then
    begin
      ControlState := ControlState + [csLButtonDown];
      Dispatch(Message);
    end;
  end
  else if not (((Message.Msg = WM_PAINT) or (Message.Msg = WM_ERASEBKGND)) and
    Items.FNoRedraw) then
    inherited WndProc(Message);
end;

procedure TCoolBar.PaintWindow(DC: HDC);
begin
  Perform(WM_ERASEBKGND, DC, 0);
  inherited;
end;

총괄: 사실 하는 일이 많지 않다. 자류는 WM 을 일으킨다.ARASEBKGND 메시지는 부모 클래스에 주거나 메시지 값을 바꾸거나 부모 컨트롤러가 하는 일도 적고 가장 중요한 것은 컨트롤러에 배경색을 주거나 더 이상 전달하지 못하게 하는 것이다.
실험:
1. 창 위에 WM을 덮어씌운다ERASEBKGND 메시지의 컨트롤(예: TButton)
2. 덮어쓰지 않는 WM 을 넣는다ERASEBKGND 메시지의 공간, 예를 들어 Tpanel, 도대체 무슨 일이 일어날지 보자.
비고: 사실 이런 소식은 디버깅하기 어려워서 로그에 모두 기록할 수 밖에 없어요.

좋은 웹페이지 즐겨찾기