Question

Je peins ma barre de légende sur mesure dans une application assez volumineuse avec de nombreux formulaires et j'ai décidé d'essayer de le faire à l'ancienne en gérant moi-même certains messages et en effectuant le dessin lorsque je gère WM_NCPAINT.

La peinture elle-même se déroule plutôt bien et fonctionne pour l'essentiel.Une chose qui ne fonctionnera pas est de peindre le TMainMenu.Soit le gestionnaire WM_NCACTIVATE par défaut dessine toute la zone non client (je dois exécuter un message WM_NCACTIVATE dans mon gestionnaire WM_NCPAINT) avant de peindre dessus, ce qui provoque un scintillement qui ne peut apparemment pas être évité.Ou je peux essayer que le gestionnaire par défaut de WM_NCPAINT peigne uniquement le Rect contenant le TMainMenu, ce qui donne un résultat fluide, mais ne repeint pas le menu.

Ma question est:

  • Comment puis-je repeindre un TMainMenu et uniquement un TMainmenu lorsque je gère moi-même WM_NCPAINT ?

J'ai essayé quelques fois et je pense que je suis sur la bonne voie, mais je me heurte à un mur ;Je n'ai pas suffisamment de connaissances sur ce que je fais et je n'arrive pas à trouver une documentation claire à ce sujet.La partie la plus importante de mon code est :

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

Et je pense que c'est exactement ce qui ne va pas.(ou plutôt, le code où je calcule 'MenuRegion'.Je n'ai tout simplement aucune idée si ça ne va pas parce que j'utilise le mauvais système de coordonnées ou si c'est parce que je m'y prends complètement dans le mauvais sens.

Voici une version réduite de mon code qui sera compilé et exécuté « tel quel » dans Delphi (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.
Était-ce utile?

La solution

"Comment puis-je repeindre un TMainMenu et uniquement un TMainmenu lorsque je gère moi-même WM_NCPAINT ?"

En théorie vous pouvez modifier la région WM_NCPAINT est passé.Définissez une région qui correspond à la zone de la barre de menu, laissez de côté le reste.Ce qui suit est une preuve de 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;


En pratique, vous finirez par abandonner cette voie.Notez le test pour « 1 » dans l’exemple ci-dessus.Un pseudo handle pour la région de mise à jour n'est pas mentionné dans le Documentation.Pourtant, le handle de région est toujours « 1 » pour une fenêtre normale.En fait, la gestion des non-clients n’a jamais été correctement documentée.Je suppose que c'est parce que le système d'exploitation lui-même ne respecte pas les règles.Prenons par exemple le fait que la zone CN étant peinte lors de la gestion par défaut de WM_NCACTIVATE.Qu'est-ce que l'activation a à voir avec la peinture NC ?Pourquoi le système d'exploitation peint-il dans votre dos ?

Ma suggestion est de suivre la voie empruntée par les styles VCL.Une fois que vous êtes dans la zone de dessin NC, dessinez le tout.La zone de la barre de menus fait partie de la zone non-client.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top