【问题标题】:Creating a ComboBox with one or more separator items?使用一个或多个分隔项创建一个 ComboBox?
【发布时间】:2010-12-04 17:46:39
【问题描述】:

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

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

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

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

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

没有。 1 可以使用 DrawItem 来实现。没有二号我也能活下去,因为我对此一无所知。

对于 3 号,我正在寻求您的帮助。我发现在关闭后立即将 CBN_CLOSEUP 消息发送到组合框。

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

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

谢谢!


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

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

【问题讨论】:

  • 您要求提供标准组合框,但您还要求提供具有分隔符的组合框。这些是互斥的请求。 Mozilla 的组合框可以做到这一点,因为它不是操作系统提供的控件; Mozilla 为其所有控件提供了所有自己的代码,因此它可以在所有受支持的平台上一致地工作。
  • Mozilla 的小部件一直是我的烦恼之源。它们以完全错误的方式实现一致性,跨平台保持一致,但用户倾向于在单个平台上运行。您真正想要的一致性是与本机平台的一致性,而他们做不到。除了Qt还有什么可以实现跨平台原生的widget吗?
  • Mozilla 的增强组合框非常适合 Win 环境,并通过组合框分隔符添加了一个绝妙的想法。但我同意你的观点,Windows 的 iTunes/Safari 非常糟糕且令人困惑(尽管它的目标显然是让从 Win PC 到 Mac 的转换更容易)......
  • 我只是讨厌他们不使用原生平台 UX 元素做事。你见过 Firefox 4 的新菜单习语吗?太糟糕了。

标签: delphi combobox delphi-7 separator


【解决方案1】:

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

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

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

上帝保佑

【讨论】:

  • 是的,我也通过 Google 找到了该页面,但是当用户单击分隔项时,如何防止组合框关闭?
  • 谢谢,我想我在同一时间找到了您在编辑中链接的组件 :-)
【解决方案2】:

我尝试制作不可点击的分隔项(如this answer 中所述)并遇到了几个 UI 故障。问题是组合框的行为有几个方面很难完全正确:

  • 在下拉列表时按向上和向下箭头键可在列表中导航。
  • 按 Enter 关闭下拉列表,选择当前项目。
  • 按 Escape 关闭下拉列表,选择当前项目(如果当前项目是使用向上和向下箭头键选择的)或最后一个选定项目。
  • 如果组合框具有焦点,则按向上和向下箭头键可更改当前选择而不显示列表。
  • 如果组合框具有焦点,则键入任何内容都会选择与正在键入的内容匹配的组合框项。
  • 如果组合框有焦点,则按 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;
  }
}

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我完全不同意;使用原生平台主题对你的组件来说是最重要的,除非你当然想模仿 Windows 的 iTunes/Safari!
    • Jan 你没有好好阅读我的帖子。我说过我会坚持使用标准的 XP/Vista/7 主题,并且不惜一切代价避免使用任何蒙皮/100% 自绘组件!
    • @Steve 你肯定需要所有者绘制,所以我猜你需要进入 uxtheme 编码,这是一场红润的噩梦!
    • @David:UXTheme 编码非常简单。我一直这样做:stackoverflow.com/questions/3986067/…stackoverflow.com/questions/3900833/… 等。
    • @Steve 和@David:SPTbxLib 非常支持标准的 Windows 主题。是的,您可以进行剥皮,但您不必这样做。我可以理解您不想使用第三方组件,但是如果您想要您所要求的全部功能,那么您基本上是在编写自己的组件。我只是指出其他人已经为您完成了这项工作。
    猜你喜欢
    • 2019-10-20
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多