I want to make a component based on TListBox
.
When the Items.Count
changes, I want to update the Caption
of a TLabel
:
Label1.Caption := IntToStr(ListBox1.Items.Count);
I made the component below, but it does not work:
unit UChangeListBox;
interface
uses
Classes, StdCtrls, Messages;
type
TChListBox = class(StdCtrls.TListBox)
private
FItemIndex: Integer;
FOnChange: TNotifyEvent;
procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND;
protected
procedure DoChange; virtual;
procedure SetItemIndex(const Value: Integer); override;
published
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
procedure Register;
implementation
procedure TChListBox.DoChange;
begin
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TChListBox.CNCommand(var AMessage: TWMCommand);
begin
inherited;
if (AMessage.NotifyCode = LBN_SELCHANGE) and (FItemIndex <> ItemIndex) then
begin
FItemIndex := ItemIndex;
DoChange;
end;
end;
procedure TChListBox.SetItemIndex(const Value: Integer);
begin
inherited;
if FItemIndex <> ItemIndex then
begin
FItemIndex := Value;
DoChange;
end;
end;
procedure Register;
begin
RegisterComponents('MyComponents',[TChListBox]);
end;
end.
TListBox
inherits a protected Changed()
method from TControl
, which sends a CM_CHANGED
message to derived classes. TListBox
calls Changed()
in reply to LBN_SELCHANGE
. You don't need to define your own Change()
method, just handle CM_CHANGED
instead.
TListBox.SetItemIndex()
sends a LB_SETCURSEL
message to the ListBox HWND
. That message does not trigger LBN_SELCHANGE
, so you would have to detect the ItemIndex
change yourself. Which you attempted to do.
However, these issues only apply to selection changes, not to Item.Count
changes. Changing the ItemIndex
does not change the Items.Count
. If your goal is simply to display a new Items.Count
whenever an item is added or removed from the ListBox, you need to handle the LB_ADDSTRING
, LB_INSERTSTRING
, LB_DELETESTRING
and LB_RESETCONTENT
messages instead.
Try something more like this:
type
TChListBox = class(TListBox)
private
FOnItemCountChange: TNotifyEvent;
protected
procedure WndProc(var Message: TMessage); override;
published
property OnItemCountChange: TNotifyEvent read FOnItemCountChange write FOnItemCountChange;
end;
procedure TChListBox.WndProc(var Message: TMessage);
var
OldCount: Integer;
begin
case Message.Msg of
LB_ADDSTRING, LB_INSERTSTRING, LB_DELETESTRING:
begin
// for LB_(ADD|INSERT)STRING, Message.Result is the 0-based
// index of the added string, or a LB_ERR... error code.
//
// for LB_DELETESTRING, Message.Result is the number of items
// remaining in the list, or a LB_ERR... error code.
//
inherited;
if (Message.Result >= 0) and Assigned(FOnItemCountChange) then
FOnItemCountChange(Self);
end;
LB_RESETCONTENT:
begin
// the Message.Result is not used in this message.
//
OldCount := Items.Count;
inherited;
if (OldCount <> Items.Count) and Assigned(FOnItemCountChange) then
FOnItemCountChange(Self);
end;
else
inherited;
end;
end;