在使用ScrollWindowEx滚动Cwnd后,控件将消失 [英] Controls disappear after scrolling in Cwnd with ScrollWindowEx

查看:180
本文介绍了在使用ScrollWindowEx滚动Cwnd后,控件将消失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Cwnd中实现了一个CScrollBar,但是滚动后,窗口上的控件消失。我听说我可以使用DeferWindowPos一些,但我不知道如何做到这一点。任何想法?

  CPanel :: CPanel()
{
CreateEx(WS_EX_CONTROLPARENT,_T ),NULL,WS_CHILD | WS_TABSTOP | WS_BORDER,m_clRect,pwndParent,IDC_PANEL_FORM);
ScrollBarInit();
}

创建滚动条

  void CPanel :: ScrollBarInit()
{

//在这之前我计算滚动条的大小和滚动区的大小
m_pclScrollBar = new CScrollBar();
m_pclScrollBar-> Create(WS_CHILD | WS_VISIBLE | SBS_VERT | SBS_RIGHTALIGN,clRectScrollbar,this,IDC_SCROLLBAR_​​FORM);
m_pclScrollBar-> SetScrollRange(VSCROLL_RANGE_MIN,VSCROLL_RANGE_MAX);
//之后,我添加滚动条信息

}

处理消息

  void CPanel :: OnVScroll(UINT iSBCode,UINT iPos,CScrollBar * pclScrollBar)
{
switch(pclScrollBar-> GetDlgCtrlID())
{
case IDC_SCROLLBAR_​​FORM:
ScrollBarScroll(iSBCode,iPos,pclScrollBar);
break;
}
}

