Click here to Skip to main content
15,887,350 members
Articles / Desktop Programming / MFC
Article

A CListBox with automatic HSCROLL maintenance

Rate me:
Please Sign up or sign in to vote.
4.50/5 (8 votes)
27 Jun 20013 min read 159K   1.7K   41   21
Taking the pain out of adding a horizontal scrollbar to a listbox.

A question that seems to come up about once a week in the newsgroup goes something like "I checked the horizontal scroll style in my ListBox, and I don't see any scrollbar. What can I do?"

Horizontal scrolling is poorly understood and even more poorly documented. In order for horizontal scrolling to work, you have to call the SetHorizontalExtent method and set the total width of the horizontal space being used by the entries in the ListBox. If the horizontal extent is larger than the client area of the listbox, the horizontal scrollbar will appear, providing you have selected the horizontal scroll style. You need both to be set to get the effect.

Unfortunately, this is hard to do in the parent window. It involves having to do the computation at every site where you add a string. This is not well object-oriented. So what I've done is create a new class derived from CListBox that incorporates this functionality automatically.

The way I do this is maintain a value which is the maximum width set thus far. Whenever a new string is added, I update the width. When strings are deleted I update the width. I do this by overriding the ResetContent, InsertItem, AddItem, and DeleteString methods.

Note that this works only for non-owner-drawn list boxes. For owner-drawn, it is somewhat easier because you can maintain it in the DrawItem handler.

You can download the sample code, but here's some excerpts. The sample code includes a complete project which demonstrates the scrolling. 

Note that to include this class in your project, you need to include the source file in your project, then delete the .clw file and re-invoke the ClassWizard to get it to see the new class (Microsoft used to allow the importation of classes directly, but this feature seems to have been deleted in the latest versions of Visual Studio). You can then create control variables using the CHListBox class directly in ClassWizard. If you don't do the rebuild of the .clw file, you will have to hand-edit your header file. If you don't know how to do this, check out my essay on Avoiding GetDlgItem.

Constructor

We need to initialize the width variable in the constructor:

CHListBox::CHListBox()
   {
    width = 0;
   }

AddString and InsertString

In the AddString and InsertString handlers, we call a common function to update the width:

int CHListBox::AddString(LPCTSTR s)
   {
    int result = CListBox::AddString(s);
    if(result < 0)
       return result;
    updateWidth(s);
    return result;
   }
int CHListBox::InsertString(int i, LPCTSTR s)
   {
    int result = CListBox::InsertString(i, s);
    if(result < 0)
       return result;
    updateWidth(s);
    return result;
   }

The updateWidth function is defined as

void CHListBox::updateWidth(LPCTSTR s)
    {
     CClientDC dc(this);

     CFont * f = CListBox::GetFont();
     dc.SelectObject(f);

     CSize sz = dc.GetTextExtent(s, _tcslen(s));
     sz.cx += 3 * ::GetSystemMetrics(SM_CXBORDER);
     if(sz.cx > width)
	 { /* extend */
	  width = sz.cx;
	  CListBox::SetHorizontalExtent(width);
	 } /* extend */
    }

The reason we add the 3*SM_CXBORDER factor is because we need to allow a bit of additional space to account for the (undocumented and inaccessible) margin that is used to draw the characters. This fudge factor appears to give the best result. To get the correct computation, we have to select the font that is set in the control into the DC.

ResetContent

The ResetContent method is trivial:

void CHListBox::ResetContent()
    {
     CListBox::ResetContent();
     width = 0;
    }

DeleteString

The DeleteString operation is expensive because we don't know if we have deleted the widest string. Consequently, we have to evaluate all the strings all over again. Since this can be a bit expensive if we keep calling updateWidth, so the functionality of DC creation and font selection have been moved back into the DeleteString. This could probably be expedited with some inline functions but the code is not very complex, so there seems to be little reason to not duplicated it.

int CHListBox::DeleteString(int n)
    {
     int result = CListBox::DeleteString(n);
     if(result < 0)
	 return result;
     CClientDC dc(this);

     CFont * f = CListBox::GetFont();
     dc.SelectObject(f);

     width = 0;
     for(int i = 0; i < CListBox::GetCount(); i++)
	 { /* scan strings */
	  CString s;
	  CListBox::GetText(i, s);
	  CSize sz = dc.GetTextExtent(s);
          sz.cx += 3 * ::GetSystemMetrics(SM_CXBORDER);
	  if(sz.cx > width)
	      width = sz.cx;
	 } /* scan strings */
     CListBox::SetHorizontalExtent(width);
     return result;
    }

The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this web site.
Copyright © 1999 CompanyLongName All Rights Reserved.
www.flounder.com/mvp_tips.htm

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Retired
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

