Monday, 30 July 2018

Delphi - Sort an Object List by Property


Below is a quick example of how to use the .Sort function. In this case i have two objects declared in the Type seciton. On form create i am creating an instance of TPersons, then creating a number of dummy TPersons and adding them to my 'Persons'. Then behind a button click i am sorting the order of the Persons using the following code:

Persons.Sort(TComparer<TPerson>.Construct(
      function (const L,R: TPerson): integer
      begin
        if L.Name > R.Name then
          Result := 1
        else if L.Name < R.Name then
          Result := -1
        else
          Result := 0;
      end
      ));
  end;


To recreate this simple example, drop a memo and a button on a form. The make your code look like this:

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, Generics.Collections, FMX.ScrollBox,
  FMX.Memo, Generics.Defaults;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TPerson = class(TObject)
    Name: string;
    Age: integer;
  end;

  TPersons = class(TObjectList<TPerson>)
  end;


var
  Form2: TForm2;

implementation

{$R *.fmx}

var
  Persons: TPersons;


procedure TForm2.Button1Click(Sender: TObject);
var
  Person: TPerson;
  I: Integer;
begin
  for Person in Persons do
  begin
    Persons.Sort(TComparer<TPerson>.Construct(
      function (const L,R: TPerson): integer
      begin
        if L.Name > R.Name then
          Result := 1
        else if L.Name < R.Name then
          Result := -1
        else
          Result := 0;
      end
      ));
  end;

  for I := 0 to Persons.Count -1 do
  begin
    Memo1.Lines.Add(Persons[I].Name);
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  Person: TPerson;
  I: Integer;
begin
  Persons := TPersons.Create(true);

  Person := TPerson.Create;
  Person.Name := 'John';
  Person.Age := 34;
  Persons.Add(Person);

  Person := TPerson.Create;
  Person.Name := 'James';
  Person.Age := 31;
  Persons.Add(Person);

  Person := TPerson.Create;
  Person.Name := 'Sally';
  Person.Age := 21;
  Persons.Add(Person);
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  Persons.Free;
end;

end.

Monday, 23 July 2018

Delphi FMX/Firemonkey - Fill a Rect/RoundRect/Ellipse with a Image/Bitmap

Here is a simple example of how to fill a given shape (TRectangle, TRoundRect, TEllipse etc) with the bitmap which belongs to an image. In this example i have created a new project, dropped a button and an RoundRect onto the form. On the Button Click i have the following code:

procedure TForm1.Button1Click(Sender: TObject);
var
  Image: TImage;
begin
  Image := TImage.Create(self);
  Image.Bitmap.LoadFromFile('C:\Temp\Test.jpg');


  RoundRect1.Fill.Kind := TbrushKind.Bitmap;
  RoundRect1.Fill.Bitmap.WrapMode := TWrapMode.TileStretch;
  RoundRect1.Fill.Bitmap.Bitmap := Image.Bitmap;
end;


This will create a new instance of a TImage. Load my Test.jpg file into its Bitmap property. Then set the Image to fill the space in the RoundRect.

Thursday, 19 July 2018

Delphi - Set Font Property of a Component in Firemonkey/FMX

In order to set the font property of a component in FMX there are two ways you can do it:

1) In design time. By dropping a component onto the form and then changing its properties in the Object Inspector you can alter the properties of the font (font fmaily, size, color etc).

2) At run time. If, for example, you have a project which drops a series of labels onto a form when a button is clicked then you'll need to handle the changing of the font in your code.

 Option 1 is pretty straight forward but it's worth noting that if you make this change, you can view the effect this has had by saving your project and then opening up the .fmx file in notedpad and looking to see what has changed. In my project, i added a label to my form and changed the Text Settings > Font Color to be 'crimson'. In my .fmx file i can see that there are now two new lines:

    object Label1: TLabel
      Position.X = 120.000000000000000000
      Position.Y = 352.000000000000000000
      TextSettings.FontColor = claCrimson

      StyledSettings = [Family, Size, Style]
      Text = 'Label1'
    end 


This shows us what the impact was of changing the font color. The first change is to be expected since the property we changed was the FontColor. The second change is important, however, since it shows us what other change delphi made under the covers - namely that delphi altered the StyledSettings. The styled settings tell delphi whether to pull down the projects style settings (i.e. those which may be applied through a stylebook) for a given setting. Thus if you go back to design time and drop down the StyledSettings option, you'll see that 'FontColor' is currently unchecked. If you check the box against it, the font in the label will cease to be red.  

Thus, if you're creating new components at run time and need to alter some property of the font style, you'll need to include two lines - one to set the font setting and one to uncheck the relevant setting in StyledSettings:

MyLabel.TextSettings.FontColor := TAlphaColor($FF009900);
MyLabel.StyledSettings := [TStyledSetting.Family, TStyledSetting.Size, TStyledSetting.Style]; 

Monday, 9 July 2018

Delphi - Load Image From Project Resource (FMX) - Simple Example

For this example i have a created a new FMX (firemonkey) form. I have added a button and an image onto the. I have then gone to Project > Resources and Images, and loaded in a bitmap image. I have set:
Resource Identifier: 'Bitmap_1'.
Resource Type: 'RCDATA'
Back on my form i have double clicked on the button and behind the button click put the code to instantiate a resource stream, load the image into it, assign it to a bitmap and then set it to be the image on the form.


unit Unit2;

interface

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

type
  TForm2 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

procedure TForm2.Button1Click(Sender: TObject);
var
  B: TBitmap;
  RS: TResourceStream;
begin
  RS := TResourceStream.Create(HInstance, 'Bitmap_1', RT_RCDATA);
  try
    B := TBitmap.Create;
    B.LoadFromStream(RS);
    Image1.Bitmap := B;
  finally
    RS.Free;
  end;
end;

end.