3
votes

I am a beginner in MFC. I have a dialog embedded in a property sheet. Since the dialog is bigger than property sheet, some portion get cropped.

So I am planning to add a vertical scrollbar. I have tried two ways.

  1. Added a scrollbar control from the toolbox in the dialog itself.

Created a control variable.

DDX_Control(pDX, IDC_SCROLLBAR, m_ctlScrollBar);

Added message map as below:

ON_WM_VSCROLL(IDC_SCROLLBAR,OnVScroll)

Added below code in OnInitDialog():

SCROLLINFO ScrollInfo;
ScrollInfo.cbSize = sizeof(ScrollInfo);     
ScrollInfo.fMask = SIF_ALL;                 
ScrollInfo.nMin = 0;                        
ScrollInfo.nMax = 100;                      
ScrollInfo.nPage = 40;                      
ScrollInfo.nPos = 50;                       
ScrollInfo.nTrackPos = 0;                   
m_ctlScrollBar.SetScrollInfo(&ScrollInfo,TRUE);

OnVScroll() function overridden as below:

void CFeesPage::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    SCROLLINFO ScrollInfo;
    m_ctlScrollBar.GetScrollInfo(&ScrollInfo); 

    switch (nSBCode)
    {
        case SB_BOTTOM:         //Scrolls to the lower right. 
        break;

        case SB_ENDSCROLL:      //Ends scroll. 
        break;

        case SB_LINEDOWN:       //Scrolls one line down. 
        m_ctlScrollBar.SetScrollPos(m_ctlScrollBar.GetScrollPos() + 1,TRUE);
        break;

        case SB_LINEUP:         //Scrolls one line up. 
        m_ctlScrollBar.SetScrollPos(m_ctlScrollBar.GetScrollPos() - 1,TRUE);
        break;

        case SB_PAGEDOWN:       //Scrolls one page down. 
        m_ctlScrollBar.SetScrollPos(m_ctlScrollBar.GetScrollPos() + ScrollInfo.nPage, TRUE);
        break;

        case SB_PAGEUP:         //Scrolls one page up. 
        m_ctlScrollBar.SetScrollPos(m_ctlScrollBar.GetScrollPos() - ScrollInfo.nPage, TRUE);
        break;

        case SB_THUMBPOSITION:
        break;

        case SB_THUMBTRACK:
        m_ctlScrollBar.SetScrollPos(nPos, TRUE);
        break;

        case SB_TOP:            //Scrolls to the upper left. 
        break;
    }
}

In this case scrollbar moving, but child controls doesn't?

  1. On another way, I have enabled scrollbar control for property sheet as below in OnInitDialog:

    CScrollBar* test = this->GetScrollBarCtrl(SB_VERT);
    

    Set SCROLLINFO as above.

The OnVScroll written as below:

void CSubTranSheet::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    SCROLLINFO ScrollInfo;
    GetScrollInfo(SB_VERT, &ScrollInfo); 

    switch (nSBCode)
    {
        case SB_BOTTOM:         //Scrolls to the lower right. 
            break;

        case SB_ENDSCROLL:      //Ends scroll. 
            break;

        case SB_LINEDOWN:       //Scrolls one line down. 
            SetScrollPos(SB_VERT, GetScrollPos(SB_VERT) + 1, TRUE);
            break;

        case SB_LINEUP:         //Scrolls one line up. 
            SetScrollPos(SB_VERT, GetScrollPos(SB_VERT) - 1, TRUE);
            break;

        case SB_PAGEDOWN:       //Scrolls one page down. 
            SetScrollPos(SB_VERT, GetScrollPos(SB_VERT) + ScrollInfo.nPage, TRUE);
            break;

        case SB_PAGEUP:         //Scrolls one page up. 
            SetScrollPos(SB_VERT, GetScrollPos(SB_VERT) - ScrollInfo.nPage, TRUE);
            break;

        case SB_THUMBPOSITION:  
            break;

        case SB_THUMBTRACK:    
            SetScrollPos(SB_VERT, nPos, TRUE);
            break;

        case SB_TOP:            //Scrolls to the upper left. 
            break;
    }
}

In this case also scrollbar moving but child dialog doesn't?