Comments and Discussions

 
GeneralA little problem with owner drawn Pin
_kane_16-Nov-06 1:47
_kane_16-Nov-06 1:47 
GeneralRe: A little problem with owner drawn Pin
_Bids13-May-07 2:00
_Bids13-May-07 2:00 
GeneralRe: A little problem with owner drawn Pin
_kane_13-May-07 3:24
_kane_13-May-07 3:24 
GeneralRe: A little problem with owner drawn Pin
_Bids15-May-07 6:57
_Bids15-May-07 6:57 
GeneralRe: A little problem with owner drawn Pin
_Bids16-May-07 10:48
_Bids16-May-07 10:48 
GeneralSource with Windows-API functions [modified] Pin
Peter Wucherer26-Jul-06 2:26
Peter Wucherer26-Jul-06 2:26 
GeneralInteresting Pin
KaЯl7-Mar-06 4:18
KaЯl7-Mar-06 4:18 
GeneralRe: Interesting Pin
Joseph M. Newcomer7-Mar-06 5:54
Joseph M. Newcomer7-Mar-06 5:54 
GeneralFlame wars Pin
KaЯl7-Mar-06 6:24
KaЯl7-Mar-06 6:24 
GeneralRe: Flame wars Pin
Joseph M. Newcomer7-Mar-06 8:16
Joseph M. Newcomer7-Mar-06 8:16 
GeneralRe: Flame wars Pin
KaЯl8-Mar-06 21:42
KaЯl8-Mar-06 21:42 
Joseph M. Newcomer wrote:
void SomeFunction(int m_thing, LPCTSTR g_other)

Somebody writing that just proves(s)he didn't understand the concepts of OO languages. It isn't a reason to condemn the standard practices. On the contrary, in that case it helps to show the coder needs complementary explanations.

Joseph M. Newcomer wrote:
I force the programmer to ***actually look at the code*** instead of making possibly invalid assumptions.

Following this logic, you should call your methods M1 and M2, your variables a, b, c, your parameters p1, p2...

Joseph M. Newcomer wrote:
Efficiency is hardly an issue for SaveDC/RestoreDC. You are about to execute a couple hundred thousand instructions, or have just finished, and the cost of SaveDC/RestoreDC is minimal by comparison.

The problem is not about performance, it is about the managment of a limited resource. As you says, SaveDC/RestoreDC pushes more than 30 parameters in the stack.

Joseph M. Newcomer wrote:
. I get very, very suspicious of people who play the efficiency card at the sacrifice of code clarity

You are right, code clarity is something very important. Then the use of standard conventions Smile | :)

Joseph M. Newcomer wrote:
no stack size limit is documented, and I've been nested fairly deeply.

True, since the end of 16-bits environment programming, stack size is theorically no more a problem. For a thread the stack size is now 1 MB[^]. Stack overflows are now something rare, unless the coder goes wild. Nonetheless, the stack limited size should be a parameter a coder should be aware of. If he knowingly chooses to extensively use it, there is no problem, as long as he knws what he does (and then we go back on the point we are in fact both enforcing: know what you do, make your choice according to reality).

Joseph M. Newcomer wrote:
The older I get, the more I appreciate the value of really simple code

Agreed.

Joseph M. Newcomer wrote:
Once you've profiled your program, and know EXACTLY where the time is going, is the time to start worrying about how to make the inner loops faster.

The efforts are not the same: For instance, I work on a scientific module horribly written. it took me two days to simply modify loops code and gain 20% in performance. By redesigning the architecture I could probably gain much more, but it will take me weeks, perhaps months. In the end I will probably do that if I am allowed too, but the customers can use right now something 20% more efficient.
I agree with you to say: "think about your design, it is the most important thing". But I would add: "But don't ruin it by coding it without care".

Joseph M. Newcomer wrote:
check out my essay "Optimization--your worst enemy"

Any link?







Pull the tapeworm out of your ass
Fold with us! ¤ flickr

GeneralRe: Flame wars Pin
Joseph M. Newcomer8-Mar-06 22:15
Joseph M. Newcomer8-Mar-06 22:15 
GeneralRe: Flame wars Pin
KaЯl9-Mar-06 0:18
KaЯl9-Mar-06 0:18 
GeneralThanks from me too. Pin
RancidCrabtree24-Nov-05 6:50
RancidCrabtree24-Nov-05 6:50 
GeneralGreat ! Pin
Rodrigo Pinto Pereira de Souza14-Oct-04 10:00
Rodrigo Pinto Pereira de Souza14-Oct-04 10:00 
Generalwhy using "width" param Pin
10-Aug-01 1:02
suss10-Aug-01 1:02 
GeneralMSDN info on this subject Pin
Rick York1-Jul-01 14:03
mveRick York1-Jul-01 14:03 
QuestionLinker Errors? Pin
14-Jun-01 8:06
suss14-Jun-01 8:06 
AnswerRe: Linker Errors? Pin
14-Jun-01 8:54
suss14-Jun-01 8:54 
GeneralYes but.... Pin
2-May-01 20:45
suss2-May-01 20:45 
GeneralRe: Yes but.... Pin
Dnicholson3-Mar-04 12:16
Dnicholson3-Mar-04 12:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.