12 min read

This article written by Daniele Teti, the author of Delphi Cookbook, explains the process of writing enumerable types. It also discusses the steps to customize FireMonkey controls.

(For more resources related to this topic, see here.)

Writing enumerable types

When the for…in loop was introduced in Delphi 2005, the concept of enumerable types was also introduced into the Delphi language.

As you know, there are some built-in enumerable types. However, you can create your own enumerable types using a very simple pattern.

To make your container enumerable, implement a single method called GetEnumerator, that must return a reference to an object, interface, or record, that implements the following three methods and one property (in the sample, the element to enumerate is TFoo):

   function GetCurrent: TFoo;
   function MoveNext: Boolean;
   property Current: TFoo read GetCurrent;

There are a lot of samples related to standard enumerable types, so in this recipe you’ll look at some not-so-common utilizations.

Getting ready

In this recipe, you’ll see a file enumerable function as it exists in other, mostly dynamic, languages. The goal is to enumerate all the rows in a text file without actual opening, reading and closing the file, as shown in the following code:

var
  row: String;
begin
  for row in EachRows('....myfile.txt') do
    WriteLn(row);
end;

Nice, isn’t it? Let’s start…

How to do it…

We have to create an enumerable function result. The function simply returns the actual enumerable type. This type is not freed automatically by the compiler so you’ve to use a value type or an interfaced type. For the sake of simplicity, let’s code to return a record type:

function EachRows(const AFileName: String): TFileEnumerable;
begin
  Result := TFileEnumerable.Create(AFileName);
end;

The TFileEnumerable type is defined as follows:

type
  TFileEnumerable = record
  private
    FFileName: string;
  public
    constructor Create(AFileName: String);
    function GetEnumerator: TEnumerator<String>;
  end;
. . .
constructor TFileEnumerable.Create(AFileName: String);
begin
  FFileName := AFileName;
end;

function TFileEnumerable.GetEnumerator: TEnumerator<String<;
begin
  Result := TFileEnumerator.Create(FFileName);
end;

No logic here; this record is required only because you need a type that has a GetEnumerator method defined. This method is called automatically by the compiler when the type is used on the right side of the for..in loop.

An interesting thing happens in the TFileEnumerator type, the actual enumerator, declared in the implementation section of the unit. Remember, this object is automatically freed by the compiler because it is the return of the GetEnumerator call:

type
  TFileEnumerator = class(TEnumerator<String>)
  private
    FCurrent: String;
    FFile: TStreamReader;
  protected
    constructor Create(AFileName: String);
destructor Destroy; override;
    function DoGetCurrent: String; override;
    function DoMoveNext: Boolean; override;
  end;

{ TFileEnumerator }
constructor TFileEnumerator.Create(AFileName: String);
begin
  inherited Create;
  FFile := TFile.OpenText(AFileName);
end;

destructor TFileEnumerator.Destroy;
begin
  FFile.Free;
  inherited;
end;

function TFileEnumerator.DoGetCurrent: String;
begin
  Result := FCurrent;
end;

function TFileEnumerator.DoMoveNext: Boolean;
begin
  Result := not FFile.EndOfStream;
  if Result then
    FCurrent := FFile.ReadLine;

end;

The enumerator inherits from TEnumerator<String> because each row of the file is represented as a string. This class also gives a mechanism to implement the required methods.

The DoGetCurrent (called internally by the TEnumerator<T>.GetCurrent method) returns the current line.

The DoMoveNext method (called internally by the TEnumerator<T>.MoveNext method) returns true or false if there are more lines to read in the file or not. Remember that this method is called before the first call to the GetCurrent method. After the first call to the DoMoveNext method, FCurrent is properly set to the first row of the file.

The compiler generates a piece of code similar to the following pseudo code:

it = typetoenumerate.GetEnumerator;
while it.MoveNext do
begin
  S := it.Current;
  //do something useful with string S
end
it.free;

There’s more…

Enumerable types are really powerful and help you to write less, and less error prone, code. There are some shortcuts to iterate over in-place data without even creating an actual container.

If you have a bounce or integers or if you want to create a not homogenous for loop over some kind of data type, you can use the new TArray<T> type as shown here:

