Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Get rectangle of scroll thumb!

Posted on 2008-11-10
16
Medium Priority
?
1,641 Views
Last Modified: 2013-11-20
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
0
Comment
Question by:mrwad99
  • 8
  • 6
  • 2
16 Comments
 
LVL 3

Assisted Solution

by:romanm
romanm earned 400 total points
ID: 22926677
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
 
LVL 19

Author Comment

by:mrwad99
ID: 22932023
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
 
LVL 3

Assisted Solution

by:romanm
romanm earned 400 total points
ID: 22934637
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 49

Expert Comment

by:DanRollins
ID: 22937817
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
 
LVL 19

Author Comment

by:mrwad99
ID: 22938425
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
 
LVL 49

Expert Comment

by:DanRollins
ID: 22943489
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
 
LVL 19

Author Comment

by:mrwad99
ID: 22952824
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
 
LVL 49

Expert Comment

by:DanRollins
ID: 22956462
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
 
LVL 19

Author Comment

by:mrwad99
ID: 22968325
>> 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
 
LVL 49

Expert Comment

by:DanRollins
ID: 22972202
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
 
LVL 49

Accepted Solution

by:
DanRollins earned 600 total points
ID: 22972299
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
 
LVL 19

Author Comment

by:mrwad99
ID: 22987328
Thanks, I will look at these links, then close this question.
0
 
LVL 19

Author Closing Comment

by:mrwad99
ID: 31515110
OK, points split based on effort.  Many thanks all :o)
0
 
LVL 19

Author Comment

by:mrwad99
ID: 23078705
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
 
LVL 49

Expert Comment

by:DanRollins
ID: 23080911
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
 
LVL 19

Author Comment

by:mrwad99
ID: 23084497
Ah right.  Thanks for that :)
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Kernel Data Recovery is a renowned Data Recovery solution provider which offers wide range of softwares for both enterprise and home users with its cost-effective solutions. Let's have a quick overview of the journey and data recovery tools range he…
Suggested Courses

575 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question