Using Tedit Inside of a FireMonkey component

940 views Asked by At

I have created a component in Fire-monkey and also created a TEdit inside it. My component has a String Property named Value that by pouting any string to it , My Component will show that in Tedit. at design Time every thing is OK. but at Run time no thing shows in Tedit My Code is

type
TMyComponent = class(TPanel)
private
  { Private declarations }
  Edit:TEdit;
  FValue:String;
  Procedure SetValue(Const Value:String);
protected
  { Protected declarations }
  Constructor Create(Aoner:TComponent); Override;
  Destructor Destroy; Override;
public
  { Public declarations }
published
  { Published declarations }
  Property Value:String Read FValue Write SetValue;
end;

procedure Register;

implementation

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

Constructor TMyComponent.Create(Aoner:TComponent);
begin
  Inherited;
  Width:=100;
  Height:=100;
  Edit:=TEdit.Create(Self);
  Edit.Parent:=Self;
  Edit.Width:=30;
  Edit.Text:='';
  Edit.Align:=TAlignLayout.Scale;
end;

Procedure TMyComponent.SetValue(const Value: string);
begin
  FValue:=Value;
  Edit.Text:=FValue;
end;

Destructor TMyComponent.Destroy;
begin
  Edit.Destroy;
  Inherited Destroy;
end;
end.

What Should I do?

2

There are 2 answers

0
Tom Brunberg On BEST ANSWER

Initially I thought you were having problems both with creating and using your component at runtime. Instantiating and using your component at runtime works perfectly ok.

After the comment by EugeneK I now understand that the component doesn't work, if you instantiate it at design time and attempt to change the text property of the TEdit at runtime.

The reason can be traced to the fact that your component is a composed type and constructed from subcomponents (well, only a TEdit). If the Stored property of the subcomponent(s) are not set to False they might get streamed several times at design time, f.ex. when you switch between form view and text view of the form.

See documentation and scroll down to TCalender: Constructed Complexity where you can find the following:

Every object in the component tree has its Stored property set to False and its Locked property set to True. Disabling Stored prevents the object from being streamed out to the .fmx file by the Form Designer. If the Stored property is not disabled, subcomponents would be redundantly created when loading.

The result after switching the form view to text view twice:

  object MyComponent1: TMyComponent
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    Size.Width = 100.000000000000000000
    Size.Height = 100.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 5
    object TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Scale
      TabOrder = 1
      Size.Width = 90.000000000000000000
      Size.Height = 18.333332061767580000
      Size.PlatformDefault = False
    end
    object TEdit
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      Align = Scale
      TabOrder = 0
      Size.Width = 90.000000000000000000
      Size.Height = 18.333332061767580000
      Size.PlatformDefault = False
    end
  end

while this is how it should look like:

  object MyComponent1: TMyComponent
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    Size.Width = 100.000000000000000000
    Size.Height = 100.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 5
  end

The redundant TEdit controls will cover the one you create in the constructor.

The correction to your code: Add the marked line

constructor TMyComponent.Create(Aoner: TComponent);
begin
  inherited;
  Width:=100;
  Height:=100;
  Edit:=TEdit.Create(Self);
  Edit.Parent:=Self;
  Edit.Width:=90;
  Edit.Align:=TAlignLayout.Scale;
  Edit.Stored := False;  // *** add this line ***
end;
0
EugeneK On

You are supposed to do this kind of things using styles in FireMonkey, so for your example you need MyComponent.style file as following

object TRectangle
  StyleName = 'MyComponentStyle'
  Align = Center
  Fill.Color = xFFF0F0F0
  HitTest = False
  Size.Width = 159.000000000000000000
  Size.Height = 131.000000000000000000
  Size.PlatformDefault = False
  Stroke.Color = xFFCCCCCC
  object TEdit
    StyleName = 'MyComponentEditStyle'
    Touch.InteractiveGestures = [LongTap, DoubleTap]
    Align = Scale
    TabOrder = 0
    Size.Width = 79.499755859375000000
    Size.Height = 21.833337783813480000
    Size.PlatformDefault = False
  end
end

then you need to include MyComponent.rc in the project file as following

MyComponentStyle RCDATA  "MyComponent.style"

and your MyComponent.pas will be

unit MyComponent;

interface

uses System.Classes, FMX.Types, FMX.Controls, FMX.StdCtrls, FMX.Edit, FMX.Styles;

type
  TMyComponent = class(TPanel)
  private
    { Private declarations }
    FEdit: TEdit;
    FValue: string;
    procedure SetValue(const AValue: string);
    function GetValue: string;
  protected
    { Protected declarations }
    procedure FreeStyle; override;
    procedure ApplyStyle; override;
    function GetStyleObject: TFmxObject; override;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    property Value: string read GetValue write SetValue;
  end;

procedure Register;

implementation

uses Winapi.Windows;

{$R MyComponent.res}

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

procedure TMyComponent.ApplyStyle;
begin
  inherited;
  if FindStyleResource<TEdit>('MyComponentEditStyle', FEdit) then
  begin
    FEdit.Visible := True;
    FEdit.Text := Value;
  end;
end;

function TMyComponent.GetStyleObject: TFmxObject;
const
  Style = 'MyComponentStyle';
begin
  if (StyleLookup = '') then
    Result := TStyleStreaming.LoadFromResource(HInstance, Style, RT_RCDATA)
  else
    Result := inherited GetStyleObject;
end;

procedure TMyComponent.FreeStyle;
begin
  FEdit := nil;
  inherited;
end;

constructor TMyComponent.Create(AOwner: TComponent);
begin
  inherited;
  Width := 100;
  Height := 100;
end;

procedure TMyComponent.SetValue(const AValue: string);
begin
  FValue := AValue;
  if Assigned(FEdit) then
  begin
    FEdit.Text := FValue;
  end;
end;

function TMyComponent.GetValue: string;
begin
  Result := FValue;
end;

end.