Delphi TListView - 调用 'Free' 时添加的按钮不会消失

标签 delphi delphi-xe tlistview

(使用: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;

图片:

enter image description here

最佳答案

嗯,我看到两个潜在的问题。首先,您使用的是 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/

相关文章:

delphi - Windows.CopyFile 是否在源和目标是网络共享时创建临时本地文件?

Delphi XE 显示至少 200 个错误,但程序编译得很好

delphi - 从delphi.exe提取项目文件

delphi - 为 ListView Delphi XE7 创建自定义项目外观

delphi - 检测 TWebBrowser 文档中的事件元素何时发生变化

delphi - TIdHTTPWebBrokerBridge 文档

Delphi 32 到 Delphi XE2(64 位)转换

delphi - 如何在Delphi XE中为TListview设置背景图片?

delphi - 如何使用 OwnerData true 处理 TListView 中的默认列自动调整大小

delphi - 使用 Ctrl+Enter 清空 TMemo