滚动

  void CPanel :: ScrollBarScroll(UINT iSBCode,UINT iPos,CScrollBar * pclScrollBar)
{
int iScrollPositionPrevious;
int iScrollPosition;
int iScrollPositionOriginal;

iScrollPositionOriginal = m_pclScrollBar-> GetScrollPos();
iScrollPosition = iScrollPositionOriginal;

if(m_pclScrollBar!= NULL)
{
SCROLLINFO info = {sizeof(SCROLLINFO),SIF_ALL};
pclScrollBar-> GetScrollInfo(& info,SB_CTL);

pclScrollBar-> GetScrollRange(& info.nMin,& info.nMax);
info.nPos = pclScrollBar-> GetScrollPos();

iScrollPositionPrevious = info.nPos;

switch(iSBCode)
{
case SB_TOP://滚动到顶部
iScrollPosition = VSCROLL_RANGE_MIN;
break;

case SB_BOTTOM://滚动到底部
iScrollPosition = VSCROLL_RANGE_MAX;
break;

case SB_ENDSCROLL://结束滚动
break;

case SB_LINEUP://向上滚动一行
if(iScrollPosition - VSCROLL_LINE> = VSCROLL_RANGE_MIN)
iScrollPosition - = VSCROLL_LINE;
else
iScrollPosition = VSCROLL_RANGE_MIN;
break;

case SB_LINEDOWN://向下滚动一行
if(iScrollPosition + VSCROLL_LINE <= VSCROLL_RANGE_MAX)
iScrollPosition + = VSCROLL_LINE;
else
iScrollPosition = VSCROLL_RANGE_MAX;
break;

case SB_PAGEUP://向上滚动一页
{
//获取页面大小
SCROLLINFO scrollInfo;
m_pclScrollBar-> GetScrollInfo(& scrollInfo,SIF_ALL);

if(iScrollPosition> VSCROLL_RANGE_MIN)
iScrollPosition = max(VSCROLL_RANGE_MIN,iScrollPosition - VSCROLL_PAGE);
break;
}

case SB_PAGEDOWN://向下滚动一页
{
//获取页面大小
SCROLLINFO scrollInfo;
m_pclScrollBar-> GetScrollInfo(& scrollInfo,SIF_ALL);

if(iScrollPosition< VSCROLL_RANGE_MAX)
iScrollPosition = min(VSCROLL_RANGE_MAX,iScrollPosition + VSCROLL_PAGE);
break;
}

case SB_THUMBPOSITION://滚动到绝对位置。当前位置在nPos
中提供case SB_THUMBTRACK://拖动滚动框到指定位置。当前位置在nPos
iScrollPosition = iPos中提供;
break;

默认值:
break;
}

if(iScrollPositionOriginal!= iScrollPosition)
{
m_pclScrollBar-> SetScrollPos(iScrollPosition);

CRect customerArea;
GetClientRect(clientArea);

CRect scrollbarArea;
m_pclScrollBar-> GetWindowRect(scrollbarArea);

CRect scrollArea(clientArea);
scrollArea.DeflateRect(0,0,scrollbarArea.Width(),0);
ScrollWindowEx(0,iScrollPositionOriginal - iScrollPosition,scrollArea,NULL,
NULL,NULL,SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
}
}
}


解决方案>

使用移动子窗口CWnd :: ScrollWindowEx 使用 SW_SCROLLCHILDREN 标志有问题:


如果 SW_SCROLLCHILDREN 标志,如果滚动子窗口的一部分,Windows将无法正确更新屏幕。位于源矩形外部的滚动子窗口的部分将不会被擦除,并且不会在其新目标中正确重绘。使用 DeferWindowPos Windows功能移动不完全位于lpRectScroll中的子窗口矩形。


解决方案是手动移动子窗口。 DeferWindowPos 具有与调用 SetWindowPos 用于多个窗口,但是优化为在单个调用中执行布局。



DeferWindowPos 这样可以减少视觉效果,需要保留新窗口属性的结构。它是通过调用 BeginDeferWindowPos 创建的,然后为每个窗口更新为调用 DeferWindowPos ,最后发送到系统以执行重新定位 EndDeferWindowPos 。下面的代码假设一个包含 cx cy 保持的数组中所有子控件的 CWnd * 水平和垂直偏移。它用于替换 ScrollWindowEx 的调用:

  [] = {m_pEdit,m_pButton,...}; 

HDWP hDwp = :: BeginDeferWindowPos(ARRAYSIZE(controls));

for(size_t index = 0; index< ARRAYSIZE(controls); ++ index){
//查找当前窗口位置
CRect wndRect;
controls [index] - > GetWindowRect(wndRect);
// DeferWindowPos需要客户端坐标,所以我们需要从屏幕坐标转换
ScreenToClient(wndRect);
//设置控件的新位置
hDwp = :: DeferWindowPos(hDwp,* controls [index],NULL,
wndRect.left + cx,wndRect.top + cy,0,0 ,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE |
SWP_NOZORDER);
}

//所有新的控制位置都已记录。现在执行操作
:: EndDeferWindowPos(hDwp);


I've implemented a CScrollBar in a Cwnd, but after scroll the controls on the window disappear. I've heard I could use DeferWindowPos someway, but I don't know how to do this. Any ideas?

CPanel::CPanel()
{
    CreateEx(WS_EX_CONTROLPARENT, _T("Static"), NULL, WS_CHILD | WS_TABSTOP | WS_BORDER, m_clRect, pwndParent, IDC_PANEL_FORM);
    ScrollBarInit();    
}

Creating scrollbar

void CPanel::ScrollBarInit()
{

    //Before this i calculate size of scrollbar and size of scrollarea
    m_pclScrollBar = new CScrollBar();
    m_pclScrollBar->Create(WS_CHILD | WS_VISIBLE | SBS_VERT | SBS_RIGHTALIGN, clRectScrollbar, this, IDC_SCROLLBAR_FORM);
    m_pclScrollBar->SetScrollRange(VSCROLL_RANGE_MIN, VSCROLL_RANGE_MAX);
    //After this I add scrollbar info

}

Handle message

void CPanel::OnVScroll(UINT iSBCode, UINT iPos, CScrollBar* pclScrollBar)
    {
        switch(pclScrollBar->GetDlgCtrlID())
        {
            case IDC_SCROLLBAR_FORM:
                ScrollBarScroll(iSBCode, iPos, pclScrollBar);
                break;
        }
    }

Scroll

void CPanel::ScrollBarScroll(UINT iSBCode, UINT iPos, CScrollBar *pclScrollBar)
    {
        int     iScrollPositionPrevious;
        int     iScrollPosition;
        int     iScrollPositionOriginal;

        iScrollPositionOriginal = m_pclScrollBar->GetScrollPos();
        iScrollPosition = iScrollPositionOriginal;

        if(m_pclScrollBar != NULL)
        {
            SCROLLINFO info = {sizeof( SCROLLINFO ), SIF_ALL};
            pclScrollBar->GetScrollInfo(&info, SB_CTL);

            pclScrollBar->GetScrollRange(&info.nMin, &info.nMax);
            info.nPos = pclScrollBar->GetScrollPos();

            iScrollPositionPrevious = info.nPos;

            switch(iSBCode)
            {
                case SB_TOP:            // Scroll to top
                    iScrollPosition = VSCROLL_RANGE_MIN;
                    break;

                case SB_BOTTOM:         // Scroll to bottom
                    iScrollPosition = VSCROLL_RANGE_MAX;
                    break;

                case SB_ENDSCROLL:      // End scroll
                    break;

                case SB_LINEUP:         // Scroll one line up
                    if(iScrollPosition - VSCROLL_LINE >= VSCROLL_RANGE_MIN)
                        iScrollPosition -= VSCROLL_LINE;
                    else
                        iScrollPosition = VSCROLL_RANGE_MIN;
                    break;

                case SB_LINEDOWN:       // Scroll one line down
                    if(iScrollPosition + VSCROLL_LINE <= VSCROLL_RANGE_MAX)
                        iScrollPosition += VSCROLL_LINE;
                    else
                        iScrollPosition = VSCROLL_RANGE_MAX;
                    break;

                case SB_PAGEUP:         // Scroll one page up
                {
                    // Get the page size
                    SCROLLINFO   scrollInfo;
                    m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);

                    if(iScrollPosition > VSCROLL_RANGE_MIN)
                        iScrollPosition = max(VSCROLL_RANGE_MIN, iScrollPosition - VSCROLL_PAGE);
                    break;
                }

                case SB_PAGEDOWN:       // Scroll one page down
                {
                    // Get the page size
                    SCROLLINFO   scrollInfo;
                    m_pclScrollBar->GetScrollInfo(&scrollInfo, SIF_ALL);

                    if(iScrollPosition < VSCROLL_RANGE_MAX)
                        iScrollPosition = min(VSCROLL_RANGE_MAX, iScrollPosition + VSCROLL_PAGE);
                    break;
                }

                case SB_THUMBPOSITION:  // Scroll to the absolute position. The current position is provided in nPos
                case SB_THUMBTRACK:     // Drag scroll box to specified position. The current position is provided in nPos
                    iScrollPosition = iPos;
                    break;

                default:
                    break;
            }

            if(iScrollPositionOriginal != iScrollPosition)
            {
               m_pclScrollBar->SetScrollPos(iScrollPosition);

               CRect clientArea;
               GetClientRect(clientArea);

               CRect scrollbarArea;
               m_pclScrollBar->GetWindowRect(scrollbarArea);

               CRect scrollArea(clientArea);
               scrollArea.DeflateRect(0, 0, scrollbarArea.Width(), 0);
               ScrollWindowEx(0, iScrollPositionOriginal - iScrollPosition, scrollArea, NULL,
                       NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE);
            }
        }
    }

