Können Komponenten einen eigenen Aktualisierungstimer besitzen

    Können Komponenten einen eigenen Aktualisierungstimer besitzen

    Hi Zusammen,

    es geht im weitesten Sinne um Maschinensteuerung und insbesondere um Visualisierung.

    Nun ist es so, dass wir beispielsweise Leuchtdioden-Komponenten auf ein Formular ziehen. Die sollen den Zustand eines digitalen Ein- bzw. Ausgangs anzeigen. Dieser liegt "global" vor.

    Bislang machen wir das ziemlich primitiv. Wir ziehen einen Timer aufs Formular, in Ontimer hecheln wir alle Komponenten des Formulars durch.

    Das sieht dann etwa so aus:

    Delphi-Code

    1. procedure TExtFoeteForm.Timer1Timer(Sender: TObject);
    2. var
    3. i, j: integer;
    4. begin
    5. for i := 0 to ComponentCount - 1 do begin
    6. if Components[i] is TLEDLabel then begin
    7. with Components[i] as TLEDLabel do begin
    8. if Kind = LEDEingang then
    9. Lit := GetEingang(Name); // Leuchtdiode geht ja nach Zustand des Eingangs an oder aus...
    10. if Kind = LEDAusgang then
    11. Lit := GetAusgang(Name); // dito.
    12. end;
    13. end;


    OK, das funktioniert natürlich. Aber das ist nur ein Beispiel. Wir benutzen viele verschiedene Komponenten, auch
    vielen Formularen, und dann muss man das jedesmal und für jeden verwendeten Komponententyp wieder so machen.

    Es wäre eleganter, wenn die Komponenten sich selbst um ihre Aktualisierung kümmern könnten.

    Es soll aber weiter timergesteuert geschehen, denn da wird (nur lesend) auf Hintergrund-Prozesse zugegriffen, die
    in einem Echtzeitthread laufen (Echtzeitaufsatz Kithara). Dieser Thread soll keinesfalls direkt auf die Oberfläche schreiben, und auch sonst möglichst ungestört arbeiten. Es ist also nicht erwünscht, dass er beispielsweise Botschaften verschickt. Er stellt nur globale Variablen zur Verfügung, und die sollen eben timergesteuert visualisiert
    werden.

    Also, Sinn meiner Frage. Kann man die Komponenten um einen eigenen Timer erweitern, so dass sie sich selbst um ihre Darstellung kümmern würden? Wahrscheinlich ungeschickt, denn dann hätten wir leicht 100 Timer auf einem einzigen
    Formular, die alle wild durcheinander arbeiten...

    Besser vermutlich, wir ziehen einen Timer aufs Formular, und verbinden die Komponenten damit (published Property
    MyAktualisierungsTimer) im Objektinspektor den Timer auswählen. Aber wie kann ich mich im Komponenten-Quelltext in diesen Timer reinhängen? Eine eigene Timerklasse schreiben, die dafür vorgesehen ist? Oder könnte die Komponente die Timer-Msg abfangen und damit arbeiten? Ihr seht, mir geht viel durch den Kopf, aber ich weiß nicht genau was
    davon nur Blödsinn oder schlau ist...

    Jede Art Input ist also willkommen ;)

    Gruß, Joachim

    PS, noch ne dumme Forums-Anfängerfrage. Wie schaffe ich es, nur den Quelltext als Quelltext zu formatieren?
    Markieren und Taste drücken hat nicht geklappt...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DeddyH“ () aus folgendem Grund: Delphi-Tags ([delphi][/delphi]) eingefügt, künftig bitte selbst machen //MfG DeddyH

    Hi Joachim S

    Joachim S schrieb:

    Nun ist es so, dass wir beispielsweise Leuchtdioden-Komponenten auf ein Formular ziehen. Die sollen den Zustand eines digitalen Ein- bzw. Ausgangs anzeigen. Dieser liegt "global" vor.

    Wann wird die Leuchtdioden-Komponente auf das Formular gezogen? Zur Design- oder zur Laufzeit?

    Zur Laufzeit kannst du die Komponente einer Objektliste hinzufügen. Dann musst du nur diese Durchlaufen.
    Zum andern: Wenn die Komponente, bevor sie abgelegt wird, bereits einen Namen hat, kannst du dem Formular ein privates Feld verpassen, das den Namen der Komponente aufnimmt. Wenn du dem Formular noch ein zusätzlliches Ereignis verpasst, kannst du dieses aufrufen, wenn die Komponente abgelegt wird und in diesem Eventhandler beliebige Aktionen ausführen.

    Das sehe ich jetzt nachträglich:

    Joachim S schrieb:

    Nun ist es so, dass wir beispielsweise Leuchtdioden-Komponenten auf ein Formular ziehen. Die sollen den Zustand eines digitalen Ein- bzw. Ausgangs anzeigen. Dieser liegt "global" vor.

    Bislang machen wir das ziemlich primitiv. Wir ziehen einen Timer aufs Formular, in Ontimer hecheln wir alle Komponenten des Formulars durch.


    Zu jeder Leuchtdiode einen Timer?

    Die Objektliste kannst du auch grundsätzlich schon zur Designzeit nutzen.

    Gruss
    Delbor
    roase.ch/

    Was wirklich zählt, ist Intuition. Albert Einstein

    ________________

    Delbor alias Zoran
    Wann wird die Leuchtdioden-Komponente auf das Formular gezogen? Zur Design- oder zur Laufzeit?

    Zur Laufzeit kannst du die Komponente einer Objektliste hinzufügen. Dann musst du nur diese Durchlaufen.
    Zum andern: Wenn die Komponente, bevor sie abgelegt wird, bereits einen Namen hat, kannst du dem Formular ein privates Feld verpassen, das den Namen der Komponente aufnimmt. Wenn du dem Formular noch ein zusätzlliches Ereignis verpasst, kannst du dieses aufrufen, wenn die Komponente abgelegt wird und in diesem Eventhandler beliebige Aktionen ausführen.


    Hi Delbor,

    beides passiert... Ziel der Sache wäre: Wir nehmen eine Leuchtdiode, ziehen sie aufs Formular und setzen bei ein bestimmtes Property, z.B. den Namen des abzubildenden Eingangs.

    Und dann soll sie zur Laufzeit eben den Zustand des Eingangs anzeigen. Wäre eben toll, wenn man sie nicht noch in eine Liste aufnehmen müsste, und wenn man keinen weiteren Code mehr schreiben müsste. Wenn die Komponenten eben allein wüsste, was zu tun ist. Egal ob wir sie nun per Formulardesigner platzieren, oder erst zur Laufzeit erzeugen.


    Zu jeder Leuchtdiode einen Timer?


    Nein, momentan benutzen wir einen einzigen Timer, der auf dem Formular ist. Ich denke auch, jede Leuchtdiode einen Timer wäre unverantwortlicher Umgang mit den Ressourcen ;)

    Und wie gesagt, es sind nicht nur Leuchtdioden. Wir bauen so nach und nach immer mehr Visualisierungs-Komponenten auf, die alle so schlau sein sollen.

    Schönen Gruß auch, Joachim
    Theoretisch sollte das gehen. Man sollte eine neue Komponente aus den Leuchtdioden-Komponenten ableiten können, die einen Timer ihr Eigen nennt und sich so selbst steuert.

    Praktisch aber solltest du Probleme kriegen: die Anzahl aktiver Timer ist in Win begrenzt. Wieviele das sind, wird iwie geflissentlich verschwiegen. Andererseits kriegen wir in unserem Programm (und wir brauchen kaum mal einen) ab und zu die Meldung "Out of Timers",,,

    Du solltest dir also genau überlegen, ob du das wirklich in Kau nehmen willst. Wahrscheinlich wäre eine eigene Timer-Kompnente nützlicher, die die Verwaltung der zeitlichen Aspekte für alle Leuchtdioden übernimmt, also eine Arte "Supertimer". Man könnte sich zB vorstellen, dass in einer solchen enien Timer mit max. Auflösung ansteuert. Die Interaktion mit den Leuchtdioden-Komponenten könnte dahin gehend funktionieren, dass sich diese in Form einer "OnTimer"-Routine beim SuperTimer "registrieren" (der die Procedures in einem Array speichert) und die registrierten Routinen bei OnTimer abarbeitet. Muss man natürlich auf irgendwelche Performanceprobleme achten!

    cckLoud
    Hi Joachim S

    Joachim S schrieb:

    Die sollen den Zustand eines digitalen Ein- bzw. Ausgangs anzeigen.

    Wieviele dieser digitalen Ein- bzw. Ausgangs gibt es? Ich geh jetzt mal davon aus, dass über diese bestimmte Zustände von der Maschine gemeldet werden (Eingänge, bzw die Maschine nur Zustandswerte setzt (Ausgänge). Im Grunde brauchst du genau so viele Leuchtdiode, wie Zustände möglich sind, bzw. doppelt soviele (je ein Ausgang und ein Eingang).

    Joachim S schrieb:

    in einem Echtzeitthread laufen (Echtzeitaufsatz Kithara). Dieser Thread soll keinesfalls direkt auf die Oberfläche schreiben, und auch sonst möglichst ungestört arbeiten. Es ist also nicht erwünscht, dass er beispielsweise Botschaften verschickt. Er stellt nur globale Variablen zur Verfügung, und die sollen eben timergesteuert visualisiert
    werden.


    Es gilt also, diese Werte auszulesen. Also könntest du dir beispielsweise eine Klasse bauen, die einen der Zustände von der Maschine ausliest (Eingang) oder schreibt (Ausgang) und die von deiner LeuchtdiodenKlasse abgeleiten.
    Weiter müsste die Komponente 1 Property haben für Eingang , bzw. Ausgang, über die nun der Wert ein- oder ausgelesen werden kann.
    Diese Propertys kannst du nun in der OnTimer-EventHandler aufrufen, um zu lesen und die Diode aufleuchten oder Abblenden zu lassen.

    Offenbar hast du noch andere Komponenten, die sich selbst aktualisieren müssten. Wie sie das tun, ist jedesmal verschieden, eine Klasse/Komponente davon kann aber immer abgeleitet und dieser neue Eigenschaften, Methoden oder Ereignisse verpasst werden.

    Um dynamische, also zur Laufzeit erstellte Komponenten zu identifizieren, hast du die Möglichkeit
    • sämtliche Komponeten eines Containers durchzugehen; ein Container kann eine Form, ein Frame, eine Scrollbox oder auch ein Panel sein. Das ist bei mehreren hundert Komponenten nicht nur unschön, sondern kann auch zum Performanceproblem werden.
    • Weiter könntest du eine neu dynamisch erstellte Komponente einem Array vom Typ dieser Komponente zuweisen, um sie während der Programmlaufzeit direkt ansprechen zu können.
    • Wesentlich günstiger ist aber die Verwendung einer Objektliste. Dieser kannst du beliebige Klassen oder Komponenten verpassen, und wenn du generische Objectlisten verwendest, musst du diese nicht mal Typcasten.

    Gruss
    Delbor
    roase.ch/

    Was wirklich zählt, ist Intuition. Albert Einstein

    ________________

    Delbor alias Zoran

    cckLoud schrieb:

    Praktisch aber solltest du Probleme kriegen: die Anzahl aktiver Timer ist in Win begrenzt. Wieviele das sind, wird iwie geflissentlich verschwiegen

    What is the maximum number of timers a program can create?

    Dort steht was von 10.000 möglichen Timern im ganzen System unter NT4, pro Anwendung allerdings nur 32, wenn ich das richtig verstanden habe. Frühere Windows-Versionen gestatteten offenbar weniger. Hab aber jetzt nicht den ganzen Thread gelesen, daher weiß ich nicht, ob XP, Win7, Win8 und Win10 mehr oder weniger Timer erlauben. Auf jeden Fall sollten 10.000 aktive Timer im ganzen Betriebssystem mehr als ausreichend sein ... :woot:
    Hi Zusammen,

    ich antworte mal zusammenfassend: Leuchtdioden können mal so an die 200 auf einem Form werden. Theoretisch auch mehr, ansich gibt es nach oben keine Grenze. Kommt drauf an, wie groß die Gesamtanlage so wird.

    Bis jetzt hechelt halt EIN Timer auf dem Form alle am Form angemeldeten Komponenten durch, prüft ob sie vom Typ TLed sind, und aktualisiert dann ihren Status. Damit haben wir keine Performance-Probleme. Die Timer laufen üblicherweise so mit 100 ms. Aber man muss halt immer ne Menge zu Fuß programmieren.

    Die Software läuft üblicherweise auf XP-Embedded. Ab demnächst auf Windows7-Embedded. Unsere Delphi-Version ist bei einfachen Projekten ohne große Datenbank-Anbindungen immer noch (hüstel) Delphi 5. Ist halt so schön schlank...

    Auf XE3 haben wir es auch ohne große Mühen anpassen und testen können. Mit XE arbeiten wir auch, aber mehr für andere Projekte mit Datenbank-Anbindungen.

    Wenn es einen interessiert, das hier ist unser Laden, wo ich als Software-Entwickler und Inbetriebnehmer arbeite.

    roteg.de

    Ich werde mal ein bisschen experimentieren, was mit Hunderten von Timer auf einem Form so passiert ;)

    Aber im Moment schwebt mir sowas vor:

    Die erste Led, die auf dem Formular erzeugt wird, erzeugt zur Laufzeit auf dem Form gleich einen speziellen Timer. Die nächste Led findet diesen Timer bei ihrer Erzeugung und erzeugt keinen weiteren, sondern "hängt sich an diesen dran". Wie auch immer, das muss ich mir noch überlegen. So in der Art wie Delbor meint, wahrscheinlich. Realistisch?

    Gruß Joachim
    Es gibt auch noch den IdleEvent (der kommt immer dann, wenn das UI wieder Zeit hat). Das wäre ja ein guter Zeitgeber. In der Non-Idle-Zeit würden Änderungen eh nur erschwert angezeigt werden können.

    Also spart man sich schon mal jeden Timer damit.

    Dann kann man auch Änderungen an Status xy einfach so durch die Gegend rufen und jeder, den es interessiert, nimmt das zur Kenntnis. Dafür benötigt man dann sowas wie docwiki.embarcadero.com/Libraries/XE7/de/System.Messaging
    ME nicht unbedingt.
    Du kannst ruhig bei einem Timer bleiben, das sollte nicht unbedingt große Unterschiede machen. Aber dabei solltest du nicht unbedingt bei TTimer bleiben. Leite dir doch davon eine neue Klasse ab - die "Global" zur verfügung gestellt wird.

    Und bei den Leuchtdioden-Komponenten leitest du dir auch eine Neue Klasse ab, die die Public Property "OwnedByTimer" (oder so) hat. Ausserdem brauchst du noch eine Methode in der Art "OnDisplay", wo der Code (als Eventroutine) reinkommt, der die Sachen regelt, die abhängig vom Timer sind (zB. feststellen, ob "jetzt" geschaltet werden muss oder nicht). Wenn du eine der (neuen) Leuchtdioden-Komponenten createst, weist du dieser die neue Timer-Komponente zu, die dann die "OnDisplay"-Methode zugewiesen kriegt, die sie in einer Tabelle oä ablegt. Wenn jetzt ein Timer-Event stattfindet, dann wird in der neuen Timerklasse diese Tabelle in der Form abgearbeitet, dass alle eingetragenen Methoden in einer Schleife aufgerufen werden. Auf diese Art arbeitet die Leuchtdioden-Komponenten fast so, als habe sie einen eigenen Timer...

    cckloud
    Ich hab mir nun auch mal ein paar Gedanken gemacht. Das Ergebnis ist zwar nicht state of the art (da sehr unflexibel), funktioniert aber in einem kleinen Test wie gewünscht. Als Erstes eine kleine Klasse zur Zuordnung einer Integer-Variablen (genauer: einem Zeiger darauf) zu einem Label:

    Delphi-Code

    1. type
    2. TIntegerWatch = class
    3. private
    4. FIntToWatch: PInteger;
    5. FLabel: TLabel;
    6. public
    7. constructor Create(AInt: PInteger; ALabel: TLabel);
    8. procedure DisplayValue;
    9. end;
    10. constructor TIntegerWatch.Create(AInt: PInteger; ALabel: TLabel);
    11. begin
    12. if not (Assigned(AInt) and Assigned(ALabel)) then
    13. raise Exception.Create('Keiner der beiden Parameter darf nil sein!');
    14. FIntToWatch := AInt;
    15. FLabel := ALabel;
    16. end;
    17. procedure TIntegerWatch.DisplayValue;
    18. begin
    19. FLabel.Caption := IntToStr(FIntToWatch^);
    20. if FIntToWatch^ > 0 then
    21. FLabel.Font.Color := clGreen
    22. else
    23. FLabel.Font.Color := clRed;
    24. end;

    Nun eine weitere Klasse zur Verwaltung einer dynamischen Anzahl von Instanzen der obigen Klasse, welche auch den Timer enthält:

    Delphi-Code

    1. TWatchList = class
    2. private
    3. FInternalList: TObjectList;
    4. FTimer: TTimer;
    5. procedure DoOnTimer(Sender: TObject);
    6. public
    7. constructor Create;
    8. destructor Destroy; override;
    9. procedure Add(IntToWatch: PInteger; LabelForDisplay: TLabel);
    10. procedure Start;
    11. procedure Stop;
    12. end;
    13. procedure TWatchList.Add(IntToWatch: PInteger; LabelForDisplay: TLabel);
    14. begin
    15. FInternalList.Add(TIntegerWatch.Create(IntToWatch, LabelForDisplay));
    16. end;
    17. constructor TWatchList.Create;
    18. begin
    19. FInternalList := TObjectList.Create;
    20. FTimer := TTimer.Create(nil);
    21. FTimer.Enabled := false;
    22. FTimer.Interval := 100;
    23. FTimer.OnTimer := DoOnTimer;
    24. end;
    25. destructor TWatchList.Destroy;
    26. begin
    27. FTimer.Free;
    28. FInternalList.Free;
    29. inherited;
    30. end;
    31. procedure TWatchList.DoOnTimer(Sender: TObject);
    32. var
    33. i: integer;
    34. begin
    35. for i := 0 to FInternalList.Count - 1 do
    36. TIntegerWatch(FInternalList[i]).DisplayValue;
    37. end;
    38. procedure TWatchList.Start;
    39. begin
    40. FTimer.Enabled := true;
    41. end;
    42. procedure TWatchList.Stop;
    43. begin
    44. FTimer.Enabled := false;
    45. end;

    Auf meinem Formular liegen nun 4 Labels und 8 Buttons, desweiteren gibt es 4 globale Integer-Variablen in der Unit.

    Delphi-Code

    1. var
    2. Int1, Int2, Int3, Int4: integer;
    3. ...
    4. procedure TForm1.Button1Click(Sender: TObject);
    5. begin
    6. Int1 := 100;
    7. end;
    8. procedure TForm1.Button2Click(Sender: TObject);
    9. begin
    10. Int1 := 0;
    11. end;
    12. //usw. für die anderen Variablen und Buttons

    Im OnCreate wird nun eine Instanz der Liste erstellt, die Zuordnungen hinzugefügt und das Ganze gestartet:

    Delphi-Code

    1. procedure TForm1.FormCreate(Sender: TObject);
    2. begin
    3. FWatchList := TWatchList.Create;
    4. FWatchList.Add(@Int1, Label1);
    5. FWatchList.Add(@Int2, Label2);
    6. FWatchList.Add(@Int3, Label3);
    7. FWatchList.Add(@Int4, Label4);
    8. FWatchList.Start;
    9. end;

    Nicht vergessen, die Liste im OnDestroy dann auch wieder freizugeben ;)
    Wie gesagt, das ist weder flexibel noch elegant, aber das Prinzip sollte klar sein.
    10 Minuten Nachdenken ersparen oftmals 10 Stunden Fehlersuche.
    Hm ja...

    System.messaging klingt elegant, müsste man unter 5 wohl noch selber basteln per Botschaften senden?

    Oder die LEDs (und ähnliche Komponenten) legen sich beim Erzeugen in einer global verfügbaren Liste ab, und bei Idle wird die durchgearbeitet...

    Hm, ja.... Gefällt mir.

    Gruß Joachim
    Hi Deddy,

    herzlichen Dank, da hast du dir ja echt Mühe gemacht!

    Ja, das verstehe ich und kann es nachvollziehen... Verfeinert sähe es dann vielleicht so aus:

    Alle Visualisierungskomponenten bekommen einen gemeinsamen Vorfahr, der eine virtuelle Methode Namens DoUpdate oder so erhält. Die LED erbt davon, und sie meldet sich in ihrem Constructor selbst an der Liste an. So wie andere Visualisierungskomponenten dann auch.

    Die Watchlist spricht die Dinger dann als "TVorfahr" an, und kommt so mit allen Komponenten klar..

    Delphi-Code

    1. for i := 0 to FInternalList.Count - 1 do
    2. TVorfahr(FInternalList[i]).DoUpdate;


    Wenn alles automatisch gehen soll, müsste die Watchlist vielleicht global in der Unit definiert werden, die die Komponenten zur Verfügung stellt... Dann hat der Timer aber kein Fenster, auf dem er arbeiten kann... Dann müsste man wohl eher mit einer TimerProc arbeiten...

    Dank dir, das ist ne Menge Stoff zum experimentieren.

    Gruß Joachim
    Klar, ich wollte ja auch keine ausgefuchste OOP-Lösung präsentieren inkl. Vererbung, mir ging es nur um das Prinzip ;). Was Du mit dem Fenster, auf dem der Timer arbeiten soll, meinst, ist mir allerdings nicht ganz klar.
    10 Minuten Nachdenken ersparen oftmals 10 Stunden Fehlersuche.
    Was Du mit dem Fenster, auf dem der Timer arbeiten soll, meinst, ist mir allerdings nicht ganz klar.


    Äh, ja, mir auch nicht ganz ;)

    Ich dachte halt, so ein Timer bekommt vom Betriebssystem eine Botschaft gesandt, und dazu muss er doch sein Window-Handle kennen und bei SetTimer übergeben.

    Aber bevor ich mich jetzt um Kopf und Kragen rede, experimentiere ich lieber erstmal ein bisschen. Bis später,
    Gruß Joachim
    Dann will ich aber auch :)

    Hier mal eine Ableitung von einem Label, was dann solche Nachrichten empfängt.

    Erst mal die Nachrichten selber

    Delphi-Code

    1. unit DataMessage;
    2. interface
    3. uses
    4. System.Messaging;
    5. type
    6. TDataMessage = class( TMessage )
    7. private
    8. FName: string;
    9. FContextId: Integer;
    10. public
    11. constructor Create( AContextId: Integer; const AName: string );
    12. property ContextId: Integer read FContextId;
    13. property Name: string read FName;
    14. end;
    15. TDataMessage<T> = class( TDataMessage )
    16. private
    17. FValue: T;
    18. public
    19. constructor Create( AContextId: Integer; const AName: string; const AValue: T );
    20. property Value: T read FValue;
    21. end;
    22. implementation
    23. { TDataMessage }
    24. constructor TDataMessage.Create( AContextId: Integer; const AName: string );
    25. begin
    26. inherited Create;
    27. FContextId := AContextId;
    28. FName := AName;
    29. end;
    30. { TDataMessage<T> }
    31. constructor TDataMessage<T>.Create( AContextId: Integer; const AName: string; const AValue: T );
    32. begin
    33. inherited Create( AContextId, AName );
    34. FValue := AValue;
    35. end;
    36. end.

    Und das DataLabel, was auf die Nachrichten ​TDataMessage<string> hört. Über ContextId und DataName stellt man noch ein, welche Nachricht genau herausgefischt werden soll und zur Anzeige kommt.

    Eine LED würde z.B. auf TDataMessage<Boolean> lauschen. Der transportierte Wert an und für sich ist ja egal, die Komponente registriert sich eben für die Nachrichten, die sie auch darstellen kann.

    Delphi-Code

    1. unit DataLabel;
    2. interface
    3. uses
    4. System.Messaging,
    5. Vcl.StdCtrls;
    6. type
    7. TDataLabel = class( TLabel )
    8. private
    9. FContextId: Integer;
    10. FDataName: string;
    11. procedure HandleDataMessage( const Sender: TObject; const M: TMessage );
    12. procedure SetContextId( const Value: Integer );
    13. procedure SetDataName( const Value: string );
    14. procedure RequestData;
    15. public
    16. procedure AfterConstruction; override;
    17. procedure BeforeDestruction; override;
    18. published
    19. property DataName: string read FDataName write SetDataName;
    20. property ContextId: Integer read FContextId write SetContextId;
    21. end;
    22. implementation
    23. uses
    24. DataMessage;
    25. { TDataLabel }
    26. procedure TDataLabel.AfterConstruction;
    27. begin
    28. inherited;
    29. TMessageManager.DefaultManager.SubscribeToMessage( TDataMessage<string>, HandleDataMessage );
    30. RequestData;
    31. end;
    32. procedure TDataLabel.BeforeDestruction;
    33. begin
    34. TMessageManager.DefaultManager.Unsubscribe( TDataMessage<string>, HandleDataMessage );
    35. inherited;
    36. end;
    37. procedure TDataLabel.HandleDataMessage( const Sender: TObject; const M: TMessage );
    38. var
    39. LMsg: TDataMessage<string> absolute M;
    40. begin
    41. if ( LMsg.ContextId = FContextId ) and ( LMsg.Name = FDataName )
    42. then
    43. Caption := LMsg.Value;
    44. end;
    45. procedure TDataLabel.RequestData;
    46. begin
    47. if ( FDataName <> '' )
    48. then
    49. TMessageManager.DefaultManager.SendMessage( Self, TDataMessage.Create( FContextId, FDataName ) );
    50. end;
    51. procedure TDataLabel.SetContextId( const Value: Integer );
    52. begin
    53. if FContextId <> Value
    54. then
    55. begin
    56. FContextId := Value;
    57. RequestData;
    58. end;
    59. end;
    60. procedure TDataLabel.SetDataName( const Value: string );
    61. begin
    62. if FDataName <> Value
    63. then
    64. begin
    65. FDataName := Value;
    66. RequestData;
    67. end;
    68. end;
    69. end.

    Um so einem Label (ContextId=1, DataName=Test) nun einen Wert zukommen zu lassen, sendet man einfach die folgende Nachricht

    Delphi-Code

    1. procedure TDataContext.SendMessage;
    2. begin
    3. TMessageManager.DefaultManager.SendMessage( Self, TDataMessage<string>.Create( 1, 'Test', 'Eine Nachricht' ) );
    4. end;

    In das DataLabel habe ich auch noch etwas hineingebracht, dass bei einer Änderung der ContextId, DataName automatisch eine Nachricht versendet wird, um die Daten anzufordern. Die Instanz, die die Daten hängt sich an diesen Nachrichtentyp und sendet dann einfach die angeforderten Daten raus.
    @Sir Rufo : der TE benutzt Delphi 5 ! ( Keine Generics )
    Glückauf

    Herr, wirf Hirn vom Himmel

    Ein Leben ohne Möpse ist möglich, aber sinnlos ( Loriot )
    Der beste Platz für Politiker ist das Wahlplakat. Dort ist er tragbar, geräuschlos und leicht zu entfernen ( Loriot )

    Es gibt Leute, die fühlen sich überall gedemütigt, wo sie nicht frech sein dürfen ( Otto Weiss )
    Ja, nur Delphi 5. Trotzdem ist das interessant. So ein Mechanismus könnte uns irgendwann mal dazu bringen, doch auf XE umzusteigen...

    OK, folgendes hab ich getan. In der Unit, die die Leuchtdioden definiert, im Implementation-Teil folgendes hinzu:

    Delphi-Code

    1. implementation
    2. {$R *.RES}
    3. uses
    4. contnrs;
    5. var
    6. LedList :TObjectList;
    7. procedure UpdateAllLeds( ); // callback-Function für einen windows-Timer
    8. var
    9. i :integer;
    10. begin
    11. for i := 0 to LedList.Count - 1 do begin
    12. with TLed(LedList[i]) do
    13. Lit := not Lit;
    14. end;
    15. end;
    16. // Im Konstruktor die letzte Zeile ergänzt, sie nimmt jede erzeugte Led in die globale Liste auf...
    17. constructor TLED.Create(AOwner: TComponent);
    18. begin
    19. inherited Create(AOwner);
    20. { If this is the first LED then create the LED Bitmap and List object }
    21. if LEDBitmap = nil then LEDBitmap := TLEDBitmap.Create;
    22. LEDBitmap.LED_LIST.Add(Self); { Add this new LED instance to the list }
    23. fLit := False; { Default = not Lit }
    24. fColor := lcRed; { Default = clRed }
    25. ControlStyle := ControlStyle + [csOpaque] + [csDoubleClicks]
    26. + [csFixedHeight] + [csFixedWidth];
    27. LedList.Add(self); // hinzufügen zur globalen Liste aller Leds in der Anwendung.
    28. end;
    29. // Und am Ende der Unit das Folgende:
    30. var
    31. ThisTimer :cardinal;
    32. initialization
    33. LedList := TObjectList.Create;
    34. ThisTimer := SetTimer(0,0,1000,addr(UpdateAllLeds));
    35. // Api-Call, der Timer wird ohne Window-Handle erzeugt, statt dessen übergibt man eine Callback-Funktion
    36. finalization
    37. KillTimer(0,ThisTimer);
    38. //LedList.Free;
    39. end.


    Und siehe da, man nimmt eine (oder viele) LED, zieht sie aufs Formular, drückt F9, und sie blinkt friedlich vor sich hin...
    Das Blinken ist natürlich Quatsch, und muss durch einen vernünftigen UpdateStatus-Mechanismus ersetzt werden. Ist aber genau was ich wollte.

    @ DeddyH, das meinte ich mit der Timer-Callback-Geschichte.

    Ich danke euch allen herzlich für den Input. Echt Klasse hier, ich glaub ich komme jetzt öfter ;)

    Achso, das LedList.Free war keine gute Idee... Ist klar, die Objekte sind ja längst destroyed, wenn wir hier ankommen...

    Gruß Joachim
    Der TE arbeitet mit Delphi 5, XE, XE3 (je nach Anforderung).

    Das Konzept ist aber durchaus auch ohne Generics umsetzbar, somit also keine Einschränkung. Die Message-Verarbeitung muss man auf allen drei Systemen selber erstellen, da System.Messaging AFAIK erst ab XE7 dabei ist. Das ist allerdings auch kein Hexenwerk und recht schnell zusammengebaut.