【问题标题】:How can I stop window rendering and later resume?如何停止窗口渲染并稍后恢复?
【发布时间】:2010-12-05 18:20:43
【问题描述】:

我想阻止我的窗口被更新,直到我完成从服务器接收数据并呈现它。我可以挂钩 WM_PAINT 事件,还是调用一些 Win32API 方法来防止窗口被更新并在以后解冻它?

更多信息: 在用 C# 编写的 MMC 管理单元的上下文中,我们的应用程序会遇到烦人的闪烁和双重排序行为: 我们使用 MMC 的 listViews,但是由于我们订阅了排序事件。 MMC 使用它自己的魔法并对正在显示的页面进行排序(我们不能覆盖它),当我们收到来自服务器的回复时,我们再次更改 listView。 每行更改都是按顺序完成的,没有 beginUpdate 等(AFAIK)。

【问题讨论】:

  • 很抱歉,您如何从 mmc 进行这项工作(挂钩到 WM_PAINT)?我在树视图上有同样的问题 - 删除子节点时严重闪烁 - 每个 IConsoleNameSpace->DeleteItem 调用 WM_PAINT + WM_ERASEBKGND ...
  • 也许这个链接会有所帮助? msdn.microsoft.com/en-us/library/… - 我不必这样做,因为我们的问题主要出在列表视图上,我们用包含在 Winforms elementhost 中托管的 WPF 列表视图的 FormViews 完全替换了它们(~~是的,我们做到了!~~)。但我也遇到了这个树视图闪烁,可能有一天会尝试。
  • 首先感谢。但在我们的例子中,它必须在 mmc 接口下定义......我尝试了 LockUpdateWindow,它工作,但不知何故它使其他窗口闪烁:)

标签: user-interface winapi mmc doublebuffered


【解决方案1】:

通常挂接到WM_PAINT 是可行的方法,但请确保您也忽略所有WM_ERASEBKGND 通知,否则您仍然会出现闪烁,因为Windows 会为您擦除Windows 区域。 (返回非零以防止 Windows 这样做)

另一种可能性是使用LockWindowUpdate 函数,但它有一些缺点:

  • 只能锁定一个窗口
  • 解锁整个桌面后,所有子窗口(即一切)都会重新绘制,导致整个桌面短暂闪烁。 (在 XP 上比在 Vista 上更糟)

【讨论】:

  • 很抱歉,您如何从 mmc 完成这项工作(挂钩到 WM_PAINT)?
【解决方案2】:

一些控件具有用于此目的的BeginUpdateEndUpdate API。

如果您执行某些操作(例如挂钩并忽略绘制事件)禁用绘制,那么稍后强制重新绘制的一种方法是调用 Invalidate 方法。

【讨论】:

  • 在某些环境中可能是这样,但 AFAIK Invalidate 只是将整个窗口区域标记为“无效”,因此下一个绘制事件会重新绘制所有内容。 Invalidate 本身不会强制重绘,它只会强制重绘发生时,一切都被重绘。
  • 当然,这取决于框架。在 .NET 中,Invalidate 也会触发一个绘制事件。
  • @DR 还有一个Update 方法可以在失效后强制立即更新。 SFAIK,调用 Update 会导致同步绘制事件,而仅调用 Invalidate 会导致异步绘制,即当消息队列没有任何其他消息时将生成 WM_PAINT。
【解决方案3】:

好的,经过所有搜索和检查后,我发现 LockUpdateWindow 是个坏主意 - 例如,请参阅 Raimond Chen OldNewThing 的文章。但即使实现 SetRedrawWindow 的想法也不是那么简单——因为我所拥有的只是从主窗口的 IConsole2* pConsole->GetMainWindow() HWND 处理程序收到的。通过将其设置为 SetRedraw = FALSE,它以非常奇怪的方式消失了。虽然为了让程序只为 TreeView 而不是整个应用程序(我们的左面板)运行,但我运行了

EnumChildWindows(hWnd, SetChildRedraw, FALSE); //stopping redraw
//... here you do your operations
EnumChildWindows(hWnd, SetChildRedraw, TRUE); //restarting redraw

SetChildRedraw 回调的定义方式如下:

#define DECLARE_STRING(str) TCHAR str[MAX_PATH]; ZeroMemory(str, sizeof(str));
BOOL CALLBACK SetChildRedraw(HWND hwndChild, LPARAM lParam) 
{ 
    RECT rcChildRect; ZeroMemory(&rcChildRect, sizeof(rcChildRect));
    DECLARE_STRING(sText)
    GetClassName(hwndChild, sText, MAX_PATH);
    if (wcsstr(sText, L"SysTreeView32") != NULL)
    {
        SetWindowRedraw(hwndChild, lParam);
        if (lParam == TRUE)
        {
            GetWindowRect(hwndChild, &rcChildRect);
            InvalidateRect(hwndChild, &rcChildRect, TRUE);
        }
    }
    return TRUE;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-18
    • 2018-06-10
    • 2012-03-15
    • 1970-01-01
    • 2014-05-02
    • 2016-12-26
    • 2019-06-20
    相关资源
    最近更新 更多