解决方案

Moving child windows using CWnd::ScrollWindowEx using the SW_SCROLLCHILDREN flag is problematic:

If the SW_SCROLLCHILDREN flag is specified, Windows will not properly update the screen if part of a child window is scrolled. The part of the scrolled child window that lies outside the source rectangle will not be erased and will not be redrawn properly in its new destination. Use the DeferWindowPos Windows function to move child windows that do not lie completely within the lpRectScroll rectangle.

The solution is to move child windows manually. DeferWindowPos has the same effect as calling SetWindowPos for multiple windows, but is optimized to perform the layout in a single call. This helps reduce visual artifacts, where controls appear to move relative to each other, until everything is settled.

DeferWindowPos requires a structure holding the new window properties. It is created calling BeginDeferWindowPos, then updated for each window with a call to DeferWindowPos, and finally sent off to the system to perform the repositioning with EndDeferWindowPos. The following code assumes an array containing CWnd*s of all child controls in an array, with cx and cy holding the horizontal and vertical offset. It is meant to replace the call to ScrollWindowEx:

CWnd* controls[] = { m_pEdit, m_pButton, ... };

HDWP hDwp = ::BeginDeferWindowPos( ARRAYSIZE( controls ) );

for ( size_t index = 0; index < ARRAYSIZE( controls ); ++index ) {
    // Find the current window position
    CRect wndRect;
    controls[index]->GetWindowRect( wndRect );
    // DeferWindowPos requires client coordinates, so we need to convert from screen coords
    ScreenToClient( wndRect );
    // Set the control's new position
    hDwp = ::DeferWindowPos( hDwp, *controls[index], NULL,
                             wndRect.left + cx, wndRect.top + cy, 0, 0,
                             SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE |
                             SWP_NOZORDER );
}

// All new control positions have been recorded. Now perform the operation
::EndDeferWindowPos( hDwp );

这篇关于在使用ScrollWindowEx滚动Cwnd后,控件将消失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