在 MDI 主框架背景上绘制图像






4.90/5 (11投票s)
2002年3月27日
2分钟阅读

159809

3934
本文解释了如何使用窗口子类化在 MDI 主框架背景上绘制图像。
引言
很久以前,我使用Altera Max Plus II设计简单的电子芯片。我注意到这个应用程序在主框架背景上绘制了一个公司徽标。有一次我比平时有更多空闲时间,我想自己实现它。这篇文章就是结果:)
实现
所以您可能想知道如何在MDI应用程序的背景窗口上绘制图像。首先,您必须对要绘制的窗口进行子类化。为此,请调用 GetWindowLong(hMain, GWL_WNDPROC)
。此函数返回指向旧窗口过程的指针,我们希望将其存储以供将来使用。将此代码放在 CBackgroundImageApp::InitInstance()
中,就在显示主框架之前。
HWND hMain = pMainFrame->GetWindow(GW_CHILD)->GetSafeHwnd();
pfnOldWndProc = (WNDPROC)GetWindowLong(hMain, GWL_WNDPROC);
SetWindowLong(hMain, GWL_WNDPROC, (long)pfnNewWndProc);
现在我们已经对窗口进行了子类化,但是缺少窗口过程。现在就来编写它。窗口过程应该至少处理两个消息才能正常工作
-
WM_ERASEBKGND
-
WM_SIZE
第一条消息是在Windows希望应用程序绘制其背景时发送的 - 我们将使用它来绘制我们的图像。第二条消息也很重要,因为当您调整窗口大小时,图像也应该改变其大小以适应新的窗口大小。我们的窗口过程将如下所示
LRESULT CALLBACK pfnNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,LPARAM lParam) { // local variables goes here switch (uMsg) { case WM_SIZE : // ... case WM_ERASEBKGND : // ... return 1; default : return CallWindowProc(pfnOldWndProc, hwnd, uMsg, wParam, lParam); } }
重要提示
从 WM_ERASEBKGND
案例返回1非常重要,因为它告诉Windows我们已经擦除了窗口,并且它无事可做,否则Windows将使用标准背景再次擦除此窗口,我想这并不是我们想要实现的。
那其他消息呢?
这就是我们使用存储的旧窗口过程指针的时刻。我们只需调用旧过程并返回其结果。所以让我们更仔细地看看 WM_SIZE
和 WM_ERASEBKGND
案例。
WM_SIZE
- 简单地通过向其发送 WM_ERASEBKGND
来强制窗口背景重新绘制自身
SendMessage(hwnd, WM_ERASEBKGND, (WPARAM)GetDC(hwnd), 0); return 1;
WM_ERASEBKGND
- 下面的代码将简单地 BitBlt
位图。因为窗口大小通常很少与图像大小相同,所以 BitBlt()
变为 StretchBlt()
。最后它看起来如下
hcompdc = CreateCompatibleDC(hdc); SelectObject(hcompdc, hBmp); GetClientRect(hwnd, &rect); if (0 == StretchBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, hcompdc, 0, 0, 1152, 864, SRCCOPY)) MessageBox(hwnd, "error", "while StretchBlt", MB_OK); DeleteDC(hcompdc); return 1;
其中
-
hdc
,hcompdc
- 设备上下文 -
hBmp
- 位图句柄
调用 DeleteDC()
非常重要,因为我们不想导致内存泄漏。
最后说明
此解决方案的性能在很大程度上取决于操作系统、已安装的显卡以及窗口大小。 在我的情况下,Windows 98 是比 Windows 2000 更好的选择。 在第二个系统上,重新绘制图像需要更长的时间才能让用户感觉舒适。 在 Windows 98 上运行的应用程序看起来非常好。 我的图形设备是 Geforce 2 GTS。 如果您有其他系统或配置的经验,请在文章的讨论区下方发布。 StretchBlt
函数在系统之间也有所不同。 这一次,Windows 98 中使用的也更好。