for i in TArray<Integer>.Create(2, 4, 8, 16) do WriteLn(i);
//write 2 4 8 16

TArray<T> is a generic type, so the same works also for strings:

for s in TArray<String>.Create('Hello','Delphi','World') do WriteLn(s);

It can also be used for Plain Old Delphi Object (PODO) or controls:

for btn in TArray<TButton>.Create(btn1, btn31,btn2) do
btn.Enabled := false

See also

Giving a new appearance to the standard FireMonkey controls using styles

Since Version XE2, RAD Studio includes FireMonkey. FireMonkey is an amazing library. It is a really ambitious target for Embarcadero, but it’s important for its long-term strategy. VCL is and will remain a Windows-only library, while FireMonkey has been designed to be completely OS and device independent. You can develop one application and compile it anywhere (if anywhere is contained in Windows, OS X, Android, and iOS; let’s say that is a good part of anywhere).

Getting ready

A styled component doesn’t know how it will be rendered on the screen, but the style. Changing the style, you can change the aspect of the component without changing its code. The relation between the component code and style is similar to the relation between HTML and CSS, one is the content and another is the display. In terms of FireMonkey, the component code contains the actual functionalities the component has, but the aspect is completely handled by the associated style. All the TStyledControl child classes support styles.

Let’s say you have to create an application to find a holiday house for a travel agency. Your customer wants a nice-looking application to search for the dream house their customers. Your graphic design department (if present) decided to create a semitransparent look-and-feel, as shown in the following screenshot, and you’ve to create such an interface. How to do that?

Delphi Cookbook

This is the UI we want

How to do it…

In this case, you require some step-by-step instructions, so here they are:

  1. Create a new FireMonkey desktop application (navigate to File | New | FireMonkey Desktop Application).
  2. Drop a TImage component on the form. Set its Align property to alClient, and use the MultiResBitmap property and its property editor to load a nice-looking picture.
  3. Set the WrapMode property to iwFit and resize the form to let the image cover the entire form.
  4. Now, drop a TEdit component and a TListBox component over the TImage component. Name the TEdit component as EditSearch and the TListBox component as ListBoxHouses.
  5. Set the Scale property of the TEdit and TListBox components to the following values:
    • Scale.X: 2
    • Scale.Y: 2
  6. Your form should now look like this:

    Delphi Cookbook

    The form with the standard components

    The actions to be performed by the users are very simple. They should write some search criteria in the Edit field and click on Return. Then, the listbox shows all the houses available for that criteria (with a “contains” search). In a real app, you require a database or a web service to query, but this is a sample so you’ll use fake search criteria on fake data.

  7. Add the RandomUtilsU.pas file from the Commons folder of the project and add it to the uses clause of the main form.
  8. Create an OnKeyUp event handler for the TEdit component and write the following code inside it:
    procedure TForm1.EditSearchKeyUp(Sender: TObject;
         var Key: Word; var KeyChar: Char; Shift: TShiftState);
    var
    I: Integer;
    House: string;
    SearchText: string;
    begin
    if Key <> vkReturn then
       Exit;
     
    // this is a fake search...
    ListBoxHouses.Clear;
    SearchText := EditSearch.Text.ToUpper;
    //now, gets 50 random houses and match the criteria
    for I := 1 to 50 do
    begin
       House := GetRndHouse;
       if House.ToUpper.Contains(SearchText) then
         ListBoxHouses.Items.Add(House);
    end;
    if ListBoxHouses.Count > 0 then
       ListBoxHouses.ItemIndex := 0
    else
       ListBoxHouses.Items.Add('<Sorry, no houses found>');
    ListBoxHouses.SetFocus;
    end;
  9. Run the application and try it to familiarize yourself with the behavior.
  10. Now, you have a working application, but you still need to make it transparent. Let’s start with the FireMonkey Style Designer (FSD).

    Just to be clear, at the time of writing, the FireMonkey Style Designer is far to be perfect. It works, but it is not a pleasure to work with it. However, it does its job.

  11. Right-click on the TEdit component. From the contextual menu, choose Edit Custom Style (general information about styles and the style editor can be found at http://docwiki.embarcadero.com/RADStudio/XE6/en/FireMonkey_Style_Designer and http://docwiki.embarcadero.com/RADStudio/XE6/en/Editing_a_FireMonkey_Style).
  12. Delphi opens a new tab that contains the FSD. However, to work with it, you need the Structure pane to be visible as well (navigate to View | Structure or Shift + Alt + F11).
  13. In the Structure pane, there are all the styles used by the TEdit control. You should see a Structure pane similar to the following screenshot:

    Delphi Cookbook

    The Structure pane showing the default style for the TEdit control

  14. In the Structure pane, open the editsearchstyle1 node, select the background subnode, and go to the Object Inspector.
  15. In the Object Inspector window, remove the content of the SourceLookup property.

    The background part of the style is TActiveStyleObject. A TActiveStyleObject style is a style that is able to show a part of an image as default and another part of the same image when the component that uses it is active, checked, focused, mouse hovered, pressed, or selected. The image to use is in the SourceLookup property. Our TEdit component must be completely transparent in every state, so we removed the value of the SourceLookup property.

  16. Now the TEdit component is completely invisible. Click on Apply and Close and run the application. As you can confirm, the edit works but it is completely transparent. Close the application.
  17. When you opened the FSD for the first time, a TStyleBook component has been automatically dropped on the form and contains all your custom styles. Double-click on it and the style designer opens again.
  18. The edit, as you saw, is transparent, but it is not usable at all. You need to see at least where to click and write. Let’s add a small bottom line to the edit style, just like a small underline.
  19. To perform the next step, you require the Tool Palette window and the Structure pane visible. Here is my preferred setup for this situation:

    Delphi Cookbook

    The Structure pane and the Tool Palette window are visible at the same time using the docking mechanism; you can also use the floating windows if you wish

  20. Now, search for a TLine component in the Tool Palette window. Drag-and-drop the TLine component onto the editsearchstyle1 node in the Structure pane. Yes, you have to drop a component from the Tool Palette window directly onto the Structure pane.
  21. Now, select the TLine component in the Structure Pane (do not use the FSD to select the components, you have to use the Structure pane nodes). In the Object Inspector, set the following properties:
    • Align: alContents
    • HitTest: False
    • LineType: ltTop
    • RotationAngle: 180
    • Opacity: 0.6
  22. Click on Apply and Close.
  23. Run the application. Now, the text is underlined by a small black line that makes it easy to identify that the application is transparent. Stop the application.
  24. Now, you’ve to work on the listbox; it is still 100 percent opaque.
  25. Right-click on the ListBoxHouses option and click on Edit Custom Style.
  26. In the Structure pane, there are some new styles related to the TListBox class. Select the listboxhousesstyle1 option, open it, and select its child style, background.
  27. In the Object Inspector, change the Opacity property of the background style to 0.6. Click on Apply and Close.
  28. That’s it! Run the application, write Calif in the Edit field and press Return. You should see a nice-looking application with a semitransparent user interface showing your dream houses in California (just like it was shown in the screenshot in the Getting ready section of this recipe). Are you amazed by the power of FireMonkey styles?

How it works…

The trick used in this recipe is simple. If you require a transparent UI, just identify which part of the style of each component is responsible to draw the background of the component. Then, put the Opacity setting to a level less than 1 (0.6 or 0.7 could be enough for most cases). Why not simply change the Opacity property of the component? Because if you change the Opacity property of the component, the whole component will be drawn with that opacity. However, you need only the background to be transparent; the inner text must be completely opaque. This is the reason why you changed the style and not the component property.

In the case of the TEdit component, you completely removed the painting when you removed the SourceLookup property from TActiveStyleObject that draws the background.

As a thumb rule, if you have to change the appearance of a control, check its properties. If the required customization is not possible using only the properties, then change the style.

There’s more…

If you are new to FireMonkey styles, probably most concepts in this recipe must have been difficult to grasp. If so, check the official documentation on the Embarcadero DocWiki at the following URL:

http://docwiki.embarcadero.com/RADStudio/XE6/en/Customizing_FireMonkey_Applications_with_Styles

Summary

In this article, we discussed ways to write enumerable types in Delphi. We also discussed how we can use styles to make our FireMonkey controls look better.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here