wm_ncpaintを取り扱うときにTMainMenuの統一を強制する
-
21-12-2019 - |
質問
私は多くのフォームを持つかなり大きなアプリケーションでキャプションバーをカスタム塗装していて、私がwm_ncpaintを取り扱うときに描画を扱うことによって昔ながらの方法を試みて昔ながらの道をやることにしました。
絵自体はかなり元気で本質的に作品しています。 TmainMenuを塗ることにうまくいかないことの一つ。既定のWM_NCACTIVATEハンドラは、描画前に描画する前に、既定の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」を計算するコード。私は間違った座標系を使っているのか、それがこれまでに間違った方法を使っているからです。
これは、Delphi(XE3)の「ASとAS」をコンパイルして実行するのが短いバージョンです。
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.
. 解決
「私は自分でwm_ncpaintを処理しているときにTMainMenuと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エリアを描画したら、それをすべて描きます。メニューバー領域は、クライアント以外の領域の一部です。