我尝试制作不可点击的分隔项(如本答案中所述)并遇到了几个 UI 故障。问题是组合框的行为有几个方面很难完全正确:
- 下拉列表时,按向上和向下箭头键可导航列表。
- 按 Enter 关闭下拉列表,选择当前项目。
- 按 Escape 关闭下拉列表,选择当前项目(如果当前项目是使用向上和向下箭头键选择的)或最后一个选定项目。
- 如果组合框具有焦点,则按向上和向下箭头键更改当前选择而不显示列表。
- 如果组合框具有焦点,则键入任何内容都会选择与正在键入的内容匹配的组合框项目。
- 如果组合框有焦点,则按 F4 会下拉组合框列表,然后可以通过键盘或鼠标控制。
确保禁用的分隔符项目不响应任何这些事件(以及我可能遗漏的任何其他事件,例如屏幕阅读器?)似乎充满了错误。
相反,我使用的方法是将分隔符绘制为项目的一部分:
- 使用可变高度所有者绘制组合框。
- 为需要分隔符的任何项目的高度添加 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;
}
}