Please help me on this. I am not sure which method is right. Thanks in advance.

1
@Andrew Truckle... Thanks for updating the question..Prasanth
Here a look here: codeproject.com/Articles/3895/Scrollable-PropertyPage. I notice that you are not calling the base class handler at the bottom: CPropertySheet::OnVScroll(nSBCode, nPos, pScrollBar);. Although, looking at the above tutorial you need to do this on the page and not the sheet.Andrew Truckle
@AndrewTruckle The article in the link also working fine. Thanks for providing the link.Prasanth

1 Answers

2
votes

PropertySheet will pick the largest page dialog and it will resize itself so that all of the page dialogs are shown. Scrolling is not needed unless there is override for PropertySheet's size, or additional controls have been added in CMyPropertyPage::OnInitDialog

Moreover, the end-user's screen may have lower resolution, in which case parts of the propertysheet will be obscured. You just have to make smaller dialog pages, no taller than 1000 pixels, or about 300 dialog points.

The code shown in question is an attempt to update the scroller. In addition to updating the scroller, you have to scroll the dialog itself.

The link from @AndrewTruckle shows how to use ScrollWindow to achieve this.

Alternatively you can manually move all the child controls as shown below. This is somewhat easier because you can resize the dialog and adjust the scroller range without worrying about the child controls' alignment.

#include <map>

class CMyPropertyPage : public CPropertyPage
{
    std::map<CWnd*, CRect> rc_children;
    CRect rc_max;
    void OnSize(UINT flags, int cx, int cy);
    void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
    DECLARE_MESSAGE_MAP()
};

void CMyPropertyPage::OnSize(UINT flags, int cx, int cy)
{
    CPropertyPage::OnSize(flags, cx, cy);
    CRect rc;
    if(!rc_max.bottom)
    {
        //initialize once:
        for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
        {
            //save the rectangles for all child controls
            p->GetWindowRect(&rc);
            ScreenToClient(&rc);
            rc_children[p] = rc;

            //find the lowest point in dialog
            if(rc.bottom > rc_max.bottom)
                rc_max.bottom = rc.bottom;
        }
    }

    GetClientRect(&rc);
    SCROLLINFO info = { sizeof(info) };
    info.fMask = SIF_ALL;
    info.nMin = 0;
    info.nMax = (rc_max.bottom + 100); //100 pixels below the lowest control
    info.nPage = rc.bottom;
    SetScrollInfo(SB_VERT, &info, TRUE);
}

void CMyPropertyPage::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    CPropertyPage::OnVScroll(nSBCode, nPos, pScrollBar);
    SCROLLINFO info = { sizeof(SCROLLINFO) };
    GetScrollInfo(SB_VERT, &info, SIF_ALL);

    //update scroller
    switch(nSBCode)
    {
    case SB_LEFT: info.nPos = info.nMin; break;
    case SB_RIGHT: info.nPos = info.nMax; break;
    case SB_LINELEFT: info.nPos--; break;
    case SB_LINERIGHT: info.nPos++;  break;
    case SB_PAGELEFT: info.nPos -= info.nPage; break;
    case SB_PAGERIGHT: info.nPos += info.nPage; break;
    case SB_THUMBPOSITION: info.nPos = info.nTrackPos; break;
    case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
    }
    SetScrollInfo(SB_VERT, &info, TRUE);

    if(info.nPos < 0 || info.nPos > rc_max.bottom)
        return;

    //find how many child controls we have
    int count = 0;
    for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
        count++;

    //go through all child controls and move them:
    HDWP hdwp = BeginDeferWindowPos(count);
    for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
    {
        CRect rc;
        p->GetWindowRect(&rc);
        ScreenToClient(&rc);
        if(rc_children.find(p) != rc_children.end())
        {
            int y = info.nPos - rc_children[p].top;
            DeferWindowPos(hdwp, p->m_hWnd, NULL, rc.left, -y, 0, 0,
                SWP_NOSIZE | SWP_NOACTIVATE);
        }
    }
    EndDeferWindowPos(hdwp);
}

BEGIN_MESSAGE_MAP(CMyPropertyPage, CPropertyPage)
    ON_WM_VSCROLL()
    ON_WM_SIZE()
END_MESSAGE_MAP()