【问题标题】:MSDN example for scrolling large image malfunctions滚动大图故障的 MSDN 示例
【发布时间】:2015-09-07 08:42:56
【问题描述】:

简介及相关资料:

我正在尝试以自然大小在主窗口中绘制图像。目前我需要渲染 EMF。

上网浏览后发现this MSDN example

问题:

我试过了,发现效果不好。

用户右键单击后,按照说明,桌面屏幕截图会正确绘制在主窗口中。

但是,当用户调整窗口大小时,会出现如下图所示的绘制伪影。当用户稍微滚动然后调整窗口大小时,也会出现以下效果。

图片如下:

这是我第一次尝试滚动图像并使用滚动条,所以我真的很难找到解决方案。

问题:

如何修复文章代码以删除此视觉伪影?

您能否推荐另一个示例,例如教程/代码示例/等 我可以学习吗?

您能否提供对我有帮助的“口头”说明/指南?

为了使您的任务更加轻松,这里是重现问题的最小代码。请注意,我只是复制/粘贴了窗口过程,并为程序添加了最少的代码来创建工作演示:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    SCROLLINFO si;

    // These variables are required by BitBlt. 
    static HDC hdcWin;           // window DC 
    static HDC hdcScreen;        // DC for entire screen 
    static HDC hdcScreenCompat;  // memory DC for screen 
    static HBITMAP hbmpCompat;   // bitmap handle to old DC 
    static BITMAP bmp;           // bitmap data structure 
    static BOOL fBlt;            // TRUE if BitBlt occurred 
    static BOOL fScroll;         // TRUE if scrolling occurred 
    static BOOL fSize;           // TRUE if fBlt & WM_SIZE 

    // These variables are required for horizontal scrolling. 
    static int xMinScroll;       // minimum horizontal scroll value 
    static int xCurrentScroll;   // current horizontal scroll value 
    static int xMaxScroll;       // maximum horizontal scroll value 

    // These variables are required for vertical scrolling. 
    static int yMinScroll;       // minimum vertical scroll value 
    static int yCurrentScroll;   // current vertical scroll value 
    static int yMaxScroll;       // maximum vertical scroll value 

    switch (uMsg)
    {
    case WM_CREATE:

        // Create a normal DC and a memory DC for the entire 
        // screen. The normal DC provides a snapshot of the 
        // screen contents. The memory DC keeps a copy of this 
        // snapshot in the associated bitmap. 
        hdcScreen = CreateDC(L"DISPLAY", (PCTSTR)NULL,
            (PCTSTR)NULL, (CONST DEVMODE *) NULL);
        hdcScreenCompat = CreateCompatibleDC(hdcScreen);

        // Retrieve the metrics for the bitmap associated with the 
        // regular device context. 
        bmp.bmBitsPixel =
            (BYTE)GetDeviceCaps(hdcScreen, BITSPIXEL);
        bmp.bmPlanes = (BYTE)GetDeviceCaps(hdcScreen, PLANES);
        bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);
        bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);

        // The width must be byte-aligned. 
        bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15) / 8;

        // Create a bitmap for the compatible DC. 
        hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
            bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);

        // Select the bitmap for the compatible DC. 
        SelectObject(hdcScreenCompat, hbmpCompat);

        // Initialize the flags. 
        fBlt = FALSE;
        fScroll = FALSE;
        fSize = FALSE;

        // Initialize the horizontal scrolling variables. 
        xMinScroll = 0;
        xCurrentScroll = 0;
        xMaxScroll = 0;

        // Initialize the vertical scrolling variables. 
        yMinScroll = 0;
        yCurrentScroll = 0;
        yMaxScroll = 0;

        break;

    case WM_SIZE:
    {
        int xNewSize;
        int yNewSize;

        xNewSize = LOWORD(lParam);
        yNewSize = HIWORD(lParam);

        if (fBlt)
            fSize = TRUE;

        // The horizontal scrolling range is defined by 
        // (bitmap_width) - (client_width). The current horizontal 
        // scroll value remains within the horizontal scrolling range. 
        xMaxScroll = max(bmp.bmWidth - xNewSize, 0);
        xCurrentScroll = min(xCurrentScroll, xMaxScroll);
        si.cbSize = sizeof(si);
        si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
        si.nMin = xMinScroll;
        si.nMax = bmp.bmWidth;
        si.nPage = xNewSize;
        si.nPos = xCurrentScroll;
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

        // The vertical scrolling range is defined by 
        // (bitmap_height) - (client_height). The current vertical 
        // scroll value remains within the vertical scrolling range. 
        yMaxScroll = max(bmp.bmHeight - yNewSize, 0);
        yCurrentScroll = min(yCurrentScroll, yMaxScroll);
        si.cbSize = sizeof(si);
        si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
        si.nMin = yMinScroll;
        si.nMax = bmp.bmHeight;
        si.nPage = yNewSize;
        si.nPos = yCurrentScroll;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

        break;
    }

    case WM_PAINT:
    {
        PRECT prect;

        hdc = BeginPaint(hwnd, &ps);

        // If the window has been resized and the user has 
        // captured the screen, use the following call to 
        // BitBlt to paint the window's client area. 
        if (fSize)
        {
            BitBlt(ps.hdc,
                0, 0,
                bmp.bmWidth, bmp.bmHeight,
                hdcScreenCompat,
                xCurrentScroll, yCurrentScroll,
                SRCCOPY);

            fSize = FALSE;
        }

        // If scrolling has occurred, use the following call to 
        // BitBlt to paint the invalid rectangle. 
        // 
        // The coordinates of this rectangle are specified in the 
        // RECT structure to which prect points. 
        // 
        // Note that it is necessary to increment the seventh 
        // argument (prect->left) by xCurrentScroll and the 
        // eighth argument (prect->top) by yCurrentScroll in 
        // order to map the correct pixels from the source bitmap. 
        if (fScroll)
        {
            prect = &ps.rcPaint;

            BitBlt(ps.hdc,
                prect->left, prect->top,
                (prect->right - prect->left),
                (prect->bottom - prect->top),
                hdcScreenCompat,
                prect->left + xCurrentScroll,
                prect->top + yCurrentScroll,
                SRCCOPY);

            fScroll = FALSE;
        }

        EndPaint(hwnd, &ps);

        break;
    }

    case WM_HSCROLL:
    {
        int xDelta;     // xDelta = new_pos - current_pos  
        int xNewPos;    // new position 
        int yDelta = 0;

        switch (LOWORD(wParam))
        {
            // User clicked the scroll bar shaft left of the scroll box. 
        case SB_PAGEUP:
            xNewPos = xCurrentScroll - 50;
            break;

            // User clicked the scroll bar shaft right of the scroll box. 
        case SB_PAGEDOWN:
            xNewPos = xCurrentScroll + 50;
            break;

            // User clicked the left arrow. 
        case SB_LINEUP:
            xNewPos = xCurrentScroll - 5;
            break;

            // User clicked the right arrow. 
        case SB_LINEDOWN:
            xNewPos = xCurrentScroll + 5;
            break;

            // User dragged the scroll box. 
        case SB_THUMBPOSITION:
            xNewPos = HIWORD(wParam);
            break;

        default:
            xNewPos = xCurrentScroll;
        }

        // New position must be between 0 and the screen width. 
        xNewPos = max(0, xNewPos);
        xNewPos = min(xMaxScroll, xNewPos);

        // If the current position does not change, do not scroll.
        if (xNewPos == xCurrentScroll)
            break;

        // Set the scroll flag to TRUE. 
        fScroll = TRUE;

        // Determine the amount scrolled (in pixels). 
        xDelta = xNewPos - xCurrentScroll;

        // Reset the current scroll position. 
        xCurrentScroll = xNewPos;

        // Scroll the window. (The system repaints most of the 
        // client area when ScrollWindowEx is called; however, it is 
        // necessary to call UpdateWindow in order to repaint the 
        // rectangle of pixels that were invalidated.) 
        ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
            (CONST RECT *) NULL, (HRGN)NULL, (PRECT)NULL,
            SW_INVALIDATE);
        UpdateWindow(hwnd);

        // Reset the scroll bar. 
        si.cbSize = sizeof(si);
        si.fMask = SIF_POS;
        si.nPos = xCurrentScroll;
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

        break;
    }

    case WM_VSCROLL:
    {
        int xDelta = 0;
        int yDelta;     // yDelta = new_pos - current_pos 
        int yNewPos;    // new position 

        switch (LOWORD(wParam))
        {
            // User clicked the scroll bar shaft above the scroll box. 
        case SB_PAGEUP:
            yNewPos = yCurrentScroll - 50;
            break;

            // User clicked the scroll bar shaft below the scroll box. 
        case SB_PAGEDOWN:
            yNewPos = yCurrentScroll + 50;
            break;

            // User clicked the top arrow. 
        case SB_LINEUP:
            yNewPos = yCurrentScroll - 5;
            break;

            // User clicked the bottom arrow. 
        case SB_LINEDOWN:
            yNewPos = yCurrentScroll + 5;
            break;

            // User dragged the scroll box. 
        case SB_THUMBPOSITION:
            yNewPos = HIWORD(wParam);
            break;

        default:
            yNewPos = yCurrentScroll;
        }

        // New position must be between 0 and the screen height. 
        yNewPos = max(0, yNewPos);
        yNewPos = min(yMaxScroll, yNewPos);

        // If the current position does not change, do not scroll.
        if (yNewPos == yCurrentScroll)
            break;

        // Set the scroll flag to TRUE. 
        fScroll = TRUE;

        // Determine the amount scrolled (in pixels). 
        yDelta = yNewPos - yCurrentScroll;

        // Reset the current scroll position. 
        yCurrentScroll = yNewPos;

        // Scroll the window. (The system repaints most of the 
        // client area when ScrollWindowEx is called; however, it is 
        // necessary to call UpdateWindow in order to repaint the 
        // rectangle of pixels that were invalidated.) 
        ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
            (CONST RECT *) NULL, (HRGN)NULL, (PRECT)NULL,
            SW_INVALIDATE);
        UpdateWindow(hwnd);

        // Reset the scroll bar. 
        si.cbSize = sizeof(si);
        si.fMask = SIF_POS;
        si.nPos = yCurrentScroll;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

        break;
    }

    case WM_RBUTTONDOWN:
    {
        // Get the compatible DC of the client area. 
        hdcWin = GetDC(hwnd);

        // Fill the client area to remove any existing contents. 
        RECT rect;
        GetClientRect(hwnd, &rect);
        FillRect(hdcWin, &rect, (HBRUSH)(COLOR_WINDOW + 1));

        // Copy the contents of the current screen 
        // into the compatible DC. 
        BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
            bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);

        // Copy the compatible DC to the client area.
        BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,
            hdcScreenCompat, 0, 0, SRCCOPY);

        ReleaseDC(hwnd, hdcWin);
        fBlt = TRUE;
        break;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
    int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    // register main window class
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"Main_Window";
    wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!",
            MB_ICONEXCLAMATION | MB_OK);

        return 0;
    }

    // create main window
    hwnd = CreateWindowEx(0, L"Main_Window", L"Scrollable map",
        WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, 
        50, 50, 400, 400, NULL, NULL, hInstance, 0);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return Msg.wParam;
}

