Frage

Ich male meine Beschriftungsleiste individuell in einer ziemlich großen Anwendung mit vielen Formularen und habe beschlossen, es auf die altmodische Art und Weise zu versuchen, indem ich einige Nachrichten selbst verarbeite und die Zeichnung bei der Bearbeitung von WM_NCPAINT überarbeite.

Das Malen selbst läuft ganz gut und funktioniert im Wesentlichen.Eine Sache, die jedoch nicht funktionieren wird, ist das Malen des TMainMenu.Entweder lasse ich den standardmäßigen WM_NCACTIVATE-Handler den gesamten Nicht-Client-Bereich zeichnen (ich muss eine WM_NCACTIVATE-Nachricht in meinem WM_NCPAINT-Handler ausführen), bevor ich ihn übermale, was zu einem Flackern führt, das scheinbar nicht behoben werden kann.Oder ich kann versuchen, dass der Standardhandler von WM_NCPAINT nur das Rect malt, das das TMainMenu enthält, was zu einem reibungslosen Ergebnis führt, das Menü jedoch nicht neu zeichnet.

Meine Frage ist:

  • Wie kann ich ein TMainMenu und nur ein TMainmenu neu zeichnen lassen, wenn ich WM_NCPAINT selbst verarbeite?

Ich habe es ein paar Mal versucht und denke, dass ich auf dem richtigen Weg bin, aber ich stoße an eine Wand;Ich habe nicht genug Wissen darüber, was ich tue, und kann anscheinend keine klare Dokumentation darüber finden.Der wichtigste Teil meines Codes ist:

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

Und ich denke, genau das geht schief.(oder besser gesagt, der Code, in dem ich „MenuRegion“ berechne.Ich habe einfach keine Ahnung, ob es schiefgeht, weil ich das falsche Koordinatensystem verwende, oder ob es daran liegt, dass ich es völlig falsch angehe.

Hier ist eine reduzierte Version meines Codes, der in Delphi (xe3) kompiliert und ausgeführt wird, wie er ist:

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.
War es hilfreich?

Lösung

„Wie kann ich ein TMainMenu und nur ein TMainmenu neu zeichnen lassen, wenn ich WM_NCPAINT selbst verarbeite?“

Theoretisch können Sie die Region ändern WM_NCPAINT ist bestanden.Definieren Sie einen Bereich, der dem Bereich der Menüleiste entspricht, lassen Sie den Rest weg.Das Folgende ist ein Proof of Concept:

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;


In der Praxis werden Sie diesen Weg letztendlich aufgeben.Beachten Sie den Test für „1“ im obigen Beispiel.Ein Pseudo-Handle für die Update-Region wird im nicht erwähnt Dokumentation.Dennoch ist das Regionshandle für ein normales Fenster immer „1“.Tatsächlich wurde der Umgang mit Nicht-Kunden nie ordnungsgemäß dokumentiert.Ich vermute, das liegt daran, dass sich das Betriebssystem selbst nicht an die Regeln hält.Nehmen Sie zum Beispiel die Tatsache, dass der NC-Bereich während der Standardbehandlung von gezeichnet wird WM_NCACTIVATE.Was hat die Aktivierung mit der NC-Lackierung zu tun?Warum malt das Betriebssystem hinter Ihrem Rücken?

Mein Vorschlag ist, den Weg zu gehen, den die VCL-Stile eingeschlagen haben.Sobald Sie sich im NC-Zeichnungsbereich befinden, zeichnen Sie alles.Der Menüleistenbereich ist Teil des Nicht-Client-Bereichs.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top