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.
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?
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.
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()