Creating a ComboBox with one or more separator items?

I'm using Delphi7 and I'd like to have a ComboBox with separator items (Just like in popup menus).

I've seen this beautifully implemented in Mozilla Sunbird (I know, it's not Delphi...) the following way:

  1. The separator item is a simple gray line drawn in the center of the item

  2. If you hover over the separator with the mouse, the selection doesn't appear

  3. If the user clicks the separator, it's not selected either AND the combobox doesn't closeup.

No. 1 could be implemented using DrawItem. I could live without No. 2 because I have no idea about that.

For No. 3 I'm asking for your help. I've figured out that straight after closing up a CBN_CLOSEUP message is sent to the combobox.

I thought about hooking the window proc and if CBN_CLOSEUP is sent to a certain combobox then countering it. But I'm unsure if this is the best solution, or maybe there are other, more elegant ways?

Whatever the solution is, I'd like to have a standard ComboBox which supports WinXP/Vista/7 theming properly.

Thanks!


Edit: For a working component please see this thread:

Can you help translating this very small C++ component to Delphi?

Answers


What you want is an owner-drawn combobox. See this: http://delphi.about.com/od/vclusing/a/drawincombobox.htm

Also, this seems to solve making the item unclicable: http://borland.newsgroups.archived.at/public.delphi.vcl.components.using.win32/200708/0708225320.html

As far as I know there is no VCL way of doing that, so you'll have to subclass the combobox. It would be nice to create component encapsulating those functionalities so you can reuse them easily.

God bless


I played around with making unclickable separator items (as described in this answer) and ran into several UI glitches. The problem is that combo boxes have several aspects to their behavior that can be hard to get exactly right:

  • Pressing the up and down arrow keys navigates the list while the list is dropped down.
  • Pressing Enter closes the dropped down list, selecting the current item.
  • Pressing Escape closes the dropped down list, selecting the current item (if the current item was chosen with the up and down arrow keys) or the last selected item.
  • If the combo box has the focus, then pressing the up and down arrow keys to changes the current selection without displaying the list.
  • If the combo box has the focus, then typing anything selects the combo box item matching whatever is typing.
  • If the combo box has the focus, then pressing F4 drops down the combo box list, which can then be controlled by keyboard or mouse.

Ensuring that disabled separator items don't respond to any of these events (plus any other events which I may be missing, e.g., screen readers?) seems fraught with error.

Instead, the approach I'm using is to draw the separator as part of the item:

  1. Use a variable height owner draw combo box.
  2. Add 3 pixels to the height for any items that need separators.
  3. Draw a horizontal line at the top of each item needing a separator.

Here's some C++Builder code to accomplish this; translating it to Delphi should be easy enough.

void __fastcall TForm1::ComboBox1DrawItem(TWinControl *Control,
    int Index, TRect &Rect, TOwnerDrawState State)
{
  bool draw_separator = NeedsSeparator(Index) && 
      !State.Contains(odComboBoxEdit);

  TCanvas *canvas = dynamic_cast<TCustomCombo*>(Control)->Canvas;
  canvas->FillRect(Rect);

  TRect text_rect = Rect;
  // Add space for separator if needed.
  if (draw_separator) {
    text_rect.Top += 3;
  }

  canvas->TextOut(text_rect.Left + 3,
      (text_rect.Top + text_rect.Bottom) / 2 - 
        canvas->TextHeight(ComboBox1->Items->Strings[Index]) / 2), 
      ComboBox1->Items->Strings[Index]);

  // Draw a separator line above the item if needed.
  if (draw_separator) {
    canvas->Pen->Color = canvas->Font->Color;
    canvas->MoveTo(Rect.Left, Rect.Top + 1);
    canvas->LineTo(Rect.Right, Rect.Top + 1);
  }
}

void __fastcall TForm1::ComboBox1MeasureItem(
    TWinControl * /* Control */, int Index, int &Height)
{
  Height = ComboBox1->ItemHeight;

  // Add space for the separator if needed.
  if (Index != -1 && NeedsSeparator(Index)) {
    Height += 3;
  }
}

Need Your Help

How to get jenkins to deploy .war file to tomcat6 not localhost

maven deployment jenkins tomcat6 pom.xml

I have a jenkins ci that builds our projects and make a .war file of it.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.