7
votes

Is there a trick to getting "OnChange" type of functionality with a TListBox? I can subclass the component and add a property, etc then only carry out OnClick code if the Index changes... I can also hack it with a form level variable to store the current index but just wondering if I'm overlooking the obvious before I go one way or the other.

2
What do you mean by that OnChange type functionality ? Content change, selection change, combination of it, something else ? - TLama
@TLama "... if the Index changes ..." ;o) - Sir Rufo
There is no OnChange event for TListBox only OnClick. Basically if you are using OnClick and click in the control, but not on an item, it still fires the OnClick event which can retrigger code unless you are also tracking the previously selected index. What I'm looking for is probably against the control design, but more like a TComboBox in list form. - Charles R. Thompson
The OnClick event (in this case it is quite confusing name) is fired whenever the user selects an item by mouse or keyboard, but not when you set it in code through ItemIndex property (which you may bypass by overriding SetItemIndex method and calling Click from there). - TLama
OnClick appears to fire if you click anywhere in the listbox... you do not have to actually click an item. It also fires if you click an item that is already selected. So I only want the OnClick to fire if an actual item that is not the previous item or the listbox itself is selected. I.e. emulation of an OnChange event. - Charles R. Thompson

2 Answers

12
votes

There seems to be no way other than implementing this by yourself. What you need is to remember the currently selected item and whenever the ItemIndex property is changed from code or whenever the control receives the LBN_SELCHANGE notification (which currently fires the OnClick event), you will compare the item index you stored with item index which is currently selected and if they differ, fire your own OnChange event. In code of an interposed class it could be:

type
  TListBox = class(StdCtrls.TListBox)
  private
    FItemIndex: Integer;
    FOnChange: TNotifyEvent;
    procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND;
  protected
    procedure Change; virtual;
    procedure SetItemIndex(const Value: Integer); override;
  published
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

implementation

{ TListBox }

procedure TListBox.Change;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TListBox.CNCommand(var AMessage: TWMCommand);
begin
  inherited;
  if (AMessage.NotifyCode = LBN_SELCHANGE) and (FItemIndex <> ItemIndex) then
  begin
    FItemIndex := ItemIndex;
    Change;
  end;
end;

procedure TListBox.SetItemIndex(const Value: Integer);
begin
  inherited;
  if FItemIndex <> ItemIndex then
  begin
    FItemIndex := ItemIndex;
    Change;
  end;
end;
0
votes

with the OnClick event is just like this...you need to store the last value to compare it.

if ListBox1.Items[ListBox1.ItemIndex]<> Edit1.Text then
    Edit1.Text := ListBox1.Items[ListBox1.ItemIndex];