Pergunta

Eu estou pintura personalizada a minha barra de legenda um pouco em uma aplicação grande, com muitas formas e decidiram tentar e fazer isso da maneira antiga por tratamento de algumas mensagens mim e dong desenho quando eu estou tratamento WM_NCPAINT.

A pintura em si vai muito bem e que basicamente funciona.Uma coisa que não funciona que é pintura o TMainMenu.Ou eu tenho o padrão WM_NCACTIVATE manipulador de desenhar toda a área nonclient (eu tenho que Realizar uma WM_NCACTIVATE mensagem dentro do meu WM_NCPAINT manipulador) antes de pintar sobre ele, o que faz com que a cintilação que, aparentemente, não pode ser ajudado.Ou eu posso tentar ter o manipulador padrão de WM_NCPAINT pintar somente o Retângulo que contém a TMainMenu, o que resulta em um bom resultado, mas não redesenhar o menu.

A minha pergunta é:

  • Como posso ter um TMainMenu e apenas um TMainmenu pintado de novo, quando eu estou tratamento WM_NCPAINT mim?

Eu dei a ele um poucos vai e eu acho que estou no caminho certo, mas estou me batendo na parede;Eu não tenho conhecimento suficiente sobre o que eu estou fazendo e não consegue encontrar documentação clara sobre isso.A parte mais importante do meu código é:

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

E eu acho que é exatamente isso o que acontece de errado.(ou melhor, o código onde eu calcular 'MenuRegion'.Eu só tenho nenhuma idéia se isso está acontecendo de errado, porque eu estou usando errado o sistema de coordenadas ou se é porque eu estou indo sobre esta inteiramente o caminho errado.

Aqui está uma versão reduzida do meu código irá compilar e executar o 'tal como está' em 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.
Foi útil?

Solução

"Como posso ter um TMainMenu e apenas um TMainmenu pintado de novo, quando eu estou tratamento WM_NCPAINT de mim?"

Em teoria, você pode modificar a região WM_NCPAINT é passado.Definir uma região que corresponde à área da barra de menu, deixar de fora o resto.Abaixo está uma prova de conceito:

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;


Na prática, você vai finalmente abandonar esse caminho.Nota: o ensaio para a '1' no exemplo acima.Um pseudo identificador para a região de atualização não é mencionado no documentação.Ainda assim, o identificador de região é sempre " 1 " para uma janela normal.Como uma questão de fato, não do cliente processamento de nunca, nunca foram devidamente documentados.Meu palpite é que isso é porque o próprio sistema operacional não jogar pelas regras.Tomemos, por exemplo, o fato de que a NC área a ser pintada durante o tratamento padrão WM_NCACTIVATE.O que faz a ativação tem nada a ver com a NC pintura.Por que o sistema operacional tintas atrás das costas?

Minha sugestão é tomar o caminho que a VCL estilos de ter tomado.Uma vez que você está no desenho NC área, desenhar de tudo.Área da barra de Menu é parte da área de não-cliente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top