如何使用 Windows API 将菜单​​背景渐变复制或复制到位图上?

How to copy or reproduce the menu background gradient onto a bitmap using the Windows API?

我正在尝试(未成功)copy/reproduce Windows 菜单栏的背景渐变到位图上。

在下面的 IconToBitmap 函数中,FillRect(一厢情愿)使用 GetSysColorBrush(COLOR_MENU) 试图让它按照 Windows 中的方式绘制菜单背景(不出所料,画笔不是渐变,但值得一试。)

下面的 BitBlt 是 "cheat" 的尝试。抓住一部分已经绘制的菜单栏并使用它。这也不起作用,我怀疑原因可能是因为在主 window 的 WM_CREATE 期间调用了函数 IconToBitmap (我不确定菜单栏是否存在于 window 创建。)在 window 首次可见之前我确实需要背景,这就是在处理 WM_CREATE 时调用该函数的原因(但在 [=36= 之前工作的任何其他方式) ]可见即完美。)

在这一点上,我没主意了。

如果有人知道如何抓取该菜单背景或在位图上复制它,那就太好了。

谢谢。

PS:函数中的硬编码值将在最终工作版本中删除(希望会有一个。)此外,对于 Delphi,数据类型 ptrint 必须更改为NativeInt.

function IconToBitmap(Wnd : HWND; Icon : HICON) : HBITMAP;
var
  Bitmap      : HBITMAP;
  BitmapDc    : HDC;
  BitmapRect  : TRECT;

  OldBitmap   : HBITMAP;

  dc          : HDC;

  MenuHeight  : ptrint;
  MenuY       : ptrint;

  WindowDc    : HDC;

begin
  Bitmap      := 0;
  BitmapDc    := 0;
  OldBitmap   := 0;
  dc          := 0;

  MenuY       := 0;
  MenuHeight  := 0;

  WindowDc    := 0;


  MenuY       := GetSystemMetrics(SM_CYSIZEFRAME) +
                 GetSystemMetrics(SM_CYCAPTION);
  MenuHeight  := GetSystemMetrics(SM_CYMENUSIZE);

  WindowDc    := GetWindowDC(Wnd);


  dc          := GetDC(0);
  BitmapDc    := CreateCompatibleDC(dc);

  Bitmap      := CreateCompatibleBitmap(dc, 16, 16);
  OldBitmap   := SelectObject(BitmapDc, Bitmap);

  with BitmapRect do
  begin
    Left      := 0;
    Top       := 0;
    Right     := 16;
    Bottom    := 16;
  end;

  FillRect(BitmapDc, BitmapRect, GetSysColorBrush(COLOR_MENU));
  BitBlt(BitmapDc, 0, 0, 16, 16, WindowDc, 20, MenuY, SRCCOPY);

  DrawIconEx(BitmapDc,
             0,
             0,
             Icon,
             16,
             16,
             0,
             0,
             DI_NORMAL);

  SelectObject(BitmapDc, OldBitmap);
  DeleteDC(BitmapDc);
  ReleaseDC(0, dc);

  IconToBitmap := Bitmap;
end;

使用visual styles API绘制主题部分。下面的示例在窗体的客户区顶部绘制了一个菜单栏背景。您可以调整它以绘制到位图 canvas.

uses
  uxtheme, types;

procedure TForm1.FormPaint(Sender: TObject);
var
  Theme: HTHEME;
  Size: TSize;
  Rect: TRect;
begin
  Theme := OpenThemeData(Handle, VSCLASS_MENU);
  GetThemePartSize(Theme, Canvas.Handle, MENU_BARBACKGROUND, MB_ACTIVE, nil,
      TS_TRUE, Size);
  Rect.Create(0, 0, ClientWidth, Size.cy);
  DrawThemeBackground(Theme, Canvas.Handle, MENU_BARBACKGROUND, MB_ACTIVE,
      Rect, nil);
  CloseThemeData(Theme);
end;

WM_PAINT 处理程序中,这可能如下所示。

procedure TForm1.WMPaint(var Message: TWMPaint);
var
  DC: HDC;
  PS: TPaintStruct;

  Theme: HTHEME;
  Size: TSize;
  Rect: TRect;
begin
  if Message.DC = 0 then
    DC := BeginPaint(Handle, PS)
  else
    DC := Message.DC;

  Theme := OpenThemeData(Handle, VSCLASS_MENU);
  GetThemePartSize(Theme, DC, MENU_BARBACKGROUND, MB_ACTIVE, nil,
      TS_TRUE, Size);
  Rect.Create(0, 0, ClientWidth, Size.cy);
  DrawThemeBackground(Theme, DC, MENU_BARBACKGROUND, MB_ACTIVE,
      Rect, nil);
  CloseThemeData(Theme);

  if Message.DC = 0 then begin
    Message.DC := DC;
    inherited;
    EndPaint(Handle, PS);
  end else
    inherited;
end;