Delphi 从TPanel、Firemonkey版本中删除按钮
使用Delphi10.2(在Windows10“19H2”下),我可以创建一个新的应用程序,在上面放置一个面板,以及一个包含两个项目的操作列表。这两个项调用同一个例程,其目的是删除面板上的任何按钮,然后在中添加新按钮: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
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;