List Box ---如何添加水平滚动条

前段时间做了一个DLL,输出一个Dialog,里面的ListBox用到了水平滚动条.

VC6.0中作出来水平滚动条无法自动做,网上Google了一吧,www.codeguru.com中有偏文章很是好用.

转贴到这里:

http://www.codeguru.com/cpp/controls/listbox/article.php/c4759/

Overview

CListBox: This is a wrapper class for the ListBox control, and it's used in almost every application. There is a little "gotcha" to this class: The horizontal scroll bar doesn't work. Okay, I created it with the WS_HSCROLL flag set, the scroll bar is visible, and strings that I'm adding to the box are obviously longer then the box's horizontal extent, but I'm unable to scroll. What's wrong?

My theory is that the guy who originally implemented that control had way too much Greek mythology in his childhood. Villainous Procrustes and his bed where he had to cut off travelers' legs if they didn't fit in had a huge impact on the poor guy. If something is too long, chop it off. Well, whatever the reason is, the control simply ignores the fact that the strings are longer then it can show. How can we fix it?

Well, first, we have to catch all messages sent to the control that affect the box's content, hence the strings that it has (behind such functions as AddString() or ResetContent(), is SendMessage() with LB_ADDSTRING and LB_RESETCONTENT respectively). There are five messages of that type (at least that's the number I've come with. There may be more; I'm not sure.). Once they are identified, they can be caught and modified the same way as the others:

  • LB_ADDSTRING
  • LB_INSERTSTRING
  • LB_DELETESTRING
  • LB_DIR
  • LB_RESETCONTENT

After the control is subclassed, we can catch any message that is sent to it through the control's message map:

...
BEGIN_MESSAGE_MAP(CHScrollListBox, CListBox)
  //{{AFX_MSG_MAP(CHScrollListBox)
  // NOTE - the ClassWizard will add and remove mapping macros here.
  //}}AFX_MSG_MAP
  ON_MESSAGE(LB_ADDSTRING, OnAddString)
  ON_MESSAGE(LB_INSERTSTRING, OnInsertString)
  ON_MESSAGE(LB_DELETESTRING, OnDeleteString)
  ON_MESSAGE(LB_DIR, OnDir)
  ON_MESSAGE(LB_RESETCONTENT, OnResetContent)
END_MESSAGE_MAP()
...

When we catch the message (for example, LB_ADDSTRING), we are going to call default processing first, and, if the returned code is okay (most of these messages return some kind of a completion indicator), call our functions, either SetNewHExtent() or ResetHExtent(), that will bring the horizontal extent member of ListBox (this is the parameter that affects the horizontal scroll bar, if the bar is present) in sync with the longest string that it contains:

...

// OnAddString: wParam - none, lParam - string, returns - int

LRESULT CHScrollListBox::OnAddString(WPARAM wParam, LPARAM lParam)
{
  LRESULT lResult = Default();
  if (!((lResult == LB_ERR) || (lResult == LB_ERRSPACE)))
    SetNewHExtent((LPCTSTR) lParam);
  return lResult;
}
...

SetNewHExtent() in turn will calculate in pixels the length of the supplied string in the given device context (GetTextLen()) and set a new horizontal extent for the control. The extent can be set through CListBox::SetHorizontalExtent(extent) and we do it only if it's bigger then the current extent:

...

void CHScrollListBox::SetNewHExtent(LPCTSTR lpszNewString)
{
  int iExt = GetTextLen(lpszNewString);
  if (iExt > GetHorizontalExtent())
    SetHorizontalExtent(iExt);
}
...

Let's take a look at what GetTextLen() does:

...
int CHScrollListBox::GetTextLen(LPCTSTR lpszText)
{
  ASSERT(AfxIsValidString(lpszText));

  CDC *pDC = GetDC();
  ASSERT(pDC);

  CSize size;
  CFont* pOldFont = pDC->SelectObject(GetFont());
  if ((GetStyle() & LBS_USETABSTOPS) == 0)
  {
    size = pDC->GetTextExtent(lpszText, (int) _tcslen(lpszText));
    size.cx += 3;
  }
  else
  {
    // Expand tabs as well
    size = pDC->GetTabbedTextExtent(lpszText, (int)
          _tcslen(lpszText), 0, NULL);
    size.cx += 2;
  }
  pDC->SelectObject(pOldFont);
  ReleaseDC(pDC);

  return size.cx;
}
...

The first call is a sanity check with AfxIsValidString(). Then we get the control's DC through GetDC() and select the control's font in the context. After that, we look at the styles. If LBS_USETABSTOPS is set, we call GetTabbedTextExtent(); otherwise, it's GetTextExtent(), which returns the number of pixels that the specified string will occupy on the screen in a horizontal direction when drawn in that device context. I add a few more pixels here, just to make sure the string looks symmetrical when displayed, because the box has a margin on the left. That's it.

In calls OnDeleteString() and OnDir(), I use the function named ResetHExtent(). Because I do not know what string has the next longest extent from the one I'm deleting and since OnDir() will reset the content and fill it in with directory or file names, I'm simply going through the whole list, looking for the longest string the list has and setting its extent.

 

Usage

Okay, how one can utilize CHScrollListBox class? Here are some steps to take, sort of manual labor, that will get you there:

  1. In the resource editor, create a dialog template that will host a listbox control.
  2. Set properties of the list box according to the picture above (at least, Horizontal Scroll, Selection, and Owner Draw should match).
  3. In the header file of your dialog's class, associated with the template, add:
    #include "HScrollListBox.h"
  4. Define a member variable of CHScrollListBox, like so:
    CHScrollListBox m_listBox;
  5. In the implementation file, in DoDataExchange() function, add a line:
    DDX_Control(pDX, IDC_YOUR_LISTBOX_CTRL_ID, m_listBox);
    This call in turn calls SubclassDlgItem(), which does the trick of routing all messages destined to the control to our message map.

Of course, IDC_YOUR_LISTBOX_CTRL_ID and m_listBox are orbitrary names; you should use your own instead.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值