2012-05-06 15 views

risposta

24

Penso che si possa lasciare lo TTimer così com'è e provare a utilizzare la funzione SetTimer e utilizzare la sua funzione di richiamata. È necessario memorizzare gli ID timer e i loro metodi (anonimi) in alcune raccolte. Dal momento che non hai menzionato la tua versione di Delphi, ho usato una semplice classe e TObjectList come raccolta.

Il principio è semplice, basta chiamare la funzione SetTimer con la funzione di callback specificata e archiviare il nuovo ID timer di sistema istanziato con il metodo anonimo nella raccolta. Quando viene eseguita questa funzione di callback, trova il timer che ha causato quella richiamata nella raccolta tramite il suo ID, uccidila, esegui il metodo anonimo ed eliminalo dalla raccolta. Ecco il codice di esempio:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, ExtCtrls, StdCtrls, Contnrs; 

type 
    TOnTimerProc = reference to procedure; 
    TOneShotTimer = class 
    ID: UINT_PTR; 
    Proc: TOnTimerProc; 
    end; 
    procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal); 

type 
    TForm1 = class(TForm) 
    Timer1: TTimer; 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 
    TimerList: TObjectList; 

implementation 

{$R *.dfm} 

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; 
    dwTime: DWORD); stdcall; 
var 
    I: Integer; 
    Timer: TOneShotTimer; 
begin 
    for I := 0 to TimerList.Count - 1 do 
    begin 
    Timer := TOneShotTimer(TimerList[I]); 
    if Timer.ID = idEvent then 
    begin 
     KillTimer(0, idEvent); 
     Timer.Proc(); 
     TimerList.Delete(I); 
     Break; 
    end; 
    end; 
end; 

procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal); 
var 
    Timer: TOneShotTimer; 
begin 
    Timer := TOneShotTimer.Create; 
    Timer.ID := SetTimer(0, 0, ATimeout, @TimerProc); 
    Timer.Proc := AProc; 
    TimerList.Add(Timer); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    SetTimeout(procedure 
    begin 
     ShowMessage('OnTimer'); 
    end, 
    1000 
); 
end; 

initialization 
    TimerList := TObjectList.Create; 
    TimerList.OwnsObjects := True; 

finalization 
    TimerList.Free; 

end. 


versione semplificata (Delphi 2009 su):

Come suggerito da @ il commento di David, qui è lo stesso codice come sopra, solo in un separato unità con l'uso del dizionario dei generici. Utilizzo della SetTimeout da questa unità è stesso nel codice sopra:

unit OneShotTimer; 

interface 

uses 
    Windows, Generics.Collections; 

type 
    TOnTimerProc = reference to procedure; 
    procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal); 

var 
    TimerList: TDictionary<UINT_PTR, TOnTimerProc>; 

implementation 

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; 
    dwTime: DWORD); stdcall; 
var 
    Proc: TOnTimerProc; 
begin 
    if TimerList.TryGetValue(idEvent, Proc) then 
    try 
    KillTimer(0, idEvent); 
    Proc(); 
    finally 
    TimerList.Remove(idEvent); 
    end; 
end; 

procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal); 
begin 
    TimerList.Add(SetTimer(0, 0, ATimeout, @TimerProc), AProc); 
end; 

initialization 
    TimerList := TDictionary<UINT_PTR, TOnTimerProc>.Create; 
finalization 
    TimerList.Free; 

end. 
+4

Questo è un pezzo di codice davvero interessante, ben fatto! – Pateman

+0

È necessario eseguire quel ciclo all'indietro perché si elimina dall'elenco iterato su –

+0

@David, non necessario qui. Sto cancellando solo un elemento e rompendo il ciclo. – TLama

1

Qualcosa di simile

type 
TMyProc = Procedure of Object(Sender: TObject); 

TMyClass = Object 
    HandlerList = TStringList; 
    TimerList = TStringlist; 

    Procedure CallThisFunction(Sender :TObject); 

    function setTimeout(Timeout: Integer; ProcToCall : TMyProc) 

end; 






function setTimeout(Timeout: Integer; ProcToCall : TMyProc) 
var 
    Timer : TTimer; 
begin 

    Timer := TTimer.Create(nil); 
    Timer.OnTimer := CallOnTimer; 
    Timer.Interval := Timeout; 
    Timer.Enabled := true; 
    HandlerList.AddObject(ProcToCall); 
    TimerList.AddObject(ProcToCall); 

end; 


function CallOnTimer(Sender : TObject) 
var TimerIndex : Integer; 
    HandlerToCall : TMyProc; 
    Timer : TTimer; 
begin 

TimerIndex := TimerList.IndexOfObject(Sender); 
HandlerToCall := (HandlerList.Objects[TimerIndex] as TMyProc) ; 

HandlerToCall(Self); 

HandlerList.Delete(TimerIndex); 
Timer := (TimerList.Objects(TimerIndex) as TTimer); 
Timer.Free; 
TimerList.Delete(TimerIndex); 


end; 

Questo è stato appena messo insieme e non provato in alcun modo, ma mostra il concetto. Fondamentalmente crea un elenco dei timer e delle procedure che desideri chiamare. Poiché è l'oggetto self viene passato alla procedura quando viene chiamato ma è possibile creare una terza lista che contiene l'oggetto da utilizzare come parametro nella chiamata a setTimeout.

Gli oggetti vengono quindi ripuliti liberando dopo che il metodo è stato chiamato.

Non proprio lo stesso di javascripts setTimeout ma un'approssimazione delphi.

ps. Non mi sono ancora trasferito da Delphi7, quindi se c'è un nuovo modo di farlo in Delphi XE non ne so nulla.

+0

no, voglio usare [funzione anonima] (http://delphi.about.com/od/objectpascalide/a/understanding-anonymous-methods-in-delphi.htm). il tuo codice usa il puntatore del metodo. – MajidTaheri

0

Supponendo, la funzione deve essere chiamata una volta e non 5 volte al secondo, forse così:

Parallel.Async( 
     procedure; begin 
      Sleep(200); 
      Self.Counter:=Self.Counter+1; end;); 

Ci sono soluzioni più complesse come quella che hai accettato, prendendo oggetti con nome per le azioni del timer e usando il metodo SetTimer. Mi piace http://code.google.com/p/omnithreadlibrary/source/browse/trunk/tests/17_MsgWait/test_17_MsgWait.pas Le versioni precedenti avevano SetTimer con funzione anonima, ma sono sparite ora.

Tuttavia, per l'approccio di chiusura anonima semplicistico richiesto, potrebbe essere necessario attendere (xxX).

0

faccio di solito in questo modo

TThread.CreateAnonymousThread(procedure begin 
    Sleep(1000); // timeout 

    // put here what you want to do 

end).Start; 
Problemi correlati