(使用:Delphi XE)
我正在向 ListView 的每一行添加一个 TButton。在按钮中,OnClick 处理程序是 Sender.Free。但是(虽然列表行因填充 ListView 的数据集已更新而消失),但该按钮在应该消失时仍保留在 ListView 上。我做错了什么?
这是我的代码,显示了按钮的创建,以及要释放按钮的 OnClick:
(另一方面,我知道在事件处理程序中销毁组件并不是一个好的做法。这就是这里的问题吗?你能建议另一种方法来删除按钮吗?)
procedure TfMain.actWaitListExecute(Sender: TObject);
var
li: TListItem;
s: string;
btRect: TRect;
p: PInteger;
begin
lstWaitList.Items.Clear;
lstWaitList.Clear;
with uqWaitList do
begin
if State = dsInactive then
Open
else
Refresh;
First;
while not EOF do
begin
li := lstWaitList.Items.Add;
s := MyDateFormat(FieldByName('VisitDate').AsString);
li.Caption := s;
New(p);
p^ := FieldByName('ROWID').AsInteger;
li.Data := p;
s := MyTimeFormat(FieldByName('InTime').AsString);
li.SubItems.Add(s);
li.SubItems.Add(FieldByName('FirstName').AsString + ' ' +
FieldByName('LastName').AsString);
// li.SubItems.Add(FieldByName('LastName').AsString);
with TButton.Create(lstWaitList) do
begin
Parent := lstWaitList;
btRect := li.DisplayRect(drBounds);
btRect.Left := btRect.Left + lstWaitList.Column[0].Width +
lstWaitList.Column[1].Width + lstWaitList.Column[2].Width;
btRect.Right := btRect.Left + lstWaitList.Column[3].Width;
BoundsRect := btRect;
Caption := 'Check Out';
OnClick := WaitingListCheckOutBtnClick;
end;
Next;
end;
end;
end;
procedure TfMain.lstWaitListDeletion(Sender: TObject; Item: TListItem);
begin
Dispose(Item.Data);
end;
procedure TfMain.WaitingListCheckOutBtnClick(Sender: TObject);
var
SelROWID, outtime: integer;
x: longword;
y: TPoint;
h, mm, s, ms: word;
begin
y := lstWaitList.ScreenToClient(Mouse.CursorPos);
// Label23.Caption := Format('%d %d', [y.X, y.y]);
x := (y.y shl 16) + y.X;
PostMessage(lstWaitList.Handle, WM_LBUTTONDOWN, 0, x);
PostMessage(lstWaitList.Handle, WM_LBUTTONUP, 0, x);
Application.ProcessMessages;
SelROWID := integer(lstWaitList.Selected.Data^);
// ShowMessage(IntToStr(SelROWID));
with TfCheckOut.Create(Application) do
begin
try
if ShowModal = mrOk then
begin
decodetime(teTimeOut.Time, h, mm, s, ms);
outtime := h * 100 + mm;
uqSetOutTime.ParamByName('ROWID').Value := SelROWID;
uqSetOutTime.ParamByName('OT').Value := outtime;
uqSetOutTime.Prepare;
uqSetOutTime.ExecSQL;
(TButton(Sender)).Visible := False;
(TButton(Sender)).Free;
actWaitListExecute(Self);
end;
finally
Free;
end;
end;
end;
图片:
最佳答案
嗯,我看到两个潜在的问题。首先,您使用的是 with
block ,这可能会使编译器解析某些标识符的方式与您认为它们应该解析的方式不同。例如,如果 TfCheckOut 有一个名为 Sender 的成员,您最终将释放该成员,而不是本地 Sender。
其次,TButton(Sender).Free
调用位于条件内部,并且仅当对 ShowModal 的调用返回
mrOK` 时才会激活。您是否已进入调试器并确保该代码分支正在执行?
关于不在其自己的事件处理程序中释放按钮的问题,从代码角度来看,这样做是完全合法的。这不是一个好主意,释放它可能会导致在事件处理程序完成后引发异常,但它不应该不执行任何操作,这就是您在这里看到的。这几乎肯定表明 Free
根本没有被调用。如果您想要一种安全释放它的方法,请查看消息传递。您需要在表单上创建一个消息 ID 和一个处理程序,然后使用该控件作为参数将该消息 PostMessage
(而不是 SendMessage
)发送到您的表单,并且消息处理程序应该释放该按钮。这样您就可以确保事件处理程序不再运行。
编辑:好的,所以如果您确定Free
正在被调用,那么Free
正在被调用,并且如果Free
完成时没有引发异常,然后按钮将被销毁。真的就是这么简单。 (运行此代码后尝试再次单击该按钮。除非发生非常非常奇怪的事情,否则什么都不会发生。)如果之后您仍然看到该按钮,那就是另一个问题了。这意味着父级(TListView)不会重新绘制自身。尝试调用其 Invalidate
方法,这将使 Windows 正确地重新绘制它。
关于Delphi TListView - 调用 'Free' 时添加的按钮不会消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6627114/