2010-03-07 18 views
5

¿Cómo se evita que se inicie un nuevo manejo de eventos cuando ya se está ejecutando un evento?Delphi y evitar el manejo de eventos

Presioné un botón1 y se inició el controlador de eventos, p. trabajo lento de impresión Existen varios controles en los botones de formulario, ediciones, combos y quiero que se permita un nuevo evento solo después de finalizar el controlador de ejecución.

He utilizado la variable fRunning para bloquear el controlador en el controlador de eventos compartidos. ¿Hay alguna manera más inteligente de manejar esto?

procedure TFormFoo.Button_Click(Sender: TObject);  
begin 
    if not fRunning then 
    try 
    fRunning := true; 
    if (Sender = Button1) then // Call something slow ... 
    if (Sender = Button2) then // Call something ... 
    if (Sender = Button3) then // Call something ... 
    finally 
    fRunning := false; 
    end; 
end; 

Respuesta

6

Otra opción (que no requiere un campo de marca) sería asignar temporalmente NIL al evento:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    OldHandler: TNotifyEvent; 
begin 
    OldHandler := (Sender as TButton).OnClick; 
    (Sender as TButton).OnClick := nil; 
    try 
    ... 
    finally 
    (Sender as TButton).OnClick := OldHandler; 
    end; 
end; 

Por razones de conveniencia esto podría ser envuelto en una interfaz:

interface 

function TempUnassignOnClick(_Btn: TButton): IInterface; 

implementation 

type 
    TTempUnassignOnClick = class(TInterfacedObject, IInterface) 
    private 
    FOldEvent: TNotifyEvent; 
    FBtn: TButton; 
    public 
    constructor Create(_Btn: TButton); 
    destructor Destroy; override; 
    end; 

constructor TTempUnassignOnClick.Create(_Btn: TButton); 
begin 
    Assert(Assigned(_Btn), 'Btn must be assigned'); 

    inherited Create; 
    FBtn := _Btn; 
    FOldEvent := FBtn.OnClick; 
    FBtn.OnClick := NIL; 
end; 

destructor TTempUnassignOnClick.Destroy; 
begin 
    FBtn.OnClick := FOldEvent; 
    inherited; 
end; 

function TempUnassignOnClick(_Btn: TButton): IInterface; 
begin 
    Result := TTempUnassignOnClick(_Btn); 
end; 

para ser utilizado como esto:

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    TempUnassignOnClick(Sender as TButton); 
    ... 
end; 
+1

Esta buena solución si solo hay un Button1 en su forma. El OnClick de Button1 está deshabilitado, pero Button2 puede activar un nuevo evento OnClick si se llama a Application.ProcessMessages durante el manejo del evento Button1. – pKarelian

+0

Gracias dummzeuch. El contenedor de interfaz es una forma muy práctica de destruir el objeto de evento temporal. No tienes que llamar gratis(). – pKarelian

+1

+1 pero en * teoría *, existe la posibilidad de que el evento no se vuelva a conectar durante un tiempo. En la práctica, creo que es seguro suponer que la instancia de interfaz se destruye cuando se realiza la llamada (y el controlador de eventos se vuelve a adjuntar). –

2

Usted no tiene que hacer esto en absoluto, ya que todo esto está sucediendo en el hilo principal (VCL): Ningún otro botón (VCL) evento se puede introducir hasta que el evento anterior (VCL) controlador ha devuelto ... La ejecución simultánea de otro controlador de eventos solo podría ocurrir inesperadamente si algún otro hilo entraba preventivamente en un segundo evento de botón (antes de que el primero se haya completado), pero eso no puede suceder, ya que solo hay uno Hilo VCL.

Ahora, si lo largo que está haciendo se hace en otro subproceso porque no desea que bloquee la GUI, entonces simplemente puede establecer la propiedad Button.Enabled en falso hasta que finalice su procesamiento.
Y si decide simplemente colocar el evento de botón hasta que todo se haya completado, use application.processmessages con la frecuencia suficiente en su ciclo de procesamiento para evitar que se congele la interfaz gráfica. En cuyo caso, sí, debe deshabilitar el botón original para evitar la reentrada.

+1

Lo siento, eso no es cierto. – pKarelian

+1

Entonces me interesaría que me explicaran cómo la VCL puede ejecutar dos eventos de botón al mismo tiempo en un contexto de subproceso único. – filofel

+6

No es cierto. Los eventos pueden anidarse y la recursión puede ocurrir fácilmente, especialmente cuando se llama a Application.ProcessMessages dentro del código de evento (como a menudo tenemos que hacer). –

0

Si su aplicación es de subproceso único, mientras su código de controlador de eventos se está ejecutando, su aplicación no puede ejecutar otros códigos, por lo que todas las llamadas a ese controlador de eventos se serializarán, y usted no necesita estar preocupado.

Si su controlador de eventos está ejecutando cualquier trabajo asincrónico, puede utilizar la técnica que presentó en su pregunta.

+4

A menos que el código llame a Application.ProcessMessages. En cuyo caso, el controlador de ventilación puede ser llamado dos veces. –

2

Su solución está bien. También puede vincular los clics del botón con las acciones y habilitar/deshabilitar las acciones en el controlador de eventos TAction.OnUpdate, pero aún necesita el indicador de ejecución para hacerlo. La línea "si no hay fRunning" no puede ser nessesary aquí, pero no eliminado porque es más seguro:

// Button1.Action = acButton1, Button2.Action = acButton2, etc 

procedure TForm1.acButtonExecute(Sender: TObject); 
begin 
    if not fRunning then 

    try 
    fRunning:= True; 
    if (Sender = acButton1) then // Call something slow ... 
    if (Sender = acButton2) then // Call something ... 
    if (Sender = acButton3) then // Call something ... 
    finally 
    fRunning:= False; 
    end; 

end; 

procedure TForm1.acButtonUpdate(Sender: TObject); 
begin 
    (Sender as TAction).Enabled:= not fRunning; 
end; 
+2

Otro método es simplemente establecer Enabled: = False en el nivel del formulario. Esto es obviamente mejor con un intento ... finalmente controlador –

+0

Gracias Serg. Voy a probar la solución TActionList. – pKarelian

2

Como Gerry ya se ha mencionado en uno de los comentarios, se puede desactivar toda forma:

procedure TFormFoo.Button_Click(Sender: TObject);  
begin 
    try 
    Enabled := False; 
    //... 
    finally 
    Enabled := True; 
    end; 
end; 
+0

Gracias Torbins. Buena y simple solución. – pKarelian

Cuestiones relacionadas