Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 从TPanel、Firemonkey版本中删除按钮_Delphi_Firemonkey - Fatal编程技术网

Delphi 从TPanel、Firemonkey版本中删除按钮

Delphi 从TPanel、Firemonkey版本中删除按钮,delphi,firemonkey,Delphi,Firemonkey,使用Delphi10.2(在Windows10“19H2”下),我可以创建一个新的应用程序,在上面放置一个面板,以及一个包含两个项目的操作列表。这两个项调用同一个例程,其目的是删除面板上的任何按钮,然后在中添加新按钮: procedure TForm1.CreateNavPanelButtons(Action: TAction); begin NavPanel.RemoveObject(Btn); Btn.DisposeOf; //problem line Btn := MakeB

使用Delphi10.2(在Windows10“19H2”下),我可以创建一个新的应用程序,在上面放置一个面板,以及一个包含两个项目的操作列表。这两个项调用同一个例程,其目的是删除面板上的任何按钮,然后在中添加新按钮:

procedure TForm1.CreateNavPanelButtons(Action: TAction);
begin
  NavPanel.RemoveObject(Btn);
  Btn.DisposeOf; //problem line

  Btn := MakeButton(Action);
  NavPanel.AddObject(Btn);
end;
(我已经简化为只使用一个按钮。)删除现有按钮,在中添加新按钮。如果我调用DisposeOf(释放按钮的内存),窗口对象将变得无响应(无法调整大小、移动、关闭),直到我将焦点从应用程序移开

我已将整个代码包括在下面:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, System.Actions, FMX.ActnList;

type
  TForm1 = class(TForm)
    NavPanel: TPanel;
    ActionList: TActionList;
    acNextMenu: TAction;
    acBackToMainMenu: TAction;
    procedure FormCreate(Sender: TObject);
    procedure acNextMenuExecute(Sender: TObject);
  private
    { Private declarations }
  public
    Btn: TButton;
    procedure CreateNavPanelButtons(Action: TAction);
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

function MakeButton(A: TAction): TButton;
begin
  Result := TButton.Create(nil);
  Result.Action := A;
  Result.Text := (A as TAction).Text;
end;

procedure TForm1.acNextMenuExecute(Sender: TObject);
begin
  CreateNavPanelButtons(acBackToMainMenu);
end;

procedure TForm1.CreateNavPanelButtons(Action: TAction);
begin
  NavPanel.RemoveObject(Btn);
  Btn.DisposeOf;

  Btn := MakeButton(Action);
  NavPanel.AddObject(Btn);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateNavPanelButtons(acNextMenu);
end;

end.
以下是表格:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 480
  ClientWidth = 640
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object NavPanel: TPanel
    Align = Top
    Size.Width = 640.000000000000000000
    Size.Height = 73.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
  end
  object ActionList: TActionList
    Left = 392
    Top = 192
    object acNextMenu: TAction
      Category = 'Navigation'
      Text = 'NextMenu'
      OnExecute = acNextMenuExecute
    end
    object acBackToMainMenu: TAction
      Category = 'Navigation'
      Text = 'Back To &Main Menu'
      OnExecute = FormCreate
    end
  end
end

代码的问题是,您正在删除操作当前正在运行的按钮。当操作返回时,按钮不再存在,在Windows上,它由
DisposeOf()
释放,在移动平台上,它处于“僵尸”状态

解决方法是延迟删除按钮,直到操作结束。在标准Windows应用程序中,我会向自己发布一条消息,以确保在收到消息之前操作已结束,并可以调用
CreateNavPanelButtons()
。但我不确定这是否适用于所有其他平台

以下内容应适用于任何平台

添加
TTimer
Enabled=False
Interval=1
。然后声明表单的私有字段,
Action:TAction

更改任何更改
NavPanelButtons
的操作处理程序,如下所示:

procedure TForm2.acNextMenuExecute(Sender: TObject);
begin
//  CreateNavPanelButtons(acBackToMainMenu);
  Action := acBackToMainMenu;
  Timer1.Enabled := True;
end;
并添加
OnTimer
事件

procedure TForm2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  if Action <> nil then
     CreateNavPanelButtons(Action);
end;
程序TForm2.Timer1Timer(发送方:TObject);
开始
Timer1.Enabled:=False;
如果没有行动,那么
CreateNavPanelButtons(操作);
结束;

避免
TTimer的更新

另一个不需要消息或计时器的解决方案是在前面创建所有按钮,而在程序运行期间根本不处理它们

可以将它们分组到按钮列表中,这些列表将同时显示相关的按钮

当需要显示按钮列表时,
NavPanel
中的旧按钮只需通过
NavPanel从面板中移除(无
B.DisposeOf
)。在循环中移除对象(B)

最后,新的按钮列表将通过按钮列表do NavPanel.AddObject(b)中的b添加到面板中


这样做的缺点是内存占用更大,以防万一。

汤姆有正确的答案。我不喜欢定时器的用户,因为我不喜欢中断代码流,除非我必须这样做,所以我设计了一个双面板系统:

  TForm1 = class(TForm)
    NavPanel1: TPanel;
    NavPanel2: TPanel;
. . .
    FrontPanel: TPanel;
    BackPanel: TPanel;
释放后,我将所有按钮放在后面板上,然后将其移到前面/取消隐藏。(这段代码实际上是为多个按钮设计的,所以有点复杂。)


但这增加了复杂性,包括需要两个面板以及在表单上将它们放置在彼此上方等,这可能比使用计时器更复杂。所以我可以使用定时器解决方案。我将此作为备选方案发布。

请显示与此相关的所有代码,尤其是声明。。我不知道NvPanelButtons、NavPanel、MakeButton是什么,也不知道它们是什么。包括它们和简化的MakeButton。我可以使用RemoveObject,但内存仍然被占用。我正在尝试处置内存,但我采取的任何步骤都会导致此行为。请提供一个复制此问题的。您发布了两个完全脱离上下文的过程。A将包括完整的源代码(包括.fmx文件的文本内容)和源代码.pas文件。它应该能够被复制并直接粘贴到IDE中并运行,以便重现您描述的问题。好的。事情是这样的。正如你所指出的,战术是关键。我最终采用了一种稍有不同的方法,如下所示,但我可能会使用您的方法,因为(尽管使用了计时器),它实际上更简单。
procedure TForm1.CreateNavPanelButtons(Action: TAction);
  procedure Swap;
  var P: TPanel;
  begin
    P := BackPanel;
    BackPanel := FrontPanel;
    FrontPanel := P;
    BackPanel.Visible := false;
    FrontPanel.Visible := true;
  end;
var P: TPanel;
    B: TButton;
    I: Integer;
begin
   for I := BackPanel.ChildrenCount-1 downto 0 do
   if BackPanel.Children[I] is TButton then
   begin
      B := BackPanel.Children[I] as TButton;
      BackPanel.RemoveObject(B);
      B.DisposeOf;
   end;
   BackPanel.AddObject(MakeButton(Action));
   Swap;
end;