Adding a horizontal scrollbar to a ComboBox using C#

In our WebCopy application we decided to update the User Agent configuration to allow selection from a predefined list of common agents, but still allow the user to enter their own custom agent if required.

Rather than use two separate fields, we choose to use a ComboBox in simple mode, which is both a textbox and a listbox in a single control. This mode seems somewhat out of fashion, I think the only place I see it used is in the Font common dialog, virtually unchanged since Windows 3.1.

The problem was immediately apparent however on firing up WebCopy and going to select a user agent - the agent strings can be very long, far longer than the width of the control.

Unfortunately however, the .NET ComboBox doesn't allow you to directly enable horizontal scrolling. So we'll do it the old fashioned way using the Windows API.

In order for a window to support horizontal scrolling, it needs to have the WS_HSCROLL style applied to it. And to setup the horizontal scrollbar, we need to call the SendMessage API with the CB_SETHORIZONTALEXTENT message.

As usual, we'll be starting off by creating a new Component, which we'll inherit from ComboBox.

Traditionally, you would call GetWindowLong and SetWindowLong API's with the GWL_STYLE or GWL_EXSTYLE flags. However, we can more simply override the CreateParams property of our component and set the new style when the control is created.

protected override CreateParams CreateParams
{
  get
  {
    CreateParams createParams;

    createParams = base.CreateParams;
    createParams.Style |= WS_HSCROLL;

    return createParams;
  }
}

With that done, we can now inform Windows of the size of the horizontal scroll area, and it will automatically add the scrollbar if required. To do this, I'll add two new methods to the component. The first will set the horizontal extent to a given value. The second will calculate the length of the longest piece of text in the control and then set the extent to match.

public void SetHorizontalExtent()
{
  int maxWith;

  maxWith = 0;
  foreach (object item in this.Items)
  {
    Size textSize;

    textSize = TextRenderer.MeasureText(item.ToString(), this.Font);
    if (textSize.Width > maxWith)
      maxWith = textSize.Width;
  }

  this.SetHorizontalExtent(maxWith);
}

public void SetHorizontalExtent(int width)
{
  SendMessage(this.Handle, CB_SETHORIZONTALEXTENT, new IntPtr(width), IntPtr.Zero);
}

The first overload of SetHorizontalExtent iterates through all the items in the control and uses the TextRenderer object to measure the size of the text. Once it has found the largest piece of text, it calls the second overload with the size.

The second overload does the actual work of notifying Windows using the SendMessage call, CB_SETHORIZONTALEXTENT message and the given width. SendMessage takes two configuration parameters per message, but CB_SETHORIZONTALEXTENT only requires one, and so we send 0 for the second.

The above function works with all display modes of the ComboBox.

For completeness, here are the API declarations we are using:

private const int WS_HSCROLL = 0x100000;
private const int CB_SETHORIZONTALEXTENT = 0x015E;

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

As usual, a demonstration project is available from the link below.

Downloads

Filename Description Version Release Date
horizontallyscrollingcombobox.zip
  • md5: de00358a12b9f95e5210e3ba8807e615

Sample C# project showing how to add a horizontal scrollbar to a ComboBox in C# using the WS_HSCROLL style and CB_SETHORIZONTALEXTENT message.

13/07/2010 Download

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

Leave a Comment

While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?

Styling with Markdown is supported

Comments

Gravatar

ViDom

# Reply

Sorry.. Do not work when i use DisplayMember and ValueMember from DataSet

Gravatar

ViDom

# Reply

public void SetHorizontalExtent()
{
  int maxWith;

  maxWith = 0;
  if (object.ReferenceEquals(this.DataSource, null))
  {
      foreach (object item in this.Items)
      {
          Size textSize;
          textSize = TextRenderer.MeasureText(item.ToString(), this.Font);
          if (textSize.Width > maxWith)
              maxWith = textSize.Width;
      }
  }
  else
  {
      foreach (DataRowView item in this.Items)
      {
          Size textSize;
          textSize = TextRenderer.MeasureText(item.Row[this.DisplayMember].ToString(), this.Font);
          if (textSize.Width > maxWith)
              maxWith = textSize.Width;
      }
  }
  this.SetHorizontalExtent(maxWith);
}

;) Thanks....

Gravatar

Richard Moss

# Reply

I never use the built in data binding so I wasn't aware of this issue. Thank you for pointing it out, and providing the fix!

Regards; Richard Moss

Max

# Reply

If you are using ASP.NET you can use a Coolite combobox control. you can check a sample on this blog: http://goo.gl/nAMHY