创建一个包含一个或多个分隔项的组合框?

发布于 2024-10-05 15:32:08 字数 733 浏览 0 评论 0原文

我正在使用 Delphi7,我想要一个带有分隔符项目的组合框(就像在弹出菜单中一样)。

我已经在 Mozilla Sunbird(我知道,它不是 Delphi...)中看到了这个完美的实现,方法如下:

  1. 分隔符项目是一条简单的灰线 绘制在项目的中心

  2. 如果将鼠标悬停在分隔符上 鼠标,选择不 出现

  3. 如果用户单击分隔符, 它也没有被选择并且 组合框不会关闭。

第 1 点可以使用 DrawItem 来实现。没有第二名我也可以生活,因为我对此一无所知。

对于第三点,我请求你的帮助。我发现关闭后 CBN_CLOSEUP 消息会立即发送到组合框。

我考虑过挂钩窗口过程,如果 CBN_CLOSEUP 被发送到某个组合框,则对其进行反击。但我不确定这是否是最好的解决方案,或者也许还有其他更优雅的方法?

无论解决方案是什么,我都希望有一个标准的 ComboBox,它可以正确支持 WinXP/Vista/7 主题。

谢谢!


编辑:有关工作组件,请参阅此线程:

您能帮忙将这个非常小的 C++ 组件翻译成 Delphi 吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

錯遇了你 2024-10-12 15:32:08

我尝试制作不可点击的分隔符项目(如 这个答案)并遇到了几个用户界面故障。问题在于,组合框的行为有几个方面很难完全正确:

  • 当列表下拉时,按向上和向下箭头键可导航列表。
  • 按 Enter 键关闭下拉列表,选择当前项目。
  • 按 Esc 键关闭下拉列表,选择当前项目(如果当前项目是使用向上和向下箭头键选择的)或最后选择的项目。
  • 如果组合框具有焦点,则按向上和向下箭头键可更改当前选择而不显示列表。
  • 如果组合框具有焦点,则键入任何内容都会选择与键入的内容相匹配的组合框项目。
  • 如果组合框获得焦点,则按 F4 会下拉组合框列表,然后可以通过键盘或鼠标控制。

确保禁用的分隔符项目不响应任何这些事件(加上我可能缺少的任何其他事件,例如屏幕阅读器?)似乎充满了错误。

相反,我使用的方法是将分隔符绘制为项目的一部分:

  1. 使用可变高度所有者绘制组合框。
  2. 对于任何需要分隔符的项目,在高度上添加 3 个像素。
  3. 在需要分隔符的每个项目的顶部画一条水平线。

这里有一些 C++Builder 代码来完成这个任务;将其翻译为 Delphi 应该很容易。

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;
  }
}

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;
  }
}
悲欢浪云 2024-10-12 15:32:08

您想要的是一个所有者绘制的组合框。请参阅:http://delphi.about.com/od/vclusing/a/ drawincombobox.htm

另外,这似乎解决了使项目无法复制的问题:
http://borland.newsgroups.archived .at/public.delphi.vcl.components.using.win32/200708/0708225320.html

据我所知,没有 VCL 方法可以做到这一点,因此您必须对组合框进行子类化。最好创建封装这些功能的组件,以便您可以轻松地重用它们。

上帝保佑

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

丑疤怪 2024-10-12 15:32:08

如果您希望控件看起来不错,请使用免费的 SpTBXLib。它支持组合样式组件,可以弹出带有线条的弹出菜单。

If you want your controls to look good use the free SpTBXLib. It supports combo style components which popup a popup menu with lines.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文