我正在使用发布的“ 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)。