我的第一个 FMX 控件(尝试创建一个 FMX TRadioGroup)- class 未找到错误
My first FMX Control (Trying to create a FMX TRadioGroup) - class not found error
使用Delphi XE6
我正在尝试使用 TGroupBox 和 TRadioButton 创建 FMX RadioGroup 控件。我可以在我的 IDE 控件面板中看到我的 TTestRadioGroup 和 TTestGroupButton 控件。我可以在我的表单上放置一个 TTestRadioGroup 并设置项目 属性,它将创建单选按钮。但是,当我 运行 应用程序并使用带有单选按钮的 TTestRadioGroup 控件调用表单时,我收到一条 "cant find TTestGroupButton" 消息。
我做错了什么?
我的第一个测试似乎工作正常,只要我在设计中。当
unit TestComponent;
interface
uses {$IFDEF MSWINDOWS}Windows, {$ENDIF}
System.Classes, FMX.Edit, System.UITypes, System.Character, FMX.DateTimeCtrls,
System.SysUtils, FMX.Types, System.DateUtils, System.SysConst, FMX.Controls,
FMX.Pickers, FMX.Platform, FMX.Text, math, FMX.Consts, FMX.Forms, FMX.StdCtrls;
type
TTestRadioGroup = class;
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
destructor Destroy; override;
end;
TTestRadioGroup = class(TGroupBox)
private
FReading: Boolean;
FButtons: TList;
FItems: TStrings;
FItemIndex: Integer;
FColumns: Integer;
FUpdating: Boolean;
FButtonMargins: Integer;
fButtonSpacing: Integer;
function GetButtons(Index: Integer): TRadioButton;
procedure SetButtonMargins(Value: Integer);
procedure SetButtonSpacing(Value: Integer);
procedure SetColumns(Value: Integer);
procedure SetItemIndex(Value: Integer);
procedure SetItems(Value: TStrings);
procedure ItemsChange(Sender: TObject);
procedure SetButtonCount(Value: Integer);
procedure ButtonClick(Sender: TObject);
procedure UpdateButtons; //updates buttons list from Items list
procedure ArrangeButtons; //rearranges buttons on Groupbox based on new properties
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Buttons[Index: Integer]: TRadioButton read GetButtons;
published
property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
property Items: TStrings read FItems write SetItems;
property Columns: Integer read FColumns write SetColumns default 1;
property ButtonMargins: Integer read fButtonMargins write SetButtonMargins default 0;
property ButtonSpacing: Integer read fButtonSpacing write SetButtonSpacing default 0;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Test', [TTestRadioGroup, TTestGroupButton]);
end;
{ TTestGroupButton }
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
end;
destructor TTestGroupButton.Destroy;
begin
TTestRadioGroup(Owner).FButtons.Remove(Self);
inherited Destroy;
end;
{ TTestRadioGroup }
constructor TTestRadioGroup.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FButtons := TList.Create;
FItems := TStringList.Create;
TStringList(FItems).OnChange := ItemsChange;
FItemIndex := -1;
FColumns := 1;
end;
destructor TTestRadioGroup.Destroy;
begin
SetButtonCount(0);
TStringList(FItems).OnChange := nil;
FItems.Free;
FButtons.Free;
inherited Destroy;
end;
procedure TTestRadioGroup.ArrangeButtons;
var
I,Y: Integer ;
begin
if (FButtons.Count <> 0) and not FReading then
begin
try
Y:= 10;
for I := 0 to FButtons.Count - 1 do
with TTestGroupButton(FButtons[I]) do
begin
Position.X:= 10;
Position.Y:= Y;
Y:= Y + 10;
Visible := True;
end;
finally
end;
end;
end;
procedure TTestRadioGroup.UpdateButtons;
var
I: Integer;
begin
SetButtonCount(FItems.Count);
for I := 0 to FButtons.Count - 1 do
TRadioButton(FButtons[I]).Text := FItems[I];
if FItemIndex >= 0 then
begin
FUpdating := True;
TRadioButton(FButtons[FItemIndex]).isChecked := True;
FUpdating := False;
end;
ArrangeButtons;
Repaint;
end;
procedure TTestRadioGroup.ButtonClick(Sender: TObject);
begin
if not FUpdating then
begin
FItemIndex := FButtons.IndexOf(Sender);
Change;
Click;
end;
end;
procedure TTestRadioGroup.ItemsChange(Sender: TObject);
begin
if not FReading then
begin
if FItemIndex >= FItems.Count then
FItemIndex := FItems.Count - 1;
UpdateButtons;
end;
end;
procedure TTestRadioGroup.SetColumns(Value: Integer);
begin
if Value < 1 then Value := 1;
if Value > 16 then Value := 16;
if FColumns <> Value then
begin
FColumns := Value;
ArrangeButtons;
Repaint;
end;
end;
procedure TTestRadioGroup.SetItemIndex(Value: Integer);
begin
if FReading then FItemIndex := Value else
begin
if Value < -1 then Value := -1;
if Value >= FButtons.Count then Value := FButtons.Count - 1;
if FItemIndex <> Value then
begin
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := False;
FItemIndex := Value;
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := True;
end;
end;
end;
procedure TTestRadioGroup.SetItems(Value: TStrings);
begin
FItems.Assign(Value);
end;
procedure TTestRadioGroup.SetButtonCount(Value: Integer);
begin
while FButtons.Count < Value do
TTestGroupButton.InternalCreate(Self);
while FButtons.Count > Value do
TTestGroupButton(FButtons.Last).Free;
end;
procedure TTestRadioGroup.SetButtonMargins(Value: Integer);
begin
if fButtonMargins <> Value then
fButtonMargins:= Value;
ArrangeButtons;
end;
procedure TTestRadioGroup.SetButtonSpacing(Value: Integer);
begin
if fButtonSpacing <> Value then
fButtonSpacing:= Value;
ArrangeButtons;
end;
function TTestRadioGroup.GetButtons(Index: Integer): TRadioButton;
begin
Result := TRadioButton(FButtons[Index]);
end;
end.
我认为第一个问题是,当您 运行 程序时,它将尝试使用 FMX 文件的副本加载对象的设计状态。这个问题是它期望 TTestGroupButton 有一个标准的 Create 构造函数,实际上你的构造函数没有,所以它使用 TRadioButton.Create 代替,这意味着在 运行 时间你的 InternalCreate 永远不会被调用。
还有第二个问题,与动态创建按钮有关,实际上可能是这个问题导致了您的第一个问题。
尝试解决此问题的一种方法可能是定义一个额外的创建。像这样:
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
...
constructor TTestGroupButton.Create(AOwner: TComponent); override;
begin
if AOwner is TTestRadioGroup then
begin
InternalCreate( AOwner as TTestRadioGroup );
end
else
begin
inherited;
end;
end;
然而,解决根本问题可能会更好,即您创建的按钮是在设计时和 运行 时动态创建的,因此要么不要在设计时创建它们,或者通过将 Stored 值设置为 FALSE 来确保它们不会像这样在设计时保存。
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
Stored := FALSE; //////////// Make sure not saved in FMX file
end;
使用Delphi XE6
我正在尝试使用 TGroupBox 和 TRadioButton 创建 FMX RadioGroup 控件。我可以在我的 IDE 控件面板中看到我的 TTestRadioGroup 和 TTestGroupButton 控件。我可以在我的表单上放置一个 TTestRadioGroup 并设置项目 属性,它将创建单选按钮。但是,当我 运行 应用程序并使用带有单选按钮的 TTestRadioGroup 控件调用表单时,我收到一条 "cant find TTestGroupButton" 消息。
我做错了什么?
我的第一个测试似乎工作正常,只要我在设计中。当
unit TestComponent;
interface
uses {$IFDEF MSWINDOWS}Windows, {$ENDIF}
System.Classes, FMX.Edit, System.UITypes, System.Character, FMX.DateTimeCtrls,
System.SysUtils, FMX.Types, System.DateUtils, System.SysConst, FMX.Controls,
FMX.Pickers, FMX.Platform, FMX.Text, math, FMX.Consts, FMX.Forms, FMX.StdCtrls;
type
TTestRadioGroup = class;
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
destructor Destroy; override;
end;
TTestRadioGroup = class(TGroupBox)
private
FReading: Boolean;
FButtons: TList;
FItems: TStrings;
FItemIndex: Integer;
FColumns: Integer;
FUpdating: Boolean;
FButtonMargins: Integer;
fButtonSpacing: Integer;
function GetButtons(Index: Integer): TRadioButton;
procedure SetButtonMargins(Value: Integer);
procedure SetButtonSpacing(Value: Integer);
procedure SetColumns(Value: Integer);
procedure SetItemIndex(Value: Integer);
procedure SetItems(Value: TStrings);
procedure ItemsChange(Sender: TObject);
procedure SetButtonCount(Value: Integer);
procedure ButtonClick(Sender: TObject);
procedure UpdateButtons; //updates buttons list from Items list
procedure ArrangeButtons; //rearranges buttons on Groupbox based on new properties
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Buttons[Index: Integer]: TRadioButton read GetButtons;
published
property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
property Items: TStrings read FItems write SetItems;
property Columns: Integer read FColumns write SetColumns default 1;
property ButtonMargins: Integer read fButtonMargins write SetButtonMargins default 0;
property ButtonSpacing: Integer read fButtonSpacing write SetButtonSpacing default 0;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Test', [TTestRadioGroup, TTestGroupButton]);
end;
{ TTestGroupButton }
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
end;
destructor TTestGroupButton.Destroy;
begin
TTestRadioGroup(Owner).FButtons.Remove(Self);
inherited Destroy;
end;
{ TTestRadioGroup }
constructor TTestRadioGroup.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FButtons := TList.Create;
FItems := TStringList.Create;
TStringList(FItems).OnChange := ItemsChange;
FItemIndex := -1;
FColumns := 1;
end;
destructor TTestRadioGroup.Destroy;
begin
SetButtonCount(0);
TStringList(FItems).OnChange := nil;
FItems.Free;
FButtons.Free;
inherited Destroy;
end;
procedure TTestRadioGroup.ArrangeButtons;
var
I,Y: Integer ;
begin
if (FButtons.Count <> 0) and not FReading then
begin
try
Y:= 10;
for I := 0 to FButtons.Count - 1 do
with TTestGroupButton(FButtons[I]) do
begin
Position.X:= 10;
Position.Y:= Y;
Y:= Y + 10;
Visible := True;
end;
finally
end;
end;
end;
procedure TTestRadioGroup.UpdateButtons;
var
I: Integer;
begin
SetButtonCount(FItems.Count);
for I := 0 to FButtons.Count - 1 do
TRadioButton(FButtons[I]).Text := FItems[I];
if FItemIndex >= 0 then
begin
FUpdating := True;
TRadioButton(FButtons[FItemIndex]).isChecked := True;
FUpdating := False;
end;
ArrangeButtons;
Repaint;
end;
procedure TTestRadioGroup.ButtonClick(Sender: TObject);
begin
if not FUpdating then
begin
FItemIndex := FButtons.IndexOf(Sender);
Change;
Click;
end;
end;
procedure TTestRadioGroup.ItemsChange(Sender: TObject);
begin
if not FReading then
begin
if FItemIndex >= FItems.Count then
FItemIndex := FItems.Count - 1;
UpdateButtons;
end;
end;
procedure TTestRadioGroup.SetColumns(Value: Integer);
begin
if Value < 1 then Value := 1;
if Value > 16 then Value := 16;
if FColumns <> Value then
begin
FColumns := Value;
ArrangeButtons;
Repaint;
end;
end;
procedure TTestRadioGroup.SetItemIndex(Value: Integer);
begin
if FReading then FItemIndex := Value else
begin
if Value < -1 then Value := -1;
if Value >= FButtons.Count then Value := FButtons.Count - 1;
if FItemIndex <> Value then
begin
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := False;
FItemIndex := Value;
if FItemIndex >= 0 then
TRadioButton(FButtons[FItemIndex]).isChecked := True;
end;
end;
end;
procedure TTestRadioGroup.SetItems(Value: TStrings);
begin
FItems.Assign(Value);
end;
procedure TTestRadioGroup.SetButtonCount(Value: Integer);
begin
while FButtons.Count < Value do
TTestGroupButton.InternalCreate(Self);
while FButtons.Count > Value do
TTestGroupButton(FButtons.Last).Free;
end;
procedure TTestRadioGroup.SetButtonMargins(Value: Integer);
begin
if fButtonMargins <> Value then
fButtonMargins:= Value;
ArrangeButtons;
end;
procedure TTestRadioGroup.SetButtonSpacing(Value: Integer);
begin
if fButtonSpacing <> Value then
fButtonSpacing:= Value;
ArrangeButtons;
end;
function TTestRadioGroup.GetButtons(Index: Integer): TRadioButton;
begin
Result := TRadioButton(FButtons[Index]);
end;
end.
我认为第一个问题是,当您 运行 程序时,它将尝试使用 FMX 文件的副本加载对象的设计状态。这个问题是它期望 TTestGroupButton 有一个标准的 Create 构造函数,实际上你的构造函数没有,所以它使用 TRadioButton.Create 代替,这意味着在 运行 时间你的 InternalCreate 永远不会被调用。
还有第二个问题,与动态创建按钮有关,实际上可能是这个问题导致了您的第一个问题。
尝试解决此问题的一种方法可能是定义一个额外的创建。像这样:
TTestGroupButton = class(TRadioButton)
private
protected
public
constructor InternalCreate(RadioGroup: TTestRadioGroup);
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
...
constructor TTestGroupButton.Create(AOwner: TComponent); override;
begin
if AOwner is TTestRadioGroup then
begin
InternalCreate( AOwner as TTestRadioGroup );
end
else
begin
inherited;
end;
end;
然而,解决根本问题可能会更好,即您创建的按钮是在设计时和 运行 时动态创建的,因此要么不要在设计时创建它们,或者通过将 Stored 值设置为 FALSE 来确保它们不会像这样在设计时保存。
constructor TTestGroupButton.InternalCreate(RadioGroup: TTestRadioGroup);
begin
inherited Create(RadioGroup);
RadioGroup.FButtons.Add(Self);
Visible := False;
Enabled := RadioGroup.Enabled;
OnClick := RadioGroup.ButtonClick;
Parent := RadioGroup;
Stored := FALSE; //////////// Make sure not saved in FMX file
end;