如何使用 Windows API 将菜单背景渐变复制或再现到位图上?
Posted
技术标签:
【中文标题】如何使用 Windows API 将菜单背景渐变复制或再现到位图上?【英文标题】:How to copy or reproduce the menu background gradient onto a bitmap using the Windows API? 【发布时间】:2019-03-25 12:18:53 【问题描述】:我正在尝试(未成功)将 Windows 菜单栏的背景渐变复制/再现到位图上。
在下面的 IconToBitmap 函数中,FillRect (希望)使用 GetSysColorBrush(COLOR_MENU) 尝试让它在 Windows 中绘制菜单背景(不出所料,画笔不是渐变,但值得试一试。)
它下面的BitBlt是企图“作弊”。抓住已经绘制的菜单栏的一部分并使用它。那也不起作用,我怀疑原因可能是因为在主窗口的 WM_CREATE 期间调用了函数 IconToBitmap (我不确定菜单栏在窗口创建的早期是否存在。)我确实需要背景窗口首先可见,这就是在处理 WM_CREATE 时调用该函数的原因(但在窗口可见之前工作的任何其他方式都是完美的。)
在这一点上,我没有想法。
如果有人知道如何抓取该菜单背景或在位图上重现它,那就太好了。
谢谢。
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;
【问题讨论】:
你到底在说什么渐变?旧的航空玻璃效应?还是工具栏上使用的效果? 菜单栏背景的渐变。当然,它会根据主题和用户偏好而变化,但无论它是什么,我都想重现该背景或在位图中复制它。要查看我在说什么,请启动记事本,记事本出现的任何菜单栏背景都是我想要复制或重现的背景。 @AlexK.: OP 可能使用的是 Windows 7(或更早版本),其中菜单栏确实具有渐变背景。例如,参见english.rejbrand.se/rejbrand/pix/screenshots/rte31/…。当然,在 Windows 10 中,菜单栏背景是纯色的。 是的,我目前使用的是 Windows 7,但是我想抓取/复制当前正在使用的任何背景。这是非常重要的。这就是为什么我试图“作弊”并将菜单栏背景的一部分简单地bitblt到位图上。这似乎是获得背景“条带”的最简单方法。我知道如果我在处理 WM_PAINT 时执行 bitblt 可以得到它,但我需要它更早。理想情况下,在 WM_CREATE 期间,但在窗口可见之前的任何时间都可以工作。 DrawThemeBackground() 和朋友可能值得一看。 【参考方案1】:使用visual styles API 绘制主题部分。下面的示例在窗体的客户区顶部绘制菜单栏背景。您可以调整它以在位图画布上绘制。
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;
【讨论】:
@Sertac,我正在尝试将您发布的代码转换为直接的 Windows API。在 WM_PAINT 消息中,第一条语句“Theme := OpenThemeData(Wnd, VSCLASS_MENU);”给我一个访问冲突。我对主题 API 不熟悉,我对该语句的“翻译”有什么问题吗?谢谢。 @Sci - 不知道你在做什么就无法判断。我发布了一个例子。 @Sertac,我正在做的是像 C 程序员(不是 C++)那样直接对 Windows API 进行编程。 IOW,任何地方都没有对象,只有 API 调用和 Window 消息处理。我的声明,“主题:= OpenThemeData(Handle,VSCLASS_MENU);”我猜测“Handle”参数是一个窗口句柄(来自 MSDN 文档),因此语句变成了“Theme := OpenThemeData(Wnd, VS_CLASS);”我传递的“Wnd”变量是接收消息的窗口句柄。因此该值是有效的。其他一切都是一样的,但我遇到了访问冲突。任何想法为什么? Handle 是窗口句柄好吗。该函数通常在失败时返回 0。 VSCLASS_MENU 是字符串 'MENU',MENU_BARBACKGROUND 是 7,MB_ACTIVE 是 1。OpenThemeData 从 uxtheme.dll 中导出,带有 stdcall 约定,接受句柄和 PWideChar 参数并返回句柄。我建议你问一个提供复制案例的问题。不要编辑这个,因为这是完全不同的事情。 Sertac,感谢您的耐心和帮助。我弄清楚了为什么我得到了一个例外。 Free Pascal 有一个指向 OpenThemeData 的指针变量,该变量仅在调用 InitThemeLibrary 时才被初始化(MSDN 文档中没有的一些细节)。现在我知道为什么这样一个简单的函数调用不起作用,我可以专注于您提供的其余代码。不幸的是,直到明天我才能再花时间解决这个问题,但我会提供反馈。这是我能做的最少的事情。再次感谢您。以上是关于如何使用 Windows API 将菜单背景渐变复制或再现到位图上?的主要内容,如果未能解决你的问题,请参考以下文章