【问题讨论】:

  • 致那些因为 寻求调试帮助的问题(“为什么这段代码不起作用?”)而投票决定关闭的人 必须包含所需的行为,一个特定的问题或错误在问题本身中重现它所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建一个最小、完整和可验证的示例。 我的重点是:我制作了演示问题的最小代码。我已经清楚地指出了错误并清楚地描述了期望的行为。请重新考虑您的接近投票。
  • 您的 WM_PAINT 处理程序应该返回 0,而不是调用 DefWindowProcmsdn.microsoft.com/en-us/library/windows/desktop/… WM_HSCROLLWM_VSCROLL 等也有同样的问题。阅读正在处理的所有消息的文档非常重要——将错误的值返回给 Windows 子系统是导致很多错误和奇怪的行为。
  • @PaulMcKenzie:不工作,同样的行为仍然存在。我已经更正了所有返回值,但没有成功。问题一定与绘画算法有关,但我就是不知道是什么。感谢您尝试...
  • 这不仅仅是一次“尝试”。无论它是否解决了您的问题,都必须对您的代码进行更正。修复返回码将是实际修复的第 1 部分。此外,还有大量显示位图滚动的其他示例存在问题。即使是旧的 Win 3.x,代码也可以滚动位图,所以即使是旧样本也可以考虑。
  • 从您的描述看来,滚动似乎是一种分心 - 从根本上说,这不只是调整大小时重新绘制的问题吗?尝试在调整窗口大小时使用InvalidateRect 强制重绘。

标签: c++ c winapi scrollbar gdi


【解决方案1】:

根据您的描述,问题似乎只是您的窗口在调整大小时没有被重新绘制。

在窗口放大时使用InvalidateRect 强制重新绘制新发现的区域,或者在窗口类上设置CS_VREDRAWCS_HREDRAW 样式以使客户区自动重新绘制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    • 2018-10-03
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    相关资源
    最近更新 更多