【问题标题】:How to get window behind dialog box to redraw when dialog is moved?移动对话框时如何让对话框后面的窗口重绘?
【发布时间】:2011-11-30 19:59:58
【问题描述】:

在 Windows 上,当我显示一个对话框(自定义对话框或标准打开/保存文件对话框)并将对话框移动到父窗口上时,父窗口不会重绘,留下的痕迹拖动时后面的对话框。 (父窗口使用 OpenGL 绘制,但不确定这是否相关。)当对话框移到主窗口前面时,如何让主窗口重绘?

我尝试在对话框的回调发送消息时添加一个重绘主窗口的调用,但这只会在按下按钮或输入编辑字段时导致重绘发生。当对话框的回调时它不会重绘窗口被移动。我怎样才能检测到这种运动?

【问题讨论】:

  • "(父窗口使用OpenGL进行绘制,但不确定是否相关。)" - 这个相关的。您需要使渲染上下文无效 - 我已经有一段时间没有做这些了,所以我必须去挖掘。
  • Windows 发送该窗口 WM_PAINT 消息告诉它重绘自己。不要忽视它们。

标签: windows dialog drawing


【解决方案1】:

我怀疑您的 opengl 应用程序只是使用一个窗口来显式地通过 opengl 在其上进行渲染,然后进入一个检查鼠标/kb 输入的紧密循环,然后重绘 opengl 表面。我猜你在画一个框架后没有发送消息。更重要的是,您为窗口设置的那个小存根 WndProc 可能没有 WM_PAINT 处理程序。

需要考虑两件事。主窗口的 WndProc 需要捕获 WM_PAINT、重绘和验证表面。那就是……

LRESULT __stdcall YourMainWndProcHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        ...
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            // redraw your stuff here
            return 0;
        }
        ...
    }
}

您可以只调用 ValidateRect,而不是调用 BeginPaint/EndPaint 来处理上面的 WM_APINT。

最好,您在绘图循环中捕获其他窗口消息。这样,如果一个窗口被您应用之外的其他东西遮住(例如,调整大小操作、最小化/恢复、其他窗口应用占据前台等),那么您也可以处理重绘通知。

典型的游戏会尝试在每个绘图帧中发送 1 条消息。

void MainLoop()
{
    MSG msg;
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // your code to simulate and render below
    DrawScreen();
}

【讨论】:

  • 谢谢。我实际上使用的是 SDL 1.3,我现在的猜测是,当 SDL 1.3 从 Windows 获取 WM_PAINT 事件时,它不会向我传递窗口重绘事件。这可能是 SDL 中需要修复的错误。我会调查的。
  • 关于上述回复的一个问题。我使用的对话框是模态的,所以它们在退出之前不会返回控制。在这种情况下,我看不出我的游戏的主事件循环如何相关?
  • 我进一步研究了它,当 SDL 收到 WM_PAINT 事件时,它会向我发送 SDL_WINDOWEVENT_EXPOSED 事件。但是,只有当我的窗口在后台,并且其他一些应用程序的窗口在前面并被拖动时,我才会收到这些事件。当我自己的对话框(模态)被拖动时,我没有收到这些事件。这里是使用无模式对话框的唯一解决方案吗?如果是这样,标准打开/保存对话框的解决方案是什么?
  • 如何获得 EXPOSED 事件?大概是通过定期调用 SDL_PollEvent 吧?但是当模式对话框启动时,这不会发生。解决方案:创建一个隐藏的 win32 窗口(具有不同 wndproc 的窗口类的实例 - 其中未调用 WS_VISIBLE 或 ShowWindow)。此窗口的 WM_CREATE 启动一个计时器 (SetTimer) 以将 WM_TIMER 发布到它自己的 WndProc。您可以通过重绘主窗口(或调用 SDL_PollEvent 来获取 EXPOSED 通知)来处理这些计时器事件。然后 DestroyWindow 或 KillTimer 在模态对话框 API 返回后进行清理。
  • 您也可以在主窗口上使用 SetTimer 调用。忘记创建单独的隐藏窗口的需要。
猜你喜欢
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-30
  • 1970-01-01
相关资源
最近更新 更多