Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Prioridad de ejecución de eventos (https://www.clubdelphi.com/foros/showthread.php?t=43354)

Flecha 09-05-2007 11:05:32

Prioridad de ejecución de eventos
 
Tengo un caso algo complejo de explicar. Así que primero expondré la pregunta y luego intentaré exponer el caso concreto.

La pregunta:
Tengo un TDataSet cualquiera (TTable o TQuery), y dos TDataSource que están enlazados a dicho TDataSet. También tengo programados los eventos OnDataChange de sendos TDataSource.
¿Existe alguna manera que forzar a que siempre se ejecute un OnDataChange antes que el otro?

Mi problema en concreto:
Me he creado una clase cuya finalidad es calcular el valor de una serie de campos calculados de forma centralizada en toda la aplicación. El equivalente a esto habría sido poner un montón de campos calculados en muchísimos TTable y TQuery, y repetir en todos los eventos OnCalcFields el mismo ritual de cálculo de todos esos campos, ya que las fórmulas serían siempre las mismas.
En su lugar he optado por crearme una clase que contiene un TDataSource y un TQuery. El TDataSource lo enlazo con TDataSet's externos a la clase, y el TQuery lo enlazo al TDataSource. Así consigo una relación "maestro-detalle" en la que el "maestro" es el TDataSet externo. El TQuery interno es el encargado de realizar los cálculos de campos calculados, y más cosas.
Todo me va perfecto excepto en una cosa. En ocasiones, el TDataSet externo tiene otro TDataSource relacionado a él, aparte del contenido en mi clase. En esos casos necesito que siempre se ejecute el evento OnDataChange del TDataSource de mi clase antes del OnDataChange del TDataSource externo a mi clase, pero por lo general sucede siempre al revés.

¿Alguna idea? :(

Flecha 09-05-2007 15:17:17

¡¡ Ya está !!
 
:cool:
Ya he encontrado una solución. No es la que más me gusta, pero era la única posible.

En fin. Resulta que la clase TDataSet contiene una variable llamada FDataSources (de tipo TList) que contiene la lista de todos los TDataSources que apuntan a dicho TDataSet. Cuando a la propiedad DataSet de un TDataSource se le asigna un TDataSet, dicho TDataSource es añadido a dicha lista FDataSources del TDataSet al que apunta. Y cuando se asigna nil, el TDataSource es borrado de dicha lista.

Cuando se producen eventos en el TDataSet, éste hace uso de su lista FDataSources para "despertar" los eventos de los TDataSource que estén apuntando al TDataSet. Por tanto, los eventos de TDataSource que se ejecutan antes son los de aquellos TDataSource que estén los primeros de la lista FDataSource.

Para solucionar mi problema me habría bastado con acceder a la lista FDataSource y hacer uso de su método Move() (ya que es de tipo TList) para cambiar el orden de los TDataSource contenidos en ella y poner el mío el primero de la lista. Pero por desgracia, tanto FDataSource como los procedimientos que lo manejan son private, así que no se puede acceder a ellos desde fuera.

Así que he optado por otra solución. Partiendo de la variable global Application he buscado todos los TDataSource creados dentro de la aplicación y que estén apuntando al TDataSet en concreto. A todos esos TDataSource les rompo su relación con el TDataSet para que desaparezcan de la lista FDataSource. Después les vuelvo a relacionar con su TDataSet y al ser incorporados de nuevo a la lista FDataSource son incorporados al final de la misma. Conclusión: he conseguido que mi TDataSource pase al primer lugar de dicha lista y que sus eventos sean los primeros de todos en ejecutarse.
Misión cumplida. :D

Aquí os paso el código fuente del procedimiento que me hace eso que os comento, por si a alguien le valiera de algo:
Código Delphi [-]
    procedure ReOrdenarDataSource(Componente:TComponent; MiDataSource:TDataSource);
    var x:integer;
    begin
      //==================
      // Este procedimiento asegura que los eventos de mi TDataSource local
      // se ejecuten antes que ningún otro TDataSource que apunte al mismo
      // TDataSet.
      //==================
      //------ Recorro los componentes contenidos
      for x:=0 to Componente.ComponentCount-1 do begin
        //------ Si a su vez puede contener TDataSource, entro en él
        if (Componente.Components[x] is TForm)
        or (Componente.Components[x] is TDataModule)
        or (Componente.Components[x] is TQuickRep)
        Then Begin
          ReOrdenarDataSource(Componente.Components[x], MiDataSource);
          Continue;
        End;
        //------ Busco otros TDataSource que apunten al mismo TDataSet
        {$B-}
        if (not (Componente.Components[x] is TDataSource))
        or (not (TDataSource(Componente.Components[x]).DataSet = MiDataSource.DataSet))
        or (Componente.Components[x] = MiDataSource))
        then Continue;
        //------ Rompo la relación entre el TDataSource y el TDataSet
        TDataSource(Componente.Components[x]).DataSet := nil;
        //------ Vuelvo a relacionar el TDataSource con el TDataSet
        TDataSource(Componente.Components[x]).DataSet := MiDataSource.DataSet;
      end;
    end;

La llamada a este procedimiento es esta:
Código Delphi [-]
  ReOrdenarDataSource(Application, MiDataSourceLocal);

Un saludo a todos.

drykea 10-05-2007 17:02:34

Hola Flecha.

Creo que la solución que buscas es muy simple, en el evento OnDatachange del datasource que quieres que se ejecute en segundo lugar, pones como primera instrucion del código una llamada al evento OnDatachange del datasource que quieres que se ejecute antes.


Quedaría algo así:

procedure TForm1.DataSource2DataChange(Sender: TObject; Field: TField);
begin
TForm1.DataSource1DataChange(TObject; TField);
end;

Espero que te sirva de ayuda.

Un saludo.

Flecha 15-05-2007 20:23:22

Por supuesto que me vale drykea.
De hecho era la solución que me guardaba en la recámara por si no encontraba otra mejor.

Pero yo quería que mi objeto fuera completamente independiente del mundo exterior. Con la solución que propones estaría obligado a siempre acordarme (yo y cualquiera de mis compañeros) de incluir esa llamada dentro del evento que se ha de ejecutar en segundo lugar. Pero ¿qué hacer si en vez de haber sólo 2 TDataSource en juego (el mío y el otro externo) hay 3 o más (el mío, y 2 o más externos)? No puedo incluir esa llamada en todos los eventos del resto de eventos.

Con la solución que al final encontré me garantizo totalmente esa independiencia que buscaba y además que el evento de mi objeto se ejecutará siempre antes que el evento de todos los demás TDataSource.

Muchas gracias de todos modos.
Un saludo.


La franja horaria es GMT +2. Ahora son las 20:44:27.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi