Re: Scrollbar thumbtrack doesn't scroll all the way
My apologies that this will sound harsh. Rather, you have helped me
numerous times, and I do value your input. But there's no way to
answer your comments without sounding harsh.
1. This is code that is not complete. It has problems, it has
issues. Hence why I'm posting here about problems. I haven't gone
over it and cleaned it up.
2. It uses things I don't know very well, so my first and primary
goal is to learn how it operates, then I can clean it. 80% of the
code here is, in fact, from a Microsoft KB post. Given that there is
a problem in it, my time and focus has been on fixing the code and in
understanding the interactions, not in immediately rewriting the
code. For instance, the difference between MoveWindow and
SetWindowPos in regards to an embedded child window. You should have
seen the code before I figured that out...
3. You assume a lot about this application. In truth this is an
emulation (legal, business emulation for simulation.). I emulate the
application I am emulating. All of these hardcoded values are taken
from this. This should never be in DLU's, percentages, or anything
else. It is precise because it is meant to be. I didn't state this,
and while it is true that this code is rough, it was posted here so
that anyone else could duplicate my problem, no matter how junky or
ugly my code is. You've yelled at me before for cleaning up code and
the critical piece not being posted.
(oh, and I DO set the font. That was in the initialization that has
nothing to do with window sizes and scrolling)
"If you aren't changing the top or left, why do you bother using
them? Just put 0,0 there
and use SWP_NOMOVE"
--Alright. That is my code, written while I was trying to get the
scrollbar to work at ALL. I'll fix that.
"It is not clear why you have a class variable for the rectangle.
Note also that OnSize is
called many, many times during the creation of a window, and the
m_rect, which is not set
until OnInitDialog, will not have a valid value at those times."
--Well, the KB post the code was obtained from did, but that is not
the reason it is still around. Rather, I noticed in the OnSize() that
windows was forcing the window to 1030 pixels tall, presumably due to
the size of the monitor. Or something. I couldn't find any
documentation on it. The rectangle is retained because my window is
nearly twice this size (though obviously not to the user), and I need
the real size for the calculations.
"(OnSize)* -> OnInitDialog -> (OnSize)*"
--That's a perfectly fine regular expression. I don't understand why
you need to correct it. Indeed, I've noticed this behavior, but aside
from mentally noting it, I have not messed with it, as the end result
of the KB code is still the same. The final size is what the user
sees. The size of the scroll bar in between is largely irrelevant to
the user (and me, at this stage in development, when I am still taking
a virtual hacksaw to my code), as the window is shown AFTER it is
initialized -- at which point the final size is already set.
"But what is this computing? The correct value is to use nPos. The
nPos value is an
absolute scroll position. I don't understand why you are using a
global m_iScrollPos
value anyway. You can get the scroll position directly from the
control."
--Actually I ran into difficulties when doing this. GetScrollPos was
returning the position from before the tracking took place, which was
incorrect for the scroll operation done at the bottom of the routine.
(If I was to switch the logic to what you suggested later, then yes,
the nPos would be fine. For a Delta operation though, GetScrollPos
and nPos fail miserably) Also, I haven't posted header files, but
m_iScrollPos isn't a global, it's a member variable, if that helps (m_
stands for member variable, in this notation).
I appreciate your input, but the majority of your comments are
directed at analyzing the Microsoft KB posting. This will undoubtedly
help me to clean the code up, once the issue is resolved. However,
I'm not that far along, as the issue is still there. I have been
temporarily able to "resolve" the issue by adding an extra page
(logically) to what Windows sees on the scrollbar.
In the OnSize Handler
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL; // SIF_ALL = SIF_PAGE | SIF_RANGE
| SIF_POS;
si.nMin = 0;
si.nMax = m_rect.Height() - m_iCurHeight + 81;
si.nPage = si.nMax/20;
si.nPos = 0;
SetScrollInfo(SB_VERT, &si, TRUE);
Because of the backwards-ness of my code in the OnVScroll, this
actually works, because the scrolling from the linedown and other
events are based on the REAL size of the window, not the expanded.
This is a hack of a workaround, and I'm no closer to solving the
issue.
Thanks for the response, and I am sorry if it comes out sounding
harsh. It's not meant to be.
~Scoots
On Nov 23, 5:16 pm, Joseph M. Newcomer <newco...@flounder.com> wrote:
See below...
On Mon, 23 Nov 2009 13:06:32 -0800 (PST), Scoots <linkingf...@msn.com> wrote:
Part of me feels rather dumb for asking this, but everything seems to
work smoothly except for one scrolling action.
I have a dialog with two embedded child dialogs within it. The top
dialog has a scrolling interface. The parent dialog is roughly 640 *
540 pixels, with the top child dialog being 640*388. The ACTUAL size
of the top child is 640*2008 (strange application, but YES, that is
intentional. It has twenty "elements" which include a couple of
statics and a picture control. So you'll see a lot of "20"'s floating
around, that's why.).
Using information here:
http://support.microsoft.com/kb/262954
I found I had to tweak it a bit to get it correct, as my Windows
seemed to want to cap my window to 1030 tall, roughly the vertical
pixel count of my screen, I guess.
Parent dialog:
BOOL C4IN1RegDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_TopDlg.Create(C4IN1RegTopDialog::IDD, this);
m_BottomDlg.Create(C4IN1RegBottomDialog::IDD, this);
m_TopDlg.ShowWindow(SW_SHOW);
CRect rc;
GetWindowRect(rc);
MoveWindow(rc.left, rc.top, 648, 530,true);
m_TopDlg.MoveWindow(0,0,640, 388, true);
****
These hardwired numbers are a bit scary. I suppose if you never, ever change the machine
you are on, or your monitor, or your display card, or the resolution you use, they might
make sense, but overall, any code like this is so amazingly fragile that it cannot be
expected to run on any other machine in the known universe. Why do you think these
numbers, which can run on only your machine, with your current display, your current
display card, your current display resolution, your current default font, your current
display driver, with the current version of the OS with the current set of hotfixes, is
going to work even *next week*?
You have to replace these values by meaningful computations.
It is not very practical to create a window that is larger than the current screen
resolution. Since this can change at any time (for example, you buy a new monitor, move
it to your laptop, etc.), all hardwired constants are suspect.
****
m_BottomDlg.ShowWindow(SW_SHOW);
m_BottomDlg.MoveWindow(0,390,640, 120, true);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
Top dialog (bottom is fine, so I'll avoid code-spammage)
BOOL C4IN1RegTopDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_iScrollPos = 0;
m_rect.top = 0;
m_rect.left = 0;
m_rect.right = 617;
****
See previous comments about hardwired integers
****> m_rect.bottom = iOffsetFromTop + ((iItem) * (iOffsetBetween +
iSizeOfImageY));
SetWindowPos(NULL, m_rect.left, m_rect.top, m_rect.right,
m_rect.bottom, SWP_NOZORDER);
****
If you aren't changing the top or left, why do you bother using them? Just put 0,0 there
and use SWP_NOMOVE
****
UpdateWindow();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void C4IN1RegTopDialog::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
m_iCurHeight = cy;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL; // SIF_ALL = SIF_PAGE | SIF_RANGE | SIF_POS;
si.nMin = 0;
si.nMax = m_rect.Height() - m_iCurHeight;
****
It is not clear why you have a class variable for the rectangle. Note also that OnSize is
called many, many times during the creation of a window, and the m_rect, which is not set
until OnInitDialog, will not have a valid value at those times.
Either you have to have a boolean that says if the rectangle is valid, or compute the
value at this point from basic properties of the window. Do not store it as you have
done, and assume that the sequence is
OnInitDialog -> OnSize
when the actual sequence is
(OnSize)* -> OnInitDialog -> (OnSize)*
where OnSize* means "zero or more times" but in fact there will be several calls on
OnSize, many of which occur before OnInitDialog. As a regular expression, it might be
better expressed as
(OnSize)+ -> OnInitDialog -> (OnSize)+
where Onsize+ means "One or more calls"
*****
si.nPage = si.nMax/20;
si.nPos = 0;
SetScrollInfo(SB_VERT, &si, TRUE);
}
void C4IN1RegTopDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*
pScrollBar)
{
// TODO: Add your message handler code here and/or call default
int nDelta;
int nMaxPos = m_rect.Height() - m_iCurHeight;
switch (nSBCode)
{
case SB_LINEDOWN:
if (m_iScrollPos >= nMaxPos)
return;
nDelta = min(nMaxPos/20,nMaxPos-m_iScrollPos);
//nDelta = min(max(nMaxPos/20,5),nMaxPos-m_iScrollPos);
break;
case SB_LINEUP:
if (m_iScrollPos <= 0)
return;
nDelta = -min(nMaxPos/20,m_iScrollPos);
//nDelta = -min(max(nMaxPos/20,5),m_iScrollPos);
break;
case SB_PAGEDOWN:
if (m_iScrollPos >= nMaxPos)
return;
nDelta = min(nMaxPos/20,nMaxPos-m_iScrollPos);
//nDelta = min(max(nMaxPos/20,5),nMaxPos-m_iScrollPos);
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
{
nDelta = (int)nPos - m_iScrollPos;
//int iPos = GetScrollPos(SB_VERT);
//nDelta = (int)iPos - m_iScrollPos;
}
****
But what is this computing? The correct value is to use nPos. The nPos value is an
absolute scroll position. I don't understand why you are using a global m_iScrollPos
value anyway. You can get the scroll position directly from the control.
****> break;
case SB_PAGEUP:
if (m_iScrollPos <= 0)
return;
nDelta = -min(nMaxPos/20,m_iScrollPos);
//nDelta = -min(max(nMaxPos/20,5),m_iScrollPos);
break;
default:
return;
}
m_iScrollPos += nDelta;
****
I now see why you are using a delta. But this is not a natural way to express this.
Instead, normal scrollbar control code stores the new position, so instead of computing
the delta in the thumbtrack/thumbposition, you would just set the new position to be nPos;
in other cases you would not compute the value, but compute the new position.
As such, this code is hard to read and to understand.
****
SetScrollPos(SB_VERT,m_iScrollPos, TRUE);
ScrollWindow(0,-nDelta);
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}
Obviously this is eliminating a LOT of initialization, loading, etc.
Now, this works perfectly when the user presses on the ends of the
scrollbar. The dialog scrolls smoothly and it's all wonderful. But
when the user clicks and drags the thumbtrack, the SB_THUMBTRACK event
seems to not scroll all the way. It goes almost all the way, but it
seems like the last "tick" isn't there. I get all but the last
element. Then if the user taps the button, it scrolls a little more,
showing everything. If the user clicks the thumbtrack, it snaps back
to that "tick" before the end. I've stepped throught he code, and
when the user taps the thumbtrack, I get a position of 1592, as
opposed to 1620 that is the max (remember, my windowsize is 388, so
2008-388=1620, which I do get with the buttons).
****
I haven't looked at the code in detail, but it looks like a classic off-by-one-unit error
somewhere.
I'd simplify this code and get rid of nearly all those member variables. Compute values
as you need them, where you need them, from first principles.
joe
****
Why isn't this last "tick" there? And how can I get it? The
thumbtrack size is dependent upon my nPage count, but I've tried to
monkey with that and that doesn't change the behaviour. It always
seems to be one tick short.
Thanks for the help,
~Scoots
Joseph M. Newcomer [MVP]
email: newco...@flounder.com
Web:http://www.flounder.com
MVP Tips:http://www.flounder.com/mvp_tips.htm- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -