0

I am trying to dynamiclly create a custom component with images and display them in a Grid , but the Images don't show up. Below is the code with omitted part of declarations , could someone help me and tell me what am I doint wrong ?

Custom component Class

unit Tile;

interface

uses FMX.Controls, FMX.StdCtrls, System.Classes, FMX.Types, System.StrUtils ,
System.SysUtils, System.Types, System.UITypes,
  System.Variants,
  FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Ani,
  FMX.Objects, FMX.Layouts;

type
  TTileType = (Slider, Memory, Tile3D);

  TTile = class
  private
    FOnChangedText: TNotifyEvent;
    FType: TTileType;
    FControl: TComponent;
    FText: String;
    FName: String;
    FBitmap : TBitmap;
    FAlign : TAlignLayout;
    procedure TextChangedDefault(Sender: TObject);
  protected
    procedure SetText(aText: String);
    procedure TextChanged; virtual;
    procedure SetControlOnClick(AProc: TNotifyEvent);
    function GetControlOnClick: TNotifyEvent;
    procedure SetControlName(aName: String);
    procedure  SetBitmap(bitmap:TBitmap);
    procedure  SetAlign(align :TAlignLayout);
  public
    constructor Create(AParent: TFmxObject; AType: TTileType);
    destructor Destroy; override;
  published
    property Text: String read FText write SetText;
    property Name: String read FName write SetControlName;
    property Bitmap:TBitmap read FBitmap write SetBitmap;
    property Align:TAlignLayout read FAlign write SetAlign;
    property OnChangedText: TNotifyEvent read FOnChangedText
      write FOnChangedText;
    property OnClick: TNotifyEvent read GetControlOnClick
      write SetControlOnClick;
  end;

implementation


constructor TTile.Create(AParent: TFmxObject; AType: TTileType);
begin
  FType := AType;
  case FType of
    Slider:
      begin
        FControl := TButton.Create(AParent as TComponent);
        FOnChangedText := TextChangedDefault;
        (FControl as TFmxObject).Parent := AParent;
      end;
    Memory:
    begin
      FControl := TImage.Create(AParent as TComponent);
        FOnChangedText := TextChangedDefault;
        (FControl as TFmxObject).Parent := AParent;


    end;
    Tile3D:
      FControl := nil;
  else
    FControl := nil;
  end;
  FName := FControl.Name;
end;

destructor TTile.Destroy;
begin
  FControl.DisposeOf;
  inherited;
end;

function TTile.GetControlOnClick: TNotifyEvent;
begin
  case FType of
    Slider:
      begin
        Result := (FControl as TButton).OnClick;
      end;
    Memory:
      begin
        Result := (FControl as TImage).OnClick;
      end;
    Tile3D:
      begin
        // TODO
      end;
  else
    Result := nil;
  end;

end;

procedure TTile.SetControlName(aName: String);
begin
  FName := aName;
  FControl.Name := aName;
end;

procedure TTile.SetBitmap(bitmap :TBitmap);
begin
  FBitmap:=bitmap;

end;

procedure TTile.SetAlign(align :TAlignLayout);
begin
  FAlign:=align;

end;





procedure TTile.SetControlOnClick(AProc: TNotifyEvent);
begin
  case FType of
    Slider:
      begin
        (FControl as TButton).OnClick := AProc;
      end;
    Memory:
      begin
        (FControl as TImage).OnClick := AProc;
      end;
    Tile3D:
      begin
        // TODO
      end;
  end;
end;

procedure TTile.SetText(aText: String);
begin
  FText := aText;
  TextChanged;
end;

procedure TTile.TextChanged;
begin
  if Assigned(FOnChangedText) then
    FOnChangedText(Self);
end;

procedure TTile.TextChangedDefault(Sender: TObject);
begin
  (FControl as TButton).Text := FText;
end;

end.

Memory Game Class:

   unit MemoryGame;

interface

uses Tile, Consts, FMX.Controls, FMX.StdCtrls, FMX.Layouts, System.Classes,
  FMX.Types, System.Types, FMX.Graphics, System.SysUtils, FMX.Dialogs,Helper,FMX.ExtCtrls  ,

     System.UITypes,
  System.Variants,
   FMX.Forms,
   FMX.TabControl, SliderPuzzle, System.Actions,
  FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
  DateUtils ,FMX.Objects  ;


type
  TMemoryGame = class(TGridLayout)
  private
    FTiles: TArray<TTile>;

    procedure FillGrid(aTileNo: Integer);
  protected

  public
    constructor Create(AParent: TFmxObject; aTileNo: Integer); reintroduce;

  end;

  var
  moveCounter : Integer = 0 ;

implementation

{ MemoryGame }

constructor TMemoryGame.Create(AParent: TFmxObject; aTileNo: Integer);
begin
  inherited Create(nil);
  Parent := AParent;
  FillGrid(aTileNo);
end;


procedure TMemoryGame.FillGrid(aTileNo: Integer);
var
  I: Integer;
  LTile: TTile;


begin
  SetLength(FTiles, aTileNo);
  for I := 0 to aTileNo - 1 do
  begin
   LTile := TTile.Create(Self, TTileType.Memory);


   FTiles[I] := LTile;

    if I = 0 then
    begin
    LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
     LTile.Align := TAlignLayout.Client;
     LTile.Align := TAlignLayout.Center;








    end
    else
    begin

      LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
        LTile.Align := TAlignLayout.Client;
  LTile.Align := TAlignLayout.Center;
   end;
 end;
end;
end.

Main Form:

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, Consts,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.ExtCtrls,
  FMX.Layouts, FMX.TabControl, SliderPuzzle, System.Actions,
  FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
  DateUtils,MemoryGame, FMX.Objects;

type
  TFormMain = class(TForm)
    tcMain: TTabControl;
    ti1Slider: TTabItem;
    ti2Runtime: TTabItem;
    ti4Game3D: TTabItem;
    ti3Memory: TTabItem;
    GridLayout: TGridLayout;
    bTile1: TButton;
    bTile2: TButton;
    bTile3: TButton;
    bTile4: TButton;
    bTile5: TButton;
    bTile6: TButton;
    bTile7: TButton;
    bTile8: TButton;
    bTile9: TButton;
    bTile10: TButton;
    bTile11: TButton;
    bTile12: TButton;
    bTile13: TButton;
    bTile14: TButton;
    bTile15: TButton;
    bTileEmpty: TButton;
    bNew: TButton;
    MultiView: TMultiView;
    bExitApp: TButton;
    ActionList: TActionList;
    FileExitActn: TFileExit;
    NewGameActn: TAction;
    StyleBook: TStyleBook;
    hitCountLabel: TLabel;
    movesCounter: TLabel;
    TimeCountLabel: TLabel;
    timer: TLabel;
    Timer1: TTimer;
    procedure bTileClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure NewGameActnExecute(Sender: TObject);
    procedure GridLayoutResize(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);





  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;
  Slider: TSliderPuzzle;
  Memory : TMemoryGame;
  firstMove : Boolean = true;
   stop, elapsed : TDateTime  ;
   start  : TDateTime = 0  ;



implementation

{$R *.fmx}


procedure TFormMain.NewGameActnExecute(Sender: TObject);
begin
  if ti1Slider.IsSelected then
    repeat
    begin
     firstMove:=true;
        Slider.ShuffleTiles(GridLayout);
        Slider.resetMoveCounter;
         Timer1.Enabled := true;
         Timer1.Interval :=1000;
    Slider.resetTimer(start);

    movesCounter.Text := IntToStr(Slider.GetMoveCount);
   timer.Text := '--/--/--';

    end;

    until not Slider.IsGameOver(GridLayout)
  else if ti2Runtime.IsSelected then
    repeat
      Slider.ShuffleTiles
    until not Slider.IsGameOver;
end;



procedure TFormMain.Timer1Timer(Sender: TObject);
var myVar:Integer;
begin
if start<>0 then
begin

  myVar :=  SecondsBetween(start,Now);

timer.Text :=Format('%.2d:%.2d', [myVar div 60, myVar mod 60]); ;


end;


end;

procedure TFormMain.bTileClick(Sender: TObject);
begin
  if firstMove then
  begin
  Slider.startCount(start);
  firstMove:=false;

  end;



  Slider.incrementCounter;
  movesCounter.Text := IntToStr(Slider.GetMoveCount);

  Slider.SwapTiles(GridLayout, Sender as TButton, bTileEmpty);
  if Slider.IsGameOver(GridLayout) then
  begin
    Slider.resetMoveCounter;
   Slider.resetTimer(start);
   // movesCounter.Text := IntToStr(Slider.GetMoveCount);
//    timer.Text := '--/--/--';
   Timer1.Enabled := false;
        ShowMessage('GAME OVER');
        firstMove:=true;
     ti3Memory.Enabled := true;
     ti3Memory.TabControl.SetActiveTabWithTransition(ti3Memory,TTabTransition.Slide);

  end;
end;

procedure TFormMain.GridLayoutResize(Sender: TObject);
begin
  GridLayout.ItemHeight := GridLayout.Height / COLS-25;
  GridLayout.ItemWidth := GridLayout.Width / ROWS;
end;



procedure TFormMain.FormShow(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := true;
  Slider := TSliderPuzzle.Create(Self.ti2Runtime, TILES);
  Slider.Height := GridLayout.Height;
  Slider.Width := GridLayout.Width;
  Slider.Align := TAlignLayout.Client;

  //PuzzleGame

   ReportMemoryLeaksOnShutdown := true;
  Memory := TMemoryGame.Create(Self.ti3Memory, TILES);
 Memory.Height := GridLayout.Height;
  Memory.Width := GridLayout.Width;
  Memory.Align := TAlignLayout.Client;


end;



end.
bummi
  • 27,123
  • 14
  • 62
  • 101
Karudi
  • 2,692
  • 3
  • 18
  • 19
  • You are missing the call to the inherited Create constructor I think... – iPath ツ Dec 30 '14 at 16:57
  • LTile := TTile.Create(Self, TTileType.Memory); Is not it ? – Karudi Dec 30 '14 at 17:15
  • constructor TTile.Create(AParent: TFmxObject; AType: TTileType); should begin with : "inherited;" - without double quotes – iPath ツ Dec 30 '14 at 17:54
  • Your code isn't anywhere near complete enough for us to compile and test. It's missing large chunks (including the basic declaration of `TTile = class(...)`, which is a vital piece of the information). If you want help, post *complete, compilable code* that demonstrates the problem you're experiencing. – Ken White Dec 31 '14 at 17:17
  • @iPathツ: You can't tell if that's relevant or not, because the code is missing the `type TTile = (...)` that would determine whether there's a need to call the `inherited Create` or not. The code is missing too much detail to diagnose any problems. The poster needs to [edit] to include actual, complete code. – Ken White Dec 31 '14 at 17:19
  • I've added the complete code , could you possibly take a look at it now ? – Karudi Dec 31 '14 at 18:25
  • You've not added the complete code, because a `TGridLayout` needs a form on which it can display. I don't see anywhere in the code that attempts to actually display the bitmap or put it into a visual container that will display it. You're simply loading a bitmap into memory - if you want it to be shown on screen, you have to do something to put it on screen. – Ken White Dec 31 '14 at 19:46
  • I've added the form code , sorry I'm really new to Delphi and don't really know how to explain everything . – Karudi Dec 31 '14 at 21:09
  • None of the code you've added makes any effort to display `FBitmap` anywhere. Something has to take the in-memory bitmap and draw it to the screen (a `TImage`, for instance). You've done nothing but load it from disk into memory. Think of an old camera with film - you've taken the picture and got it on film, but you've not had it developed yet. You need to take that step from loading it into memory to putting it on the screen somehow. – Ken White Dec 31 '14 at 21:12
  • Could you advise me how can I do it inside the FillGrid procedure ? – Karudi Dec 31 '14 at 21:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/68006/discussion-between-karudi-and-ken-white). – Karudi Dec 31 '14 at 21:53
  • I'm afraid I don't have time today to tutor you in chat. There are demos available that are provided with your copy of Delphi/RAD Studio that demonstrate how to display graphics. See Start->All Programs->Embarcadero ->Samples. – Ken White Dec 31 '14 at 23:05

3 Answers3

0

Call the assign() method of the FBitmap variable inside youe Set procedure:

procedure TTile.SetBitmap(bitmap :TBitmap);
begin
  FBitmap.Assign(bitmap);

end;
blackmes4
  • 140
  • 10
  • This is wrong, because there's no `FBitmap` yet created from which you can call `Assign`. Your code would result in an access violation. – Ken White Dec 31 '14 at 17:15
0

Adding the following code to Tile class , fixed the issues.

type 
  private 

  FOnChangedBitmap : TNotifyEvent;

protected 
  procedure BitmapChanged;virtual;


procedure TTile.BitmapChanged;
begin
  if Assigned(FOnChangedBitmap) then
    FOnChangedBitmap(Self);
end;

procedure TTile.BitmapChangedDefault(Sender: TObject);
begin
  (FControl as TImage).Bitmap := FBitmap;
end;

procedure TTile.SetBitmap(bitmap :TBitmap);
begin
  FBitmap:=bitmap;
  BitmapChanged;

end;
Karudi
  • 2,692
  • 3
  • 18
  • 19
0

This all looks very complicated and perhaps it is. But I solved a similar problem by simply setting the parent of the image:

  Fheart := TImage.Create(self);
  Fheart.Parent := self;
  Fheart.SetSubComponent(true);

It seems unneccessary setting the parent when that is passed as the owner in the constructor - but it did solve my problem