qGold
qGold

Reputation: 341

Delphi FMX: How to add an Event into an object (just like TEdit.OnChange)

Assume I have created a new FMX project with nothing more than a TButton and a TProgressBar on it. Now I added a 'Countertest.pas' file using [Shift] + [F11]. (see code below)

Now I have implemented a procedure into 'Unit1.pas' (the main app) to be triggered by a procedure inside the 'Countertest.pas' file to change the Value of the TProgressBar.

Inside 'Unit1.pas' I wrote this to be called from inside the 'Countertest.pas' file:

procedure TForm1.SomethingChanged(newPercentage:Integer);
begin
ProgressBar1.Value:=newPercentage;
showmessage('Congratulations, you have just reached '+IntToStr(newPercentage)+' Percent ;)');
end;

To simplify my problem, this is my stripped down 'Countertest.pas' file:

unit Countertest;

interface

uses FMX.Memo; // Will be used later

type
  TCountertest = Class
  private
    zahl: Integer;
  published
    constructor Create();
    destructor Destroy();
    procedure Counter();
    property Percentage: Integer read zahl;
  end;

implementation

constructor TCountertest.Create();
begin

end;

destructor TCountertest.Destroy();
begin

end;

procedure TCountertest.Counter();
begin
  for i := 0 to 1337 do
  Percentage:=0;
  begin
    zahl:=i;
    if Percentage<>round(i / 100) then 
    begin
        // Here I want to call a Procedure inside 'Unit1.pas' to change a Value of the TProgressBar (and some other components like TLabel.Text)
    end;
    Percentage:=round(i / 100);
  end;
end;

end.

As far as I know there is the possibility to use something like procedure(Sender: TObject) of object; and it seems to be the thing a want to use, however I don't have any idea of how to use this. My Intention is to write something similar to an OnChange Event used in a TEdit Control.

Of Course I could add 'Unit1.pas' into the Uses section of 'Countertest.pas' and then call the procedure directly, but as have to handle multiple Instances of TCountertest, I want to have it more like this:

procedure InstanceOfTCountertest.SomethingChanged(newPercentage:Integer);
begin
ProgressBar1.Value:=newPercentage;
showmessage('Congratulations, you have just reached a new Percent ;)');
end;

In the final app there are multiple Instances of TCountertest, so I have multiple Progressbars as well (and other GUI components such as TLabels). Maybe there are other ways to do this, so feel free to suggest anything, that could fit the purpose to show the Progress of those Instances.

Upvotes: 2

Views: 2204

Answers (1)

karliwson
karliwson

Reputation: 3485

Usually, components (like TButton for example) expose events as properties (eg.: TButton.OnCLick) and it's up to the parent or sibling components (the parent TForm in this case) to set the event handler. Let's say we want to change the Button1.OnClick event handler:

// Setting the handler:

procedure TForm1.Create(AOwner: TComponent);
begin
    Button1.OnClick := Self.MyCustomClickHandler;
end;

// And the handler implementation:

procedure TForm1.MyCustomClickHandler(Sender: TObject);
begin
    // ...
end;

So, I imagine you want to have an event for your TCountertest like TCountertest.OnCount so that other components / forms can set a handler and act in response to a change in your counter's progress. Let's describe how to do it the Delphi way (untested code):

First, your component should implement and expose the event:

unit Countertest;

interface

type
    // Custom type for your event handler
    TCountEvent = procedure(Sender: TObject; Percentage: Integer) of object;

    TCountertest = Class
    private
        FPercentage: Integer;

        // Variable that holds the event handler set by the user
        FOnCount: TCountEvent;

        // Internal event trigger
        procedure DoCount();

    public
        constructor Create();
        destructor Destroy();
        procedure Counter();

    published
        property Percentage: Integer read FPercentage;

        // This is the property for your event handler assignment
        property OnCount: TCountEvent read FOnCount write FOnCount;
    end;

implementation

constructor TCountertest.Create();
begin
    // Initialize event handler to nil
    FOnCount := nil;
end;

destructor TCountertest.Destroy();
begin
end;

// Event trigger
procedure TCounterTest.DoCount();
begin
    // Check that the user assigned an event handler
    if Assigned(FOnCount) then
        FOnCount(Self, FPercentage);
end;

procedure TCountertest.Counter();
begin
    // Your code (update FPercentage in your code)...

    // When you need to trigger the event:
    DoCount();

    // Rest of your code...
end;

end.

Now we are ready to create and set event handlers to your instances of TCountertest:

unit Unit1;

// ...

type
    TForm1 = class
    private
        // ...

        // Declare the handler
        procedure CounterCount(Sender: TObject; Percentage: Integer);
    public
        // ...
    end;

implementation

// ...

// Implement the handler
procedure TForm1.CounterCount(Sender: TObject; Percentage: Integer);
begin
    ProgressBar1.Value := Percentage;
end;

// Set the event handlers for your counters
procedure TForm1.Create(AOwner: TComponent);
var
    Counter1, Counter2: TCountertest;
begin
    // Create the counters
    Counter1 := TCountertest.Create();
    Counter2 := TCountertest.Create();

    // Set the event handlers
    Counter1.OnCount := Self.CounterCount;
    Counter2.OnCount := Self.CounterCount;
end;

end.

I hope it helps. Feel free to ask if not.

Upvotes: 4

Related Questions