如何停止复制子组件(Delphi 10.1 Berlin Firemonkey)

时间:2019-06-06 19:32:42

标签: delphi firemonkey

我正在使用发布的“ VisibleItems”属性创建组件。但是,当选中/取消选中某项的属性时,就会从组件中添加或删除某项并将其存储在dfm中(这是可行的);当组件从dfm流到IDE时,将创建重复项。我已经覆盖了该项目(TMyTreeViewItem)的受保护的“加载”过程,并向父组件(TDesignTimeTreeView)添加了“ CreateDefaultItem”过程,该过程似乎适用于Windows,但不适用于Android。

我尝试使用“存储的False”指令在父组件和子组件中标记属性。我还尝试覆盖主要组件中的“加载”过程。

下面是该组件的最低版本。如果绕过TMyTreeViewItem.Loaded和TDesignTimeTreeView.CreateDefaultItem,则在“以文本查看”和“以窗体查看”(Alt + F12)之间切换时,组件复制会在IDE中开始发生。

unit DesignTimeTreeView;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,

  FMX.Types,
  FMX.Controls,
  FMX.Layouts,
  FMX.TreeView;

type
  {$SCOPEDENUMS ON}
  TItemId = (NONE, Default, Custom, One, Two);
  {$SCOPEDENUMS OFF}

  TMyTreeViewItem = class(TTreeViewItem)
    private
      FId : TItemId;

      procedure SetFId(const Value: TItemId);
    protected
      procedure Loaded; override;
    published
      property Id: TItemId read FId write SetFid;
  end;

  TVisibleItemsChange = procedure(Id: TItemId; Visible: Boolean) of object;

  TVisibleItems = class(TPersistent)
    private
      FItem1 : Boolean;
      FItem2 : Boolean;

      FOnChange : TVisibleItemsChange;

      procedure SetVisibility(Id: TItemId; Visible: Boolean);
      procedure SetFItem1(const Visible: Boolean);
      procedure SetFItem2(const Visible: Boolean);

      property OnChange: TVisibleItemsChange read FOnChange write FOnChange;
    published
      property Item1: Boolean read FItem1 write SetFItem1;
      property Item2: Boolean read FItem2 write SetFItem2;
  end;

  TDesignTimeTreeView = class(TTreeView)
    private
      { Private declarations }
      FVisibleItems          : TVisibleItems;
      FDefaultMyTreeViewItem : TMyTreeViewItem;

      function GetItemById(Id: TItemId): TMyTreeViewItem;
      function GetItemName(Id: TItemId): string;
      procedure AddItem(Id: TItemId);
      procedure OnVisibilityChange(Id: TItemId; Visible: Boolean);
      procedure OnItemClick(Sender: TObject);
      procedure OnItemTap(Sender: TObject; const Point: TPointF);
      procedure CreateDefaultItem;
      const
        PREFIX = 'StandardItem_';
    protected
      { Protected declarations }
    public
      { Public declarations }
      constructor Create(AOwner: TComponent); override;
    published
      { Published declarations }
      property VisibleItems: TVisibleItems read FVisibleItems write FVisibleItems;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TMyTreeViewItem, TDesignTimeTreeView]);
end;

{ TDesignTimeTreeView }

procedure TDesignTimeTreeView.AddItem(Id: TItemId);
var
  Item     : TMyTreeViewItem;
  ItemName : string;
  i        : Integer;
  Sub      : TTreeViewItem;
begin
  ItemName := GetItemName(Id);
  Item     := GetItemById(Id);

  if Item <> nil then
    RemoveObject(Item);

  Item         := TMyTreeViewItem.Create(nil);
  Item.FId     := Id;
  Item.Text    := ItemName;
  Item.Name    := ItemName;
  Item.HitTest := True;
  Item.OnClick := OnItemClick;
  Item.OnTap   := OnItemTap;
  Item.Parent  := Self;

  for i := 1 to 10 do
  begin
    Sub        := TTreeViewItem.Create(nil);
    Sub.Text   := 'Sub Item (' + i.ToString + ')';
    Sub.Parent := Item;
  end;
end;

constructor TDesignTimeTreeView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  ItemHeight := 46;

  CreateDefaultItem;

  FVisibleItems := TVisibleItems.Create;
  FVisibleItems.OnChange := OnVisibilityChange;

  { Set the default values }
  FVisibleItems.FItem1 := False;
  FVisibleItems.FItem2 := False;
end;

procedure TDesignTimeTreeView.CreateDefaultItem;
var
  Item : TMyTreeViewItem;
const
  DEFAULT_ITEM_NAME = 'DEFAULT_MYTREEVIEWITEM';
begin
  if Owner <> nil then
    if Owner.FindComponent(DEFAULT_ITEM_NAME) <> nil then
    begin
      Item := TMyTreeViewItem(Owner.FindComponent(DEFAULT_ITEM_NAME));
      if Item.Id <> TItemId.Default then
        Item.Free
      else
        Exit;
    end;

  if Assigned(FDefaultMyTreeViewItem) then
    FreeAndNil(FDefaultMyTreeViewItem);

  FDefaultMyTreeViewItem      := TMyTreeViewItem.Create(Owner);
  FDefaultMyTreeViewItem.FId  := TItemId.Default;
  FDefaultMyTreeViewItem.Name := DEFAULT_ITEM_NAME;
  FDefaultMyTreeViewItem.Text := 'Created by TDesignTimeTreeView. This makes it so you do not have ' +
                          'to add a TMyTreeViewItem on the form just so this component will ' +
                          'work.';
  FDefaultMyTreeViewItem.Parent  := TFmxObject(Owner);
  FDefaultMyTreeViewItem.Visible := False;
end;

function TDesignTimeTreeView.GetItemById(Id: TItemId): TMyTreeViewItem;
var
  i        : Integer;
  ItemName : string;
begin
  Result   := nil;
  ItemName := GetItemName(Id);

  for i := 0 to Count - 1 do
    if Items[i].Name = ItemName then
      Result := TMyTreeViewItem(Items[i]);
end;

function TDesignTimeTreeView.GetItemName(Id: TItemId): string;
begin
  Result := PREFIX + Ord(Id).ToString;
end;

procedure TDesignTimeTreeView.OnItemClick(Sender: TObject);
var
  Item : TMyTreeViewItem;
begin
  Item := TMyTreeViewItem(Sender);

  Item.Text := Ord(Item.Id).ToString
             + ' Clicked (' + FormatDateTime('h:nn:ss.zzzAM/PM', Now) + ')';
end;

procedure TDesignTimeTreeView.OnItemTap(Sender: TObject; const Point: TPointF);
begin
  OnItemClick(Sender);
end;

procedure TDesignTimeTreeView.OnVisibilityChange(Id: TItemId; Visible: Boolean);
var
  Item : TMyTreeViewItem;
begin
  Item := GetItemById(Id);

  if Item <> nil then
    RemoveObject(Item);

  if Visible then
    AddItem(Id);
end;

{ TVisibleItems }

procedure TVisibleItems.SetFItem1(const Visible: Boolean);
begin
  SetVisibility(TItemId.One, Visible);
end;

procedure TVisibleItems.SetFItem2(const Visible: Boolean);
begin
  SetVisibility(TItemId.Two, Visible);
end;

procedure TVisibleItems.SetVisibility(Id: TItemId; Visible: Boolean);
begin
  case Id of
    TItemId.Custom: { Do Nothing };
    TItemId.One:
      FItem1 := Visible;
    TItemId.Two:
      FItem2 := Visible;
  end;

  if Assigned(FOnChange) then
    FOnChange(Id, Visible);
end;

{ TMyTreeViewItem }

procedure TMyTreeViewItem.Loaded;
begin
  if Id = TItemId.NONE then
  begin
    Self.Destroy;
    Exit;
  end;

  if Id = TItemId.Default then
    Exit;

  if not (Parent is TDesignTimeTreeView) then
    Self.Destroy;

  inherited;
end;

procedure TMyTreeViewItem.SetFId(const Value: TItemId);
begin
  { Read Only: But we want to see it in the .fmx file of the form }
end;

end.

我希望当选中VisibleItems.Item1时,将TMyTreeViewItem添加到组件中并存储在dfm中(正在运行)。该组件似乎也正在从dfm正确传输到IDE。我还希望程序运行时(在Windows上运行)在窗体上(如果已选中)看到Item1和Item2。

当我将此组件放到多设备应用程序表单上时,将VisibleItems.Item1和Item2设置为true,最后将其部署到Android设备上,会出现以下错误:

*。apk引发了ececption类EComponentError,并显示消息“已存在名为DEFAULT_MYTREEVIEWITEM的组件”。

*。apk引发了异常类细分错误(11)。

*。apk引发了异常类非法指令(4)。

0 个答案:

没有答案