How can I have TComboBox with some items that are disabled? I need the user to see these items, but not be able to select them.
Thanks!
We can Enable or Disable the options in the given Combobox widget by providing the state property. The state property forces to make a widget either active or disabled. To disable the Combobox widget, we have to set the state property as readonly or disabled.
Yes, and this is how to do it:
Drop a TComboBox
on your form, and set Style
to csOwnerDrawFixed
. Then add the event handlers
procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
const
INDENT = 3;
begin
with TComboBox(Control) do
begin
FillRect(Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
inc(Rect.Left, INDENT);
if boolean(Items.Objects[Index]) then
SetTextColor(Canvas.Handle, clBlack)
else
SetTextColor(Canvas.Handle, clGray);
DrawText(Canvas.Handle,
PChar(Items[Index]),
length(Items[Index]),
Rect,
DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_END_ELLIPSIS)
end;
end;
and
procedure TForm1.ComboBox1CloseUp(Sender: TObject);
begin
with TComboBox(Sender) do
if (ItemIndex <> -1) and not boolean(Items.Objects[ItemIndex]) then
begin
beep;
Perform(CB_SHOWDROPDOWN, integer(true), 0);
end;
end;
Also, in the interface section of your form, prior to the declaration of the form class, add
TComboBox = class(StdCtrls.TComboBox)
protected
procedure WndProc(var Message: TMessage); override;
end;
and implement the WndProc
as
procedure TComboBox.WndProc(var Message: TMessage);
function NextItemIsDisabled: boolean;
begin
result := (ItemIndex < Items.Count - 1) and
not boolean(Items.Objects[ItemIndex + 1]);
end;
procedure SelectNextEnabledItem;
var
i: Integer;
begin
for i := ItemIndex + 1 to Items.Count - 1 do
if boolean(Items.Objects[i]) then
begin
ItemIndex := i;
Exit;
end;
beep;
end;
procedure KillMessages;
var
msg: TMsg;
begin
while PeekMessage(msg,
Handle,
WM_KEYFIRST,
WM_KEYLAST,
PM_REMOVE) do;
end;
function PrevItemIsDisabled: boolean;
begin
result := (ItemIndex > 0) and
not boolean(Items.Objects[ItemIndex - 1]);
end;
procedure SelectPrevEnabledItem;
var
i: Integer;
begin
for i := ItemIndex - 1 downto 0 do
if boolean(Items.Objects[i]) then
begin
ItemIndex := i;
Exit;
end;
beep;
end;
begin
case Message.Msg of
WM_KEYDOWN:
case Message.WParam of
VK_DOWN:
if NextItemIsDisabled then
begin
SelectNextEnabledItem;
KillMessages;
Exit;
end;
VK_UP:
if PrevItemIsDisabled then
begin
SelectPrevEnabledItem;
KillMessages;
Exit;
end;
end;
end;
inherited;
end;
To test the combo box, write, for example
procedure TForm1.FormCreate(Sender: TObject);
begin
ComboBox1.Items.AddObject('Alpha', TObject(true));
ComboBox1.Items.AddObject('Beta', TObject(true));
ComboBox1.Items.AddObject('Gamma', TObject(false));
ComboBox1.Items.AddObject('Delta', TObject(true));
end;
I think you get the meaning of true
and false
here -- it simply means enabled
.
It's not easy (and it's a bad idea, since that's not how comboboxes behave on Windows).
You'd have to owner draw the combobox yourself. Use the Items.Objects array to store whether or not the item is enabled or disabled, and check that array before drawing each item in order to set the colors appropriately.
You'd also need to handle the OnChange
and OnClick
events, and add a way to track the last selected ItemIndex
. In OnChange/OnClick
, you disconnect the event handler, check the Objects[ItemIndex]
value to see if a selection is allowed, if not set the ItemIndex
back to the last selected ItemIndex
, and then re-enable the event handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With