tags:

views:

105

answers:

5

Hi,

I created several new objects

TMyMemo = class (TMemo)
private
  FYahoo = Integer;
  procedure SetYahoo(Value:integer)
public
  procedure Google(A,B:integer; S:string);
published
  property Yahoo:integer read FYahoo write SetYahoo;
end;

TMyPaintbox = class (TPaintbox)
private
  FYahoo = Integer;
  procedure SetYahoo(Value:integer) 
public
  procedure Google(A,B:integer; S:string);
published
  property Yahoo:integer read FYahoo write SetYahoo;
end;

TMyButton = class (TButton)
private
  FYahoo = Integer;
  procedure SetYahoo(Value:integer) 
public
  procedure Google(A,B:integer; S:string);
published
  property Yahoo:integer read FYahoo write SetYahoo;
end;

. . .

These Controls are placed on Form1. Is there a way, how can I change the same property (Yahoo) and run the procedure (Google), which is declared in different objects in general?

I do not want to manually check class type like: if Controls[i] is TMyMemo then ... if controls[i] is TMyPaintbox then ...

because I do not know how many of my new classes will have property Yahoo and method Google (This is only simple example). Probably I have to use ^ and @ operator or FieldAdress, MethodAddress I do not know what else. Can you help me find general solution?

procedure Form1.Button1Click(Sender:TObject);
var i:integer;   
begin
  for i:=0 to Form1.ControlCount-1 do
           begin   
           Controls[i].Google(4,5, 'Web');   // this should be changed somehow
           Controls[i].Yahoo:=6;             // this should be changed somehow
           end;
end;

end;

Thanks

A: 

@lyborko, the Controls[i] returns a TControl Class wich not have an implementation for the Google method and the Yahoo property.for resolve you problem, you can check the class of the Controls[i] using the ClassType property and then implement something like this.

procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
  for i:=0 to Form1.ControlCount-1 do
     begin
      if Controls[i].ClassType = TMyPaintbox  then
      begin
       TMyPaintbox (Controls[i]).Google(4,5, 'Web');
       TMyPaintbox (Controls[i]).Yahoo:=6;
      end
      else
      if Controls[i].ClassType = TMyMemo  then
      begin
       TMyMemo (Controls[i]).Google(4,5, 'Web');
       TMyMemo (Controls[i]).Yahoo:=6;
      end
      else
      if Controls[i].ClassType = TMyButton  then
      begin
       TMyButton (Controls[i]).Google(4,5, 'Web');
       TMyButton (Controls[i]).Yahoo:=6;
      end;

     end;
end;
RRUZ
Yes, and this is exactly, what I wanted to avoid !!! I do not want to solve like this, because it is not flexible. I am looking for some solution, in which I do not know in advance which controls have property yahoo and method google. Could you, please, make it in general? (Perhaps I was not exact, what I ask for. I will edit the question)
lyborko
+4  A: 

Define an interface which has both method Google() and property Yahoo defined.

Make your TMyButton, TMyMemo and TMyPaintbox inherit from that interface and override those methods to do what is necessary.

In the loop, cast the controls to the interface type using the "as" operator and access the Yahoo field and Google() method.

Here is the code - The is operator doesnt work as intended in Delphi 2009 and below, so I had to write a function for that - It needs to rely on catching a cast exception, so it isn't the cleanest solution:

type

  TMyInterface = interface(IInterface)
  ['{1F379072-BBFE-4052-89F9-D4297B9A826F}']

    function GetYahoo : Integer;
    procedure PutYahoo(i : Integer);

    property Yahoo : Integer read GetYahoo write PutYahoo;
    procedure Google(A, B : integer; S : string);
  end;

  TMyButton = class (TButton, TMyInterface)
  private
    FStr : String;
    FYah : Integer;

  public
    function GetYahoo : Integer;
    procedure PutYahoo(i : Integer);
    procedure Google(A, B : integer; S : string);
  end;

  TMyMemo = class (TMemo, TMyInterface)
  private
    FStr : String;
    FYah : Integer;

  public
    function GetYahoo : Integer;
    procedure PutYahoo(i : Integer);
    procedure Google(A, B : integer; S : string);
  end;


{ TMyButton }

function TMyButton.GetYahoo: Integer;
begin
  Result := 0;
end;

procedure TMyButton.Google(A, B: integer; S: string);
begin
  FStr := S + '=' + IntToStr(A + B);
end;

procedure TMyButton.PutYahoo(i: Integer);
begin
  FYah := 42;
end;

{ TMyMemo }

function TMyMemo.GetYahoo: Integer;
begin
  //
end;

procedure TMyMemo.Google(A, B: integer; S: string);
begin
  //
end;

procedure TMyMemo.PutYahoo(i: Integer);
begin
  //
end;

function IsMyIntf(c : TControl) : TMyInterface;
begin
  try
    Result := c as TMyInterface;
  except on e : Exception do
    Result := nil;
  end;
end;

procedure TForm2.Button1Click(Sender: TObject);
var
  i: Integer;
  p : TMyInterface;
begin
  for i  := 0 to ControlCount - 1 do
  begin
    p := IsMyIntf(Controls[i]);
    if (p <> nil) then
    begin
      p.PutYahoo(i);
      p.Google(i, i, 'Hah!');
    end;
  end;

end;
rep_movsd
Remember to test the control with the "is" operator to see if the particular control supports your interface, before casting it with "as"
rep_movsd
May you please write a simple demonstration?
lyborko
Mixing interfaces and objects, can be a challange, if refcounting is active.
BennyBechDk
@BennyBechDk: The reference counting is disabled if he uses a class that dervices from TPersistent/TComponent, what it seems to be.
Andreas Hausladen
Use the `Supports()` function instead of your `IsMyIntf()` and everything will work.
mghie
@Andreas: Even when refcounting is disabled, the compiler inserts calls to _Release. So you still have to be sure to clear all interface references before calling Free. I guess this can be a challenge. :-)
Ulrich Gerhardt
When I tried to run this, "interface not supported" error message was raised.... :-(
lyborko
@Iyborko : The IsMyIntf function relies on an exception to find out if the object supports it. In the IDE it will break on that exception, it is meant to be ignored. Also, as "mghie" said in the comments above, use the Supports() function instead of this IsmyIntf function...
rep_movsd
A: 

Hi

You are propobly looking for RTTI.
Se this article about that http://delphi.about.com/od/vclusing/a/coloringfocused.htm (look at page 3)

The Only problem is that it Can't se public properties/methods, only published (i don't know if that stil is true in delphi 2010). A lot of RTTI info for delphi 2010 can be found here http://robstechcorner.blogspot.com/2009/09/so-what-is-rtti-rtti-is-acronym-for-run.html .

Calling a method by name (via RTTI): Se http://delphi.about.com/cs/adptips2004/a/bltip0204_3.htm

BennyBechDk
Yes, you could be right... I worked with RTTI a time ago, I am not sure about firing method, but I think this is the right way... (unless anybody shows different approach, if any... ). Thanx...
lyborko
Using RTTI is the *only* method if you do not have access to source code. Using interfaces as ep_movsd suggested is faster and cleaner but that requires changes to the source code of the target objects. In your example you're looping over controls on a form, if that's really what you want to do then start digging into RTTI because even if you do have access to control sources, you don't really want to change it. On Delphi 2010 you've got access to all public items, including methods and properties.
Cosmin Prund
+1  A: 
  1. Use the same base class
  2. Use an interface
  3. Use the D2010 RTTI
  4. Implement a custom message on all controls and process it.
alex
A: 

Better that use IF...ELSE with differents classes for know the component class you can use RTTI to know if an object has a específic property. You can find the code and explanation here:
Modify control properties using RTTI
Here you can find more code for access properties of a component using RTTI.

Regards.

Neftalí