【问题标题】:My first FMX Control (Trying to create a FMX TRadioGroup) - class not found error我的第一个 FMX 控件(尝试创建 FMX TRAdioGroup) - 找不到类错误
【发布时间】:2016-03-24 00:55:15
【问题描述】:

使用德尔福 XE6

我正在尝试使用 TGroupBox 和 TRAdioButton 创建 FMX RadioGroup 控件。我可以在我的 IDE 控制面板中看到我的 TTestRadioGroup 和 TTestGroupButton 控件。我可以在我的表单上放置一个 TTestRadioGroup 并设置 items 属性,它将创建单选按钮。但是,当我运行应用程序并使用带有单选按钮的 TTestRadioGroup 控件调用表单时,我收到“找不到 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.

【问题讨论】:

  • 听起来你没有在你的 IDE 的库路径中包含这个单元的位置。

标签: delphi firemonkey vcl


【解决方案1】:

我认为第一个问题是,当您运行程序时,它会尝试使用 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;  

但是,最好解决根本问题,即您创建的按钮是在设计时和运行时动态创建的,因此要么不要在设计时创建它们,要么确保它们是在设计时不会通过将存储值设置为 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;

【讨论】:

  • 太棒了!非常感谢您的帮助。另外,感谢您对解决问题的两种方式的清晰简洁的解释。
  • @JakeSays:谢谢。顺便说一句,我不会在您的 Register 例程中包含 TTestGroupButton ,至少不是它的设计方式。它被构建为将 TTestRadioGroup 作为父级,因此您真的不希望能够将其作为单独的组件添加到例如速度按钮(Firemonkey 允许)中。 TTestRadioGroup 无论如何都会创建它,因此您实际上并不需要它在您的调色板上。但这是一个小问题。
猜你喜欢
  • 1970-01-01
  • 2020-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 2022-06-30
相关资源
最近更新 更多