Delphi Seattle TCombobox Dropdowncount not working

2019-09-13 07:07发布

问题:

I have an owner-drawn TComboBox in Delphi Seattle which ignores any settings to the DropDownCount property. The height of the individual items is 59 pixels and the width is 311 pixels. I have 5 entries in the Items stringlist. Setting a value below 8 results in no dropdown being shown at all, while any higher values result in only 1 item being shown in the dropdown along with a vertical scrollbar. I need to be able to show all 5 entries in the items list in the dropdown.

Here's the DFM to reproduce the problem:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 223
  ClientWidth = 527
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ComboBox1: TComboBox
    Left = 96
    Top = 16
    Width = 311
    Height = 22
    Style = csOwnerDrawVariable
    ItemIndex = 0
    TabOrder = 0
    Text = 'ITEM ONE'
    OnDrawItem = ComboBox1DrawItem
    OnMeasureItem = ComboBox1MeasureItem
    Items.Strings = (
      'ITEM ONE'
      'ITEM TWO'
      'ITEM THREE'
      'ITEM FOUR'
      'ITEM FIVE')
  end
end

And here's the PAS file:

unit CBBoxBugUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    procedure ComboBox1MeasureItem(Control: TWinControl; Index: Integer; var Height: Integer);
    procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
var Working_String : String;
    Text_Width ,
    Text_Height ,
    Output_X ,
    Output_Y       : Integer;
begin
  ComboBox1.Canvas.Pen.Color := clWhite;
  ComboBox1.Canvas.Brush.Color := clGreen;
  ComboBox1.Canvas.Brush.Style := bsSolid;
  ComboBox1.Canvas.Pen.Style := psSolid;
  ComboBox1.Canvas.Pen.Width := 1;
  ComboBox1.Canvas.Font.Color := clWhite;
  ComboBox1.Canvas.Font.Name := 'ARIAL BLACK';
  ComboBox1.Canvas.Font.Size := 14;
  ComboBox1.Canvas.FillRect( Rect );
  Working_String := self.ComboBox1.Items.Strings[ Index ];
  Text_Width := self.ComboBox1.Canvas.TextWidth( Working_String );
  Text_Height := self.ComboBox1.Canvas.TextHeight( Working_String );
  Output_X := ( Rect.Width - Text_Width ) div 2;
  Output_Y := ( Rect.Height - Text_Height ) div 2;
  ComboBox1.Canvas.TextOut( Output_X , Output_Y , Working_String );
end;

procedure TForm1.ComboBox1MeasureItem(Control: TWinControl; Index: Integer; var Height: Integer);
begin
  Height := 59;
end;

end.

回答1:

Even though the documentation for ItemHeight of TComboBox states:

... If Style is csOwnerDrawVariable, ItemHeight is the default height for drawing list items, but this value can be overridden by an OnMeasureItem event handler. ...

VCL has internal calculations that depend on FItemHeight. Set ItemHeight to 59 and DropDownCount will be honored (it is already honored but for the default 16). If you set the value in design time, the combo will also adjust its size to that of at runtime.