Costringendo un ridisegno di Tmainmenu quando si maneggiando WM_ncpaint da solo
-
21-12-2019 - |
Domanda
Sono personalizzato dipingere la mia barra di didascalia in un'applicazione piuttosto ampia con molte forme e ha deciso di provare a farlo il modo vecchio stile maneggiando alcuni messaggi e dong il disegno quando sto maneggiando wm_ncpaint. .
Il dipinto stesso sta andando abbastanza bene ed essenzialmente funziona. Una cosa che non funzionerà però sta dipingendo il Tmainmenu. Opponi il gestore WM_NCACTIVATE predefinito Disegna l'intera area non Asclient (devo eseguire un messaggio WM_NCACTIVATE all'interno del mio gestore WM_NCPaint) prima di dipingere su di esso, il che causa tremolante che apparentemente non può essere aiutato. Oppure posso provare ad avere il gestore predefinito di wm_ncpaint dipingere solo il retto contenente il tmainmenu, che provoca un risultato regolare, ma non ridimensiona il menu.
La mia domanda è:
- .
-
Come posso avere un tmainmenu e solo un tmainmenu ridipinto quando sto maneggiando wm_ncpaint me stesso?
L'ho dato alcuni vai e penso di essere nel modo giusto, ma sto colpendo un muro; Non ho abbastanza conoscenza su ciò che sto facendo e non riesco a trovare una chiara documentazione a riguardo. La parte più importante del mio codice è:
RedrawWindow(Handle, nil, MenuRegion, RDW_INVALIDATE or RDW_FRAME or RDW_NOERASE or RDW_ALLCHILDREN or RDW_UPDATENOW);
.
E penso che sia esattamente che ciò che va storto. (o meglio, il codice in cui calcolò 'menuregion'. Non ho alcun indizio se sta andando male perché sto usando il sistema di coordinate sbagliato o se è perché sto andando su questo completamente il modo sbagliato.
Ecco una versione ridotta del mio codice che si compilerà ed eseguirà 'come è' in 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.
. Soluzione
"Come posso avere un tmainmenu e solo un tmainmenu ridipinto quando sto maneggiando wm_ncpaint me stesso?"
In teoria è possibile modificare la regione WM_NCPAINT
. Definire una regione che corrisponde all'area della barra dei menu, lascia il resto. La seguente è una prova del concetto:
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 pratica, alla fine avrai abbandonato questa rotta. Nota il test per '1' nel campione di cui sopra. Una maniglia pseudo per la regione di aggiornamento non è menzionata nel Documentazione . Eppure la maniglia della regione è sempre '1' per una finestra normale. In effetti la gestione non client non è mai stata correttamente documentata. La mia ipotesi è, per questo perché il sistema operativo stesso non gioca per le regole. Prendiamo ad esempio il fatto che l'area NC viene dipinta durante la gestione predefinita di WM_NCACTIVATE
. Che cosa ha nulla a che fare con la pittura NC. Perché il sistema operativo dipinge dietro la schiena?