Multiline Edit Box with Automatic Scroll Bar Display

Environment: VC6, MFC

The Problem

If you need a multiline Edit Control, you can implement one at design time by using the Resource Editor and setting the styles Multiline, Horizontal scroll, Auto HScroll, Vertical scroll, and Auto VScroll from the Styles Tab in Edit Properties. Or, if you create the Edit Control from code at run time, you need to set the styles WS_HSCROLL, WS_VSCROLL, ES_MULTILINE, ES_AUTOHSCROLL, and ES_AUTOVSCROLL. For displaying multiple lines, you need to enter Ctrl+RETURN interactively from the keyboard during program execution, or from the code you need to separate the lines with the sequence "\r\n", for example:

m_oEdit.SetWindowText(_T("First Line\r\nSecond Line\r\n...\r\n"));

The problem with these approaches is that the scroll bars are visible even when they are not needed; for example, when the Edit Box is empty or when the text is very short. This problem is solved by the CScrollEdit class presented in this article.

The Solution

The CScrollEdit class is derived from the CEdit MFC class. As explained before, the styles WS_HSCROLL, WS_VSCROLL, ES_MULTILINE, ES_AUTOHSCROLL, and ES_AUTOVSCROLL have to be set for proper functioning of the Edit Control. When the scroll bars are not needed for display, they can be hidden by calling the function ShowScrollBar():

ShowScrollBar(SB_HORZ, FALSE);
ShowScrollBar(SB_VERT, FALSE);

The problem is how to detect when the scroll bars are needed and when they are not needed. This is done by the function CheckScrolling(), which is the core of the CScrollEdit class:

void CScrollEdit::CheckScrolling(LPCTSTR lpszString)
{
  CRect oRect;
  GetClientRect(&oRect);
  CClientDC oDC(this);
  int iHeight=0;
  BOOL bHoriz = FALSE;
  CFont* pEdtFont = GetFont();
  if(pEdtFont != NULL)
  {
    //Determine Text Width and Height
    SIZE oSize;
    CFont* pOldFont = oDC.SelectObject(pEdtFont);
    //Determine the line Height
    oSize = oDC.GetTextExtent(CString(_T(" ")));
    iHeight = oSize.cy;
    //Text Width
    int iWidth=0, i =0;
    CString oStr;
    //Parse the string; the lines in a multiline Edit are
    //separated by "\r\n"
    while(TRUE == ::AfxExtractSubString(oStr, lpszString,
                                        i, _T('\n')))
    {
      if(FALSE == bHoriz)
      {
        int iLen = oStr.GetLength()-1;
        if(iLen >=0)
        {
          //Eliminate last '\r'
          if(_T('\r') == oStr.GetAt(iLen))
            oStr = oStr.Left(iLen);
          oSize = oDC.GetTextExtent(oStr);
          if(iWidth < oSize.cx)
            iWidth = oSize.cx;
          if(iWidth >= oRect.Width())
            bHoriz = TRUE;
        }
      }
      i++;
    }
    oDC.SelectObject(pOldFont);
    //Text Height
    iHeight *= i;
  }
  ShowHorizScrollBar(bHoriz);
  ShowVertScrollBar(iHeight >= oRect.Height());
}

The idea in this function is to parse the Edit Control's text and extract the lines (which are separated in the text by the sequence "\r\n"). Then for getting the size of each line the GetTextExtent() function is used. The height of the text is determined by multiplying the height of a line with the number of lines. If the height of the text is greater or equal than the height of the Edit Control's client rectangle then the vertical scroll bar has to be displayed, otherwise is not displayed. For displaying the horizontal scroll bar it is enough to detect just one line with the width greater than or equal to the width of the Edit Control's client rectangle.

The function CheckScrolling() is called from two places:

  1. From the function SetWindowText(), which is overloading the CWnd::SetWindowText() function:
  2. void CScrollEdit::SetWindowText(LPCTSTR lpszString)
    {
      CheckScrolling(lpszString);
      CEdit::SetWindowText(lpszString);
    }
    
  3. From the handler OnCheckText() which is treating the special user message UWM_CHECKTEXT:
  4. LRESULT CScrollEdit::OnCheckText(WPARAM wParam, LPARAM
                                                    lParam)
    {
      CString oStr;
      GetWindowText(oStr);
      CheckScrolling(oStr);
      return 0;
    }
    

    The message UWM_CHECKTEXT is posted from the OnChar() handler (the handler for the WM_CHAR message) each time the user is editing inside the Edit Control:

    void CScrollEdit::OnChar(UINT nChar, UINT nRepCnt,
                             UINT nFlags)
    {
      //Possible Text Change
      PostMessage(UWM_CHECKTEXT);
      CEdit::OnChar(nChar, nRepCnt, nFlags);
    }
    

How to Use the Code

Using the CScrollEdit class is very easy, and it is demonstrated by the accompanying demo project. You need to include the source files ScrollEdit.h and ScrollEdit.cpp in your project. In ScrollEdit.cpp, change the line

#include "ScrollEditTst.h"

to the appropriate #include for your application's header file. Then, declare the control member variable in the appropriate header file:

#include "ScrollEdit.h"
//...
CScrollEdit m_oScrollEdit;

and do subclassing in the appropriate place; for example, in the OnInitDialog() handler:

m_oScrollEdit.SubclassDlgItem(IDC_EDIT1, this);
m_oScrollEdit.SetWindowText(_T(""));

The initialization with text _T("") is necessary for the proper initialization of the scroll bar's display (in this case, they are not displayed initially).

Downloads

Download demo project - 14 Kb
Download source - 3 Kb


Comments

  • Problem

    Posted by KalliMan on 04/11/2013 12:45am

    Actually this doesn't work in case the text doesn't contain "\n" and the CEDit is Word Wrap.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Today's "average" business in general is ever more reliant on technology and the Internet. Mobility is the most often cited business trend that has transformed the way many of us work and communicate. From an IT security perspective, this means that protection methods and tools from even a few years ago are rapidly becoming "unfit for purpose." This guide provides crucial facts to assist you in building a robust business case, meeting the demands of your business, and protecting against threats now and in the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds