Get rectangle of scroll thumb!

Ah hello.

Quite a simple query this one, I hope:

I have a scrollbar derived from CScrollbar.  In this case, it is a vertical scrollbar.  I want to get the window rectangle of the scroll thumb (the bit we can drag up and down/left and right) so I can paint a colour over it.  (See http://msdn.microsoft.com/en-us/library/ms997557.aspx#scroll32_topic2)

I can get the rect of the, say, up arrow, like this:

CRect rcWnd, rcArrow;
// Get rect of whole scrollbar
GetWindowRect ( &rcWnd ); ScreenToClient ( &rcWnd );

// Get "height" of scroll button
int nHeight = GetSystemMetrics ( SM_CYVSCROLL );

rcArrow.left = 0;
rcArrow.right = rcWnd.right;      
rcArrow.bottom = nHeight;
rcArrow.top = 0;

And similar methods for the horizontal scrolling.

Once I have the arrow rectangles and the thumb rectangle, I can get the rectangle of shafts above and below the thumb

Can someone help by providing a code sample please?

TIA
LVL 19
mrwad99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

romanmCommented:
you need to combine
SM_CXHTHUMB or SM_CYVTHUMB with scroll bar area without the arrows and scroll current position.

lets take vertical scroll for example,
CRect rcWnd;
// Get rect of whole scrollbar
GetWindowRect ( &rcWnd ); ScreenToClient ( &rcWnd );

// Get "height" of scroll button
int nHeight = GetSystemMetrics ( SM_CYVSCROLL );

rcWnd.bottom -= nHeight;
rcWnd.top += nHeight;
// adjust width,
int nWideBorder = (rcWnd.right - rcWnd.left - GetSystemMetrics (SM_CXVSCROLL)) / 2;
rcWnd.left += nWide;
rcWnd.right -= nWide;

// get thumb height
int nHeightTh = GetSystemMetrics ( SM_CYVTHUMB );

// get thumb rect
// Get the minimum and maximum scroll-bar positions.
int minpos;
int maxpos;
GetScrollRange(&minpos, &maxpos);
// Get the current position of scroll box.
int curpos = GetScrollPos();
// maxpos - minpos == range,
// (rcWnd.Height()-nHeightTh) / (maxpos - minpos) == one scroll unit
CRect rcThumb = rcWnd;
rcThumb.top = ( (rcWnd.Height()-nHeightTh) / (maxpos - minpos) ) * curpos ;
rcThumb.bottom = rcThumb.top + nHeightTh;

// you may need to tweek a bit, but this is it.
0
mrwad99Author Commented:
Thank `you for the reply.  Unfortunately, the code did not work.  I always get the height returned as 16; rest of the dimensions are equally curious (see image)

Please if you would download this sample project (~50KB) to see if you can see what the problem is: http://www.yousendit.com/download/Y2ovTmZhV3JOQnhFQlE9PQ

TIA

EG.JPG
0
romanmCommented:
ok,
       int nHeightTh = GetSystemMetrics ( SM_CYVTHUMB );
is actually the height of the thumb, which is (on more stylish bars) the small icon in the center (sometimes looks like three lines)

to make a better calculation we need to get a new value, scroll limit, which is the max offset possible with the current scroll bar size + scroll bar range,
so take out the
       int nHeightTh = GetSystemMetrics ( SM_CYVTHUMB );

and put
      int _maxpos;
      _maxpos = GetScrollLimit();
      nHeightTh = (( double )(maxpos - _maxpos) / ( double )maxpos) * rcWnd.Height();

/* just before the end, */

      // maxpos - minpos == range,
      // (rcWnd.Height()-nHeightTh) / (maxpos - minpos) == one scroll unit
      rcThumb = rcWnd;
      rcThumb.top = dTop;
      rcThumb.bottom = rcThumb.top + nHeightTh;


see code snippet
	//lets take vertical scroll for example,
	CRect rcWnd;
	// Get rect of whole scrollbar
	GetWindowRect ( &rcWnd ); ScreenToClient ( &rcWnd );
 
	// Get "height" of scroll button
	int nHeight = GetSystemMetrics ( SM_CYVSCROLL );
 
	rcWnd.bottom -= nHeight;
	rcWnd.top += nHeight;
	// adjust width,
	int nWide = (rcWnd.right - rcWnd.left - GetSystemMetrics (SM_CXVSCROLL)) / 2;
	nWide = max(1, nWide);
	rcWnd.left += nWide;
	rcWnd.right -= nWide;
 
	// get thumb rect
	// Get the minimum and maximum scroll-bar positions.
	int minpos;
	int maxpos;
	GetScrollRange(&minpos, &maxpos);
	// Get the current position of scroll box.
	int curpos = GetScrollPos();
	int nD = maxpos - minpos;
	if ( nD < 0 ) nD = 1;
	double dRatio = rcWnd.Height() / ( double ) nD;
	double dTop = dRatio * curpos;
 
	int _maxpos;
	_maxpos = GetScrollLimit();
	int nHeightTh = (( double )(maxpos - _maxpos) / ( double )maxpos) * rcWnd.Height();
 
	// maxpos - minpos == range,
	// (rcWnd.Height()-nHeightTh) / (maxpos - minpos) == one scroll unit
	rcThumb = rcWnd;
	rcThumb.top = dTop;
	rcThumb.bottom = rcThumb.top + nHeightTh;

Open in new window

0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

DanRollinsCommented:
With modern scrollbars, the size of the thumb is proportional to the amount of data that is visible in the scrollable portion of the window.  For instance, if 10% of the data is visible, then the thumb will occupy 10% of the scrollbar shaft.
The easiest way to obtain the correct values is to use the
    GetScrollInfo Function
    http://msdn.microsoft.com/en-us/library/bb787583(VS.85).aspx
The SCROLLINFO structure will be populated with everything you need to know.  For instance put this in an AppWizard-generated SDI with a RichEdit View:
 void CMainFrame::OnAppAbout()
{
     // MessageBox("hi");
     CWnd*p = GetActiveView();
     SCROLLINFO rSI;
     p->GetScrollInfo(SB_VERT, &rSI, SIF_ALL );
}
Breakpoint after the GetScrollInfo call.  Try a number of variations:  Tall window, short window, window showing a large file, window showing a small file.   You'll see that the value in rSI->nMax shows the height of the complete document, rSI->nPage shows the height of the visible area,  rSI->nPos shows the location of the top of the thumb.  The height of the thumb itself is nPage -- but with certain limits (once lower than a certain minimum, it stays that size and then disappears altogether and it also disappears when the entire file is visible.)
0
mrwad99Author Commented:
Dan, thanks for the comment, but that does not work.  The call to GetScrollInfo() always fails.  I have read the documentation and cannot see why this is: I thought initially that the scrollbar belongs to the mainframe, not the view, so tried that, and it still failed.

Any ideas?

romanm; thanks - that appears to work.   Will wait for any comments from Dan before closing
0
DanRollinsCommented:
It should be easy enough to find out which window owns the scrollbar that you are interested in painting!  Use Spy++ if needed.  
If you are using the Win32 API rather than the MFC function, be sure to populate
    SCROLLINFO.cbSize
0
mrwad99Author Commented:
romanm: I am sorry but your code does not work.  It is ok when the thumb is at the top, but otherwise it gives incorrect results.  See below.

OK this is really getting annoying now.
Dan/romanm:
I got the SCROLLINFO approach working, and am playing with a rich edit SDI (see second screenshot).  GetScrollInfo() gives the same values as romanm gets via individual function calls.  Please take a look at the screenshot below.
As you can see, I have labelled up the dimensions.  My page is 242 "units" and my scrollbar is 243 pixels high.  OK then.
My first question is "What the heck units are the values in the SCROLLINFO?"  They don't appear to be pixels - the current position of the top of the scrollbar is "71" but I have measured it on the image (in red) and it is 48 pixels!
So, I try and get some sort of "ratio" going: SCROLLINFO units to pixels:
 static int nHeight = GetSystemMetrics ( SM_CYVSCROLL );
 SCROLLINFO rSI;
 VERIFY ( GetScrollInfo ( SB_VERT, &rSI ) );
 CRect rcWnd; GetClientRect ( rcWnd );
 double dRatio =  ( rcWnd.Height() - ( 2 * nHeight ) ) / ( double ) rSI.nPage;
dRatio is the ratio of pixels of the scrollbar (not including the up/down arrows, hence subtracting twice the height of the vertical scroll arrow) to "SCROLLINFO" units.  This works out as 0.871: so, .0871 pixels is one SCROLLINFO unit.
OK then.  It figures then that the top of the scrollbar is
 double dTop = dRatio * rSI.nPos;
~62 pixels.  Nope.  Still not right.
????
Please someone tell me what is going on, my head is spinning.
TIA

SDI.JPG
Problem.JPG
0
DanRollinsCommented:
The center of the thumb is the focal point for your measurements.  The thumb's size is really only an indication of the proportion of visible to not-visible data.   So the top and bottom will vary but the center will always be there.
0
mrwad99Author Commented:
>> The center of the thumb is the focal point for your measurements.

OK, so now we are after the *center* of the thumb.  There is no function/argument for getting that though, so how do you propose I get that?

Thanks.
0
DanRollinsCommented:
You have chosen a particularly difficult task.  Several articles indicate that one can't really "skin" a standard scrollbar and that the best route is to replace it entirely.  The source code here:

   Custom Scrollbar Library  ("Cool Scrollbars")
   http://www.codeproject.com/KB/dialog/coolscroll.aspx
may include all of the required calculations.  But be sure to read the bottom part, which describes some of the complications with skinning.
There are a number of other codeProject articles that are worth reading:
    http://www.codeproject.com/info/search.aspx?artkw=scrollbar&sbo=kw
0
DanRollinsCommented:
Incidently, regarding your picture "Problem.jpg":
In my RTF test in the attached image, scrollinfo indicates:
  nPage=250
  nMax=2288
  nPos=800
The size of the components add up to 251 (why not 250? not sure one pixel at bottom?)
The page/max ratio is 10.9% (250/2288)
The thumb/page ratio is 10% (24/250)
The document is scrolled 34.9% (800/2288)
34.9% of nPage is 88 (.349*250)
The center of the thumb is at 88 ( 76+ (24/2) )
-=-=-=-=-=-=-=-=-=-==-=-
There is certain to be a lot of rounding up or down, depending upon how the Windows GUI programmers decided to go:  Yyou can't have "half of a pixel" in these types of things.  Also, the special cases -- such as short document in tall window and tall document in short window -- will have special handling.  To paint over an existing thumb, your only option would be to try many, many variations to see if you can reverse-engineer the programmers' methods.

RtfScroll.JPG
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
mrwad99Author Commented:
Thanks, I will look at these links, then close this question.
0
mrwad99Author Commented:
OK, points split based on effort.  Many thanks all :o)
0
mrwad99Author Commented:
OK, I have just got back to this, and have found that your calculations don't work,apart from in the example you showed.

Look at the screenshot below if you will, and the values that I have obtained via GetScrollInfo.

You can see that in the third image, *no way* is the document scrolled; there is not even a scrollbar visible!

This is ridiculous now.  There just does not appear to be any way to get the scrollbar location reliably.

Do you have anything to say on this Dan, do you want more points, or are you just as confused (and probably as sick of this) as I am?

1.JPG
2.JPG
3.JPG
0
DanRollinsCommented:
These are all special cases.  Either the window is taller than the document (1 and 3) so there is no thumb at all, or the thumb is "pinned" at the bottom and/or has reached its arbitrarily-selected maximum height (2).  In such cases, the programmers had to decide on how to handle the situation and there were a number of options available to them.  Since they do the calculations and they also draw the control, they can decide which option to use in which cases --  as long as they are internally consistant.
0
mrwad99Author Commented:
Ah right.  Thanks for that :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.