I am working on dialog based MFC application in WinCE.
I created few controls in a dialog and scrolled down.
When i scroll up again, the controls in the first screen got disappeared.
Controls getting created in OnInitDialog() like below at coordinates (50,10)
test->Create(_T("Title"), WS_CHILD|WS_VISIBLE, CRect(50,10,200,40), this, ID_TITLE);
Scroll handling i am doing in OnVScroll() like below
switch(nSBCode)
{
case SB_LINEDOWN:
{
if(nPos < max)
{
ScrollWindowEx(0, SCROLLDOWN_LINE_STEPSIZE, CRect(0,0, rect.right - 25, rect.bottom), NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
pScrollBar->SetScrollPos(nPos - SCROLLDOWN_LINE_STEPSIZE); //nPos+10
}
break;
}
case SB_LINEUP:
{
if(nPos > min)
{
ScrollWindowEx(0, SCROLLUP_LINE_STEPSIZE, CRect(0,0, rect.right - 25, rect.bottom), NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
pScrollBar->SetScrollPos(nPos - SCROLLUP_LINE_STEPSIZE); //Ex: nPos-10
}
break;
}
default:
printf("Notimplemented");
break;
}
I am handling Scroll down and scroll up.
While scrolling down, all the controls in Dialog are shown.
But while scrolling up, the controls at the top got disappeared.
What's going on ?
Should i implement OnPaint() method for drawing the controls each time i scroll ?
If so, how ?
I have the following code that works fine. I hope it will help you.
LRESULT CMyWindow::OnVScroll( UINT code, UINT position )
{
SCROLLINFO info = { sizeof( SCROLLINFO ), SIF_ALL };
GetScrollInfo( m_wnd, SB_VERT, &info );
int previous_pos = info.nPos;
switch( code )
{
case SB_TOP:
info.nPos = 0;
break;
case SB_BOTTOM:
info.nPos = info.nMax - info.nPage;
break;
case SB_LINEDOWN:
info.nPos = min( info.nPos + 1, info.nMax - (int)info.nPage );
break;
case SB_LINEUP:
info.nPos = max( info.nPos - 1, info.nMin );
break;
case SB_PAGEDOWN:
info.nPos = min( info.nPos + (int)info.nPage, info.nMax - (int)info.nPage );
break;
case SB_PAGEUP:
info.nPos = max( info.nPos - (int)info.nPage, info.nMin );
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
info.nPos = position;
break;
}
int offset = previous_pos - info.nPos;
if( offset != 0 )
{
ScrollWindowEx( m_wnd, 0, offset, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE );
SetScrollPos( m_wnd, SB_VERT, info.nPos, FALSE );
}
return 0L;
}
From the ScrollWindowEx docs:
dy Specifies the amount, in
device units, of vertical scrolling.
This parameter must be a negative
value to scroll up.
I bolded the relevant phrase.
Related
How to use the flag SW_SMOOTHSCROLL of the WinApi ScrollWindowEx?
SW_SMOOTHSCROLL
Scrolls using smooth scrolling. Use the HIWORD portion of the flags parameter to indicate how much time, in milliseconds, the smooth-scrolling operation should take.
Independent of what value I input as the time 100 or 9999, it continues scrolling with the same speed, would like to ask if someone knows how to properly use this flag?
case WM_VSCROLL:
{
RECT rc;
int CurPos = scrollY;
int TemPos = CurPos;
switch (LOWORD(wParam))
{
case SB_TOP: CurPos = 0; break;
case SB_LINEUP: CurPos = (CurPos > 0)? CurPos - 1 : 0; break;
case SB_THUMBPOSITION: CurPos = HIWORD(wParam); break;
case SB_THUMBTRACK: CurPos = HIWORD(wParam); break;
case SB_LINEDOWN: CurPos = (CurPos < 240)? CurPos + 1 : 240; break;
case SB_BOTTOM: CurPos = 240; break;
case SB_ENDSCROLL: break;
}
SetScrollPos(hwnd, SB_VERT, CurPos, TRUE);
TemPos = CurPos - TemPos;
UINT flags;
flags = MAKELONG(SW_SCROLLCHILDREN | SW_SMOOTHSCROLL | SW_ERASE |
SW_INVALIDATE, 100);
ScrollWindowEx(hwnd, 0, -, NULL, NULL, NULL, NULL, flags);
break;
}
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.
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?
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()
I wish to use the scroll arrows present at both the ends of the scroll bar, to scroll the positions of the bar. As it is known that the scroll bars don't have the notifications as other controls have, so I am facing a problem.
My code for the scroll bar is as follows-
void CScrollBarExDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if(nSBCode==SB_THUMBPOSITION)
{
if(pScrollBar==&m_Scroll)
{
m_Edit=nPos;
m_Scroll.SetScrollPos(nPos);
}
}
UpdateData(FALSE);
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}
You have to modify the MSDN example slightly to work with a scroller control:
//add to message map
ON_WM_HSCROLL()
//initialize in OnInitDialog
SCROLLINFO info = { sizeof(SCROLLINFO) };
info.nMin = 0;
info.nMax = 100;
info.nPage = 1;
info.fMask = SIF_ALL;
m_Scroll.SetScrollInfo(&info, TRUE);
If info.nPage is greater than 1, it may throw off the range. Add this line to fix it:
info.nMax += info.nPage - 1;
Add overload:
void CMyDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
if (pScrollBar == &m_Scroll)
{
//get scrollbar information
SCROLLINFO info;
m_Scroll.GetScrollInfo(&info, SIF_ALL);
int pos = info.nPos;
//calculate the new position of scroll box
switch (nSBCode)
{
case SB_LEFT: pos = info.nMin; break;
case SB_RIGHT: pos = info.nMax; break;
case SB_LINELEFT: pos--; break;
case SB_LINERIGHT: pos++; break;
case SB_PAGELEFT: pos -= info.nPage; break;
case SB_PAGERIGHT: pos += info.nPage; break;
case SB_THUMBPOSITION: pos = nPos; break;
case SB_THUMBTRACK: pos = nPos; break;
}
//make sure the new position is within range
if (pos < info.nMin) pos = info.nMin;
//adjust the max value, incase we had changed it earlier in OnInitDialog
int max = info.nMax - info.nPage + 1;
if (pos > max) pos = max;
//set the new position
m_Scroll.SetScrollPos(pos);
}
}
basically I have a scrollbar in a custom window where we could add other controls like buttons, textboxes and so and so forth, now here is the problem I have created the scrollbar alright it scrolls down and up, but the problem is that the maximum scroll should be specified which then blocks it to scroll more than that, but there could be as much control as the user want, which for me it would not be known, I mean like to tell the problem to be more like a textbox's built-in scroll bar when you specify it with WM_VSCROLL OR HSCROLL, and then give the user the ability to scroll as much as you type on the textbox, that's exactly what I want.
here is the code so far I have been able to go through:-
Code for Scrollbar:-
case WM_LBUTTONDOWN:
{
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = 0;
si.nTrackPos = 0;
GetScrollInfo(hwnd, SB_VERT, &si);
break;
}
case WM_VSCROLL:
{
auto action = LOWORD(wParam);
HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 50;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 50;
} else if (action == SB_PAGEUP) {
GetClientRect(hwnd, &r);
pos = g_scrollY - r.bottom;
} else if (action == SB_PAGEDOWN){
GetClientRect(hwnd, &r);
pos = g_scrollY + r.bottom;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
auto hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
case WM_SIZE:
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = MAX_RANGE;
if(si.nPos > 100)
si.nMax = 5000;
if(SB_THUMBPOSITION == 100){
si.nMax = MAX_RANGE;}
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
break;
}
as you can see si.nMax value is the maximum amount of lines it is going to be able to scroll, but I want it to be as much as there are controls.
You must know how many controls are in your window, so you can set nMax member in the SCROLLINFO structure to whatever value you want and call SetScrollInfo() whenever new controls are added.
Right now I have a window with horizontal ad vertical scrollbars. I use these parameters to initialize it.
//Set OGL Frame scroll bar
SCROLLINFO inf;
inf.cbSize = sizeof(SCROLLINFO);
inf.fMask = SIF_PAGE | SIF_POS;
inf.nPage = 20;
inf.nPos = 30;
It creates them in the center and I like their size, but when I scroll I multiply by 50 which creates chopiness. How could I add more resolution to the bars and still keep the same thumb size. Is there a way I can calculate the size and position of the bar based on the above parameters?
Thanks
Right, here's my solution even though one is already accepted.
Everytime I have issues with the windows controls I use Controlspy to experiment with them. Controlspy also lists all the different messages that can be sent to the different controls. Find one that is similar to what you are trying to do and check that specific message on MSDN.
Here is my old chunk of code that handles scrolling events. You could use the same "customized" approach.
Note that it's (probably) not the best way of solving this problem, but still a working one.
case WM_VSCROLL:
{
TEXTHANDLER * handler = ((TEXTHANDLER *)GetProp(hWnd, "TEXTHANDLER"));
BOOL needInvalidation = TRUE;
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &si);
switch (LOWORD(wParam))
{
case SB_LINEUP:
si.nPos -= 1;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_LINEDOWN:
si.nPos += 1;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_PAGEUP:
si.nPos -= handler->renderer->cyCount;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_PAGEDOWN:
si.nPos += handler->renderer->cyCount;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
// Set new text renderer parameters
handler->renderer->yPos = si.nPos;
if (needInvalidation) InvalidateRect(hWnd, NULL, FALSE);
return 0;
}