我创建了一个自定义tPanel,并在里面放置了各种自定义组件…

procedure Panel_Comp(Location: TWinControl; NumOfComp:     Integer;Left,Top,Height,width:Integer);
begin
  MyPanel := TsPanel.Create(Conf);
  MyPanel.Name := 'MyPanel' + IntToStr(NumOfComp);
  MyPanel.Parent := Location;
  MyPanel.Left := Left;
  MyPanel.Top := Top;
  MyPanel.Height := Height;
  MyPanel.Width := width;
  MyPanel.Caption := '';
end;

我这样称呼它
Panel_Comp(Conf.ScrollBox1,1,8,10,70,322);

在相同的逻辑中,我将其他自定义组件(包括具有onclick事件的tbitbtn)放入新面板中。
procedure BitBtn_Comp(Location: TWinControl; NumOfComp: Integer; Left,Top,Height,Width,ImageNum: Integer);
begin
  MyBitBtn := TBitBtn.Create(Conf);
  ......
  MyBitBtn.tag := NumOfComp;
  MyBitBtn.OnClick:= Conf.CloseCurrentPanel;
end;

在tconf.closecurrentpanel的主面板中;
procedure TConf.CloseCurrentPanel(Sender: TObject);
var
  panelComp: TComponent;
begin
  panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(TBitBtn(Sender).tag);
  TPanel(panelComp).Free;
  Application.ProcessMessages;
end;

当我打电话说我被侵犯了…
我认为在释放面板之前必须释放面板内的所有组件,但如何在面板之前释放bitbtn并继续单击事件的操作?
这是findcomponetex函数,您需要它…
function FindComponentEx(const Name: string): TComponent;
var
  FormName: string;
  CompName: string;
  P: Integer;
  Found: Boolean;
  Form: TForm;
  I: Integer;
begin
// Split up in a valid form and a valid component name
  P := Pos('.', Name);
  if P = 0 then
  begin
    raise Exception.Create('No valid form name given');
  end;
  FormName := Copy(Name, 1, P - 1);
  CompName := Copy(Name, P + 1, High(Integer));
  Found    := False;
  // find the form
  for I := 0 to Screen.FormCount - 1 do
    begin
      Form := Screen.Forms[I];
   // case insensitive comparing
      if AnsiSameText(Form.Name, FormName) then
        begin
          Found := True;
          Break;
        end;
    end;
  if Found then
    begin
      for I := 0 to Form.ComponentCount - 1 do
        begin
          Result := Form.Components[I];
         if AnsiSameText(Result.Name, CompName) then Exit;
        end;
     end;
  Result := nil;
end;

最佳答案

AV之所以发生,是因为您正在销毁组件(MyBitBTN),而该组件仍在处理Windows消息。解决方法是通过PostMessage将销毁推迟到稍后,类似于:

unit Unit1;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  ExtCtrls,
  StdCtrls;

const
  UM_DESTROYPANEL = WM_APP + 623; // some "unique" number; UM = user message

type
  TConf = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  strict private
    procedure UMDestroyPanel(var Message: TMessage); message UM_DESTROYPANEL;
  public
    { Public-Deklarationen }
  end;

var
  Conf: TConf;

implementation

{$R *.dfm}

procedure TConf.Button1Click(Sender: TObject);
begin
  PostMessage(Handle, UM_DESTROYPANEL, 0, 0);
end;

procedure TConf.UMDestroyPanel(var Message: TMessage);
begin
  Panel1.Free;
end;

end.

如果需要,可以使用wparam和lparam传递如下参数:
procedure TConf.Button1Click(Sender: TObject);
begin
  PostMessage(Handle, UM_DESTROYPANEL, WPARAM(Panel1), 0);
end;

procedure TConf.UMDestroyPanel(var Message: TMessage);
begin
  TObject(Message.WParam).Free;
end;

编辑:
在你的情况下,我可能会重写如下:
procedure TConf.CloseCurrentPanel(Sender: TObject);
var
  panelComp: TComponent;
begin
  panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(TBitBtn(Sender).Tag);
  PostMessage(Handle, UM_DESTROYPANEL, WPARAM(panelComp), 0);
end;

或者,您可以通过标记(可能是更好的解决方案,因为涉及的铸造更少):
procedure TConf.CloseCurrentPanel(Sender: TObject);
begin
  PostMessage(Handle, UM_DESTROYPANEL, TBitBtn(Sender).Tag, 0);
end;

procedure TConf.UMDestroyPanel(var Message: TMessage);
var
  panelComp: TComponent;
begin
  panelComp := FindComponentEx('Conf.MyPanel'+ IntToStr(Message.WParam));
  panelComp.Free;
end;

如果不需要TConf.CloseCurrentPanel

08-26 15:14
查看更多