翻译《The Old New Thing》 - What’s the point of DeferWindowPos?

What's the point of DeferWindowPos? - The Old New Thing (microsoft.com)icon-default.png?t=N7T8https://devblogs.microsoft.com/oldnewthing/20050706-26/?p=35023

Raymond Chen 在 2005年7月6日


DeferWindowPos 的作用是什么?

简要

        文章讨论了 DeferWindowPos 函数的用途,即一次性移动多个子窗口,以减少窗口移动时发生的重绘数量。

正文

  DeferWindowPos 函数的目的是一次性移动多个子窗口。这在一定程度上减少了窗口移动时发生的重绘数量。

        拿几个月前的 DC 刷子示例,进行以下修改:

HWND g_hwndChildren[2];
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) {
 const static COLORREF s_rgclr[2] = { RGB(255,0,0), RGB(0,255,0) };
 for (int i = 0; i < 2; i++) {
  g_hwndChildren[i] = CreateWindow(TEXT("static"), NULL,
        WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
        hwnd, (HMENU)IntToPtr(s_rgclr[i]), g_hinst, 0);
  if (!g_hwndChildren[i]) return FALSE;
 }
 return TRUE;
}

        注意我使用控件 ID 来保存所需的颜色。在选择背景颜色时,我们会检索它。

HBRUSH OnCtlColor(HWND hwnd, HDC hdc, HWND hwndChild, int type) {
 Sleep(500);
 SetDCBrushColor(hdc, (COLORREF)GetDlgCtrlID(hwndChild));
 return GetStockBrush(DC_BRUSH);
}
HANDLE_MSG(hwnd, WM_CTLCOLORSTATIC, OnCtlColor);

         我加了一个半秒的睡眠,这样更容易观察绘制过程。

void OnSize(HWND hwnd, UINT state, int cx, int cy) {
  int cxHalf = cx/2;
  SetWindowPos(g_hwndChildren[0],
               NULL, 0, 0, cxHalf, cy,
               SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  SetWindowPos(g_hwndChildren[1],
               NULL, cxHalf, 0, cx-cxHalf, cy,
               SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
}

        我们将两个子窗口并排放置在客户区域。首先,我们使用 SetWindowPos 函数来定位窗口。

编译并运行这个程序,一旦它启动,点击最大化按钮。仔细观察绿色矩形的哪些部分被重绘。

        现在让我们将定位代码更改为使用 DeferWindowPos 函数。延迟窗口定位函数的使用模式如下:

HDWP hdwp = BeginDeferWindowPos(n);
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 1
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 2
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // 3
…
if (hdwp) hdwp = DeferWindowPos(hdwp, …); // n
if (hdwp) EndDeferWindowPos(hdwp);

        这里有一些关键点:

  • 你传递给 BeginDeferWindowPos 函数的值是你打算移动的窗口数量。即使这个值错了也没关系,但正确的值会减少内部重新分配的数量。

  • DeferWindowPos 返回的值存储回 hdwp,因为返回值不一定与最初传递的值相同。如果延迟预订需要执行重新分配,DeferWindowPos 函数返回指向新的延迟信息的句柄;旧的延迟信息不再有效。更重要的是,如果延迟失败,旧的延迟信息将被销毁。这与 realloc 函数不同,如果重新分配失败,原始对象保持不变。模式 p = realloc(p, …) 会导致一个内存泄漏,但模式 hdwp = DeferWindowPos(hdwp, …) 不会。

        第二点很重要,但许多人都弄错了。

        现在你可能对这个函数感到害怕,让我们将重新定位代码更改为利用延迟窗口定位。它真的没有那么难。(将这些更改保存到一个新文件中,不过。我们将想要并排运行旧版本和新版本。)

void OnSize(HWND hwnd, UINT state, int cx, int cy) {
  HDWP hdwp = BeginDeferWindowPos(2);
  int cxHalf = cx/2;
  if (hdwp) hdwp = DeferWindowPos(hdwp, g_hwndChildren[0],
               NULL, 0, 0, cxHalf, cy,
               SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  if (hdwp) hdwp = DeferWindowPos(hdwp, g_hwndChildren[1],
               NULL, cxHalf, 0, cx-cxHalf, cy,
               SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  if (hdwp) EndDeferWindowPos(hdwp);
}

        编译并运行这个程序,再次启动后,最大化窗口并观察哪些区域重新绘制。你会发现新版本与旧版本相比,重绘的区域略有减少。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0x0007

可不可奖励我吃只毛嘴鸡 馋😋

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值