문제

나는 많은 양식을 가진 다소 큰 응용 프로그램에서 캡션 바를 맞춤화하고 WM_NCPAINT를 처리 할 때 자신을 직접 처리하고 도면을 취급하여 구식 방식을 시도하기로 결정했습니다.

그림 자체가 매우 잘되고 본질적으로 작동합니다. 그렇게 작동하지 않는 한 가지는 Tmainmenu를 그리는 것입니다. 기본 WM_NCACTIVATE 핸들러가 그 그림을 그리기 전에 전체 비영리 영역을 그릴 (WM_NCPAINT 핸들러 내에서 WM_NCACTIVATE 메시지를 수행해야합니다) 겉으로보기에는 깜박 거림을 일으킬 수 없습니다. 또는 WM_NCPAINT 페인트의 기본 처리기를 TMAINMENU가 포함 된 RECT만이 매끄러운 결과를 가져 오지만 메뉴를 다시 칠하지 않습니다.

내 질문은 다음과 같습니다.

  • wm_ncpaint 나 자신을 취급 할 때 Tmainmenu와 Tmainmenu 만 다시 칠할 수 있습니까?

나는 그것을 몇 가지 간다고 나는 올바른 방법이라고 생각하지만 벽을 때리는 것 같습니다. 나는 내가하는 일에 대해 충분한 지식이 없으며 그것에 대해 명확한 문서를 찾을 수없는 것 같습니다. 내 코드의 가장 중요한 부분은 다음과 같습니다.

RedrawWindow(Handle, nil, MenuRegion, RDW_INVALIDATE or RDW_FRAME or RDW_NOERASE or RDW_ALLCHILDREN or RDW_UPDATENOW);
.

와 나는 그것이 정확히 무엇이 잘못되었는지는 그것이 정확히 생각한다고 생각합니다. (또는 오히려 'menuregion'을 계산하는 코드). 잘못된 좌표계를 사용하고 있기 때문에 잘못되었거나 이이 전적으로 잘못된 방향으로 가기 때문에 잘못 되었다면 단서가 없습니다.

여기에 델파이 (xe3)에서 '그대로 컴파일하고 실행할 수있는 버전의 버전이 줄어 듭니다.

unit Unit3;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus;
type
  TForm3 = class(TForm)
  private
    FDrawMenu: Boolean;
    function CalcFrameRegion: HRGN;
    function CalcMenuRegion: HRGN;
    procedure DrawMenu;
    procedure FormFrame(minimal: Boolean = false);
    procedure WMNCActivate(var message: TWMNCActivate); message WM_NCACTIVATE;
    procedure WMNCPaint(var message: TMessage); message WM_NCPAINT;
    procedure WMSIZE(var message : TWMSIZE); message WM_SIZE;
    constructor Create(AOwner: TComponent); override;
  public
    { Public declarations }
  end;
var
  Form3: TForm3;
implementation
{$R *.dfm}
{ TForm3 }
function TForm3.CalcFrameRegion: HRGN;
var
  YCaption, YFrame, XFrame: Integer;
begin
  YCaption := GetSystemMetrics(SM_CYCaption);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  XFrame := GetSystemMetrics(SM_CXFRAME);
  Result :=  CreateRectRgn(0, 0, YCaption + YFrame, Width);
  Result := Result +  CreateRectRgn(0, 0, Height, XFrame);
  Result := Result + CreateRectRgn(0, Height - YFrame, Width, Height);
  Result := Result + CreateRectRgn(Width - XFrame, 0, Width, Height);
end;
function TForm3.CalcMenuRegion: HRGN;
var
  XFrame, YFrame, YCaption, YMenu: Integer;
begin
  XFrame := GetSystemMetrics(SM_CXFRAME);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  YCaption := GetSystemMetrics(SM_CYCAPTION);
  YMenu := GetSystemMetrics(SM_CYMENU);
  Result := CreateRectRgn(XFrame, YFrame + YCaption, Width - XFrame, YFrame + YCaption + YMenu);
end;
constructor TForm3.Create(AOwner: TComponent);
var
  testItem: TMenuItem;
begin
  inherited;
  // Creating a MainMenu and attatching it to the form.
  Menu := TMainMenu.Create(self);
  // The menu need san item.
  testItem := TMenuItem.Create(Menu);
  testItem.Caption := 'test';
  Menu.Items.Add(testItem);
  FDrawMenu := false;
end;
procedure TForm3.FormFrame(minimal: Boolean);
var
  YCaption, YFrame, XFrame: Integer;
begin
  YCaption := GetSystemMetrics(SM_CYCaption);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  XFrame := GetSystemMetrics(SM_CXFRAME);
  Canvas.Handle := GetWindowDC(Handle);
  Canvas.Pen.Style := psClear;
  Canvas.Brush.Style := bsSolid;
  Canvas.Brush.Color := clRed;
  if not minimal then begin
    Canvas.Rectangle(0, 0, Width + 1, YCaption + YFRame + 1);
    Canvas.Rectangle(0, YCaption + YFRame, XFrame + 1, Height + 1);
    Canvas.Rectangle(XFrame, Height - YFrame, Width + 1, Height + 1);
    Canvas.Rectangle(Width - XFrame, YCaption + YFRame, Width + 1, Height - YFrame + 1);
  end;
end;
procedure TForm3.DrawMenu;
var
  MenuRegion: HRGN;
begin
  if Assigned(Menu) then begin
    MenuRegion := CalcMenuRegion;
    FDrawMenu := true; // Make sure the inherited handler gets called.
    // Force a redraw of the region defined by MenuRegion.
    RedrawWindow(Handle, nil, MenuRegion,
                  RDW_INVALIDATE or RDW_FRAME or RDW_NOERASE or RDW_ALLCHILDREN or RDW_UPDATENOW);
    FDrawMenu := false; // Use the FormFrame function again.
  end;
end;
procedure TForm3.WMNCActivate(var message: TWMNCActivate);
begin
  FormFrame;
  message.Result := 1; // This makes sure the message gets handled properly.
end;
procedure TForm3.WMNCPaint(var message: TMessage);
begin
  if FDrawMenu then
    inherited // Gets called when the Menu has to be drawn.
  else
    FormFrame; // Gets called in all other cases.
end;
procedure TForm3.WMSIZE(var message: TWMSIZE);
begin
  inherited;
  DrawMenu;
end;
end.
.

도움이 되었습니까?

해결책

"나는 Tmainmenu를 어떻게 가질 수 있고, 내가 wm_ncpaint 나 자신을 취급 할 때 칠할 수있는 Tmainmenu 만 다시 칠할 수 있습니까?"

이론에서는 WM_NCPAINT 영역을 수정할 수 있습니다. 메뉴 모음의 영역에 해당하는 영역을 정의하고 나머지를 그대로 두십시오. 아래는 개념의 증거입니다 :

procedure TForm3.WMNCPaint(var Message: TWMNCPaint);
var
  R: TRect;
  MenubarInfo: TMenuBarInfo;
  MenuRgn: HRGN;
begin
  FormFrame( whatever );

  MenubarInfo.cbSize := SizeOf(MenubarInfo);
  GetMenuBarInfo(Handle, OBJID_MENU, 0, MenubarInfo);

  MenuRgn := CreateRectRgnIndirect(MenubarInfo.rcBar);
  if Message.RGN <> 1 then
    DeleteObject(Message.RGN);
  Message.RGN := MenuRgn;

  inherited;
end;
.



실제로 궁극적 으로이 경로를 포기할 것입니다. 위의 샘플에서 '1'을 테스트하십시오. 업데이트 영역을위한 의사 핸들은 설명서에서 언급되지 않습니다 그러나 지역 핸들은 항상 일반 창에 대해 '1'입니다. 사실 비 클라이언트 취급이 결코 제대로 문서화 된 적이 없었습니다. 내 추측은 OS 자체가 규칙에 의해 재생되지 않기 때문입니다. 예를 들어, WM_NCACTIVATE의 기본 처리 중에 NC 영역이 페인트되는 사실을 인체화하십시오. 정품 인증은 NC 회화와 관련이 있습니다. OS가 왜 뒤에 있는지 물린 이유는 무엇입니까?

제 제안은 VCL 스타일이 취한 경로를 취합니다. 일단 NC 영역을 그리면 모두 그를 그립니다. 메뉴 막대 영역은 비 클라이언트 영역의 일부입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top