Ir al contenido


Foto

[RESUELTO] Como Crear un Tpanel que detecte los cambios del State del Datasource


  • Por favor identifícate para responder
7 respuestas en este tema

#1 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 mensajes
  • LocationEspaña

Escrito 02 febrero 2011 - 11:48

Hola compañeros tengo una idea para un nuevo componente que he intentado implementar,  sin éxito hasta el momento, claro, pensaba que era más fácil, pero de eso nada.
La idea es la siguiente
Crear un panel que vinculamos a un Tdatasource,, con dos propiedades simples TipoDeAccion (xEnabled, xVisible) e AccionEstandar (boolean)
si el state es insert o edit ejecutar según la acción que elijamos y según la accionEstandar
Pudiendo darse cuatro combinaciones

Caso 1
State=Edit o Insert-----------------------State=No Edit o Insert
TipoDeAccion= xEnabled 
AccionEstandar =true
Panel Enabled----------------------------Panel No Enablel

Caso 2
State=Edit o Insert-----------------------State=No Edit o Insert
TipoDeAccion= xEnabled 
AccionEstandar =False
Panel No Enabled-------------------------Panel  Enabled

Caso 3
State=Edit o Insert-----------------------State=No Edit o Insert
TipoDeAccion= xVisible 
AccionEstandar =true
Panel Visible------------------------------Panel No visible

y por último  Caso 4
State=Edit o Insert-----------------------State=No Edit o Insert
TipoDeAccion= xVisible 
AccionEstandar =False
Panel No Visible--------------------------Panel visible

No os pongo el código ya que lo tengo en la empresa y no aquí ahora mismo,
El problema es que no logro detectar los cambios del State del Tdatasourse.
Si podéis ayudarme e incluso poner un ejemplo, más básico, por ejemplo que cambie el color cuando esta en estos modos y normal cuando no, me ayudaría mucho.
Como siempre gracias por vuestra ayuda un saludo, y si termino este componente como siempre lo publicare, como no.
  • 0

#2 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 02 febrero 2011 - 12:15

Hola.

Para detectar los cambios en el TDatasource tienes que usar el evento OnStateChange del TDatasource.

Por eso yo creo que en la inicialización de tu componente deberías programar ese evento en el TDatasource que tenga asociado.

Saludos.
  • 0

#3 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 02 febrero 2011 - 01:03

Hola

Lo que estás programando es un control DBAware. Aunque no lo uses para editar, si quieres recibir notificaciones automáticas desde el DataSet / DataSource cada vez que sucede algo, como por ejemplo un cambio en el estado del DataSet, debes utilizar un objeto TDataLink (unit db.pas) o descendiente, que sirven para ligar controles con fuentes de datos.

La clase TDataLink tiene un un método virtual ActiveChanged que es llamado cuando el DataSet vinculado se abre o cierra, y un método EditingChanged que es llamado cuando cambia el estado de dicho DataSet, esto es lo que buscas. Como ambos métodos son virtuales y no hacen nada en la clase TDataLink, puedes o bien crearte una clase descendiente o, lo que te recomiendo, tomar un objeto TFieldDataLink (unit DBCtrls.pas) que ya trae implementado algún código para responder a estos métodos (aunque TFieldDataLink está pensado para ligar con un campo concreto, en tu caso puedes ignorar esta funcionalidad tranquilamente).

Te pongo un esquema de como manejar un objeto de este tipo en tu Panel:



delphi
  1. TMiPanel = class(TPanel)
  2.   private
  3.     FDataLink : TFieldDataLink;
  4.     function GetDataSource: TDataSource;
  5.     procedure SetDataSource(const Value: TDataSource);
  6.   protected
  7.     procedure EditingChanged(Sender: TObject);  virtual;
  8.     procedure Notification(AComponent: TComponent; Operation: TOperation);  override;
  9.   public
  10.     constructor Create(AOwner: TComponent);  override;
  11.     destructor Destroy;  override;
  12.   published
  13.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  14.   end;
  15.  
  16. implementation
  17.  
  18. (...)
  19.  
  20. { TMiPanel }
  21.  
  22. constructor TMiPanel.Create(AOwner: TComponent);
  23. begin
  24.   inherited Create(AOwner);
  25.   FDataLink := TFieldDataLink.Create;
  26.   FDataLink.OnEditingChange := EditingChanged;
  27. end;
  28.  
  29. procedure TMiPanel.EditingChanged(Sender: TObject);
  30. begin
  31.   // aquí lo que quieras hacer
  32. end;
  33.  
  34. destructor TMiPanel.Destroy;
  35. begin
  36.   FreeAndNil(FDataLink);
  37.   inherited Destroy;
  38. end;
  39.  
  40. function TMiPanel.GetDataSource: TDataSource;
  41. begin
  42.   result := FDataLink.DataSource;
  43. end;
  44.  
  45. procedure TMiPanel.Notification(AComponent: TComponent;
  46.   Operation: TOperation);
  47. begin
  48.   inherited Notification(AComponent, Operation);
  49.   if (Operation = opRemove) AND (FDataLink <> nil) AND
  50.     (AComponent = DataSource) then DataSource := nil;
  51. end;
  52.  
  53. procedure TMiPanel.SetDataSource(const Value: TDataSource);
  54. begin
  55.   if NOT (FDataLink.DataSourceFixed AND (csLoading in ComponentState)) then
  56.     FDataLink.DataSource := Value;
  57.   if Assigned(Value) then Value.FreeNotification(Self);
  58. end;



Como ves, ese objeto DataLink hace de nexo entre tu Panel y el DataSource. La propiedad DataSource de tu Panel se toma y se almacena en dicho DataLink (métodos GetDataSource y SetDataSource), y éste ya se encarga de recibir y propagar las notificaciones que tu deseas. Si te fijas, en el constructor asignamos el evento OnEditingChange del DataLink a un método llamado EditingChanged del Panel, ahí dentro programas lo que quieres hacer cuando cambie el estado del DataSet.

(El método Notification se sobreescribe aquí por si se elimina el objeto DataSource ligado, para ponerlo a nil internamente y que no salten access violations.)

Saludos
  • 0

#4 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 mensajes
  • LocationEspaña

Escrito 03 febrero 2011 - 10:25

Hola andres1569, hice las pruebas y no me ha funcionado, te pongo el código, que  copie y pegue, poniendo únicamente la prueba de color



Código



delphi
  1. unit PanelPru;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Classes, Controls, ExtCtrls, Db, Dbctrls, Graphics;
  7.  
  8. type
  9.   TMiPanel = class(TPanel)
  10.   private
  11.     FDataLink : TFieldDataLink;
  12.     function GetDataSource: TDataSource;
  13.     procedure SetDataSource(const Value: TDataSource);
  14.   protected
  15.     procedure EditingChanged(Sender: TObject);  virtual;
  16.     procedure Notification(AComponent: TComponent; Operation: TOperation);  override;
  17.   public
  18.     constructor Create(AOwner: TComponent);  override;
  19.     destructor Destroy;  override;
  20.   published
  21.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  22.   end;
  23.  
  24. procedure Register;
  25.  
  26. implementation
  27.  
  28. { TMiPanel }
  29.  
  30. constructor TMiPanel.Create(AOwner: TComponent);
  31. begin
  32.   inherited Create(AOwner);
  33.   FDataLink := TFieldDataLink.Create;
  34.   FDataLink.OnEditingChange := EditingChanged;
  35. end;
  36.  
  37. procedure TMiPanel.EditingChanged(Sender: TObject);
  38. begin
  39.   // aquí lo que quieras hacer
  40.     if FDataLink.DataSet.State in [dsEdit,dsInsert] then Self.Color:=clGreen
  41.                                                     else Self.Color:=clBlue;
  42.  
  43. end;
  44.  
  45. destructor TMiPanel.Destroy;
  46. begin
  47.   FreeAndNil(FDataLink);
  48.   inherited Destroy;
  49. end;
  50.  
  51. function TMiPanel.GetDataSource: TDataSource;
  52. begin
  53.   result := FDataLink.DataSource;
  54. end;
  55.  
  56. procedure TMiPanel.Notification(AComponent: TComponent;
  57.   Operation: TOperation);
  58. begin
  59.   inherited Notification(AComponent, Operation);
  60.   if (Operation = opRemove) AND (FDataLink <> nil) AND
  61.     (AComponent = DataSource) then DataSource := nil;
  62. end;
  63.  
  64. procedure TMiPanel.SetDataSource(const Value: TDataSource);
  65. begin
  66.   if NOT (FDataLink.DataSourceFixed AND (csLoading in ComponentState)) then
  67.     FDataLink.DataSource := Value;
  68.   if Assigned(Value) then Value.FreeNotification(Self);
  69. end;
  70.  
  71. procedure Register;
  72. begin
  73.   RegisterComponents('Standard', [TMipanel]);
  74. end;
  75.  
  76. end.


  • 0

#5 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 03 febrero 2011 - 11:30

Hola Desart,

En efecto, te recomendé que usaras un TFieldDataLink pero resulta que este objeto está pensado para representar a un campo del DataSet, y si no tiene ninguno vinculado no entra en modo edición, y nunca llega a llamarse al evento OnEditingChange que te comenté.

Al final, mucho más efectivo derivar un objeto propio desde TDataLink y que haga justo lo que queremos, te pongo aquí el código fuente, ahora se usa un TPanelDatalink, por darle algún nombre, aunque bien podría servir para ligar otros controles que sólo necesiten acceder al DataSource sin indicar ningún campo, verás que es muy simple su implementación, solamente sobrescribe el método EditingChanged de TDataLink para que llame al evento que nos interesa:



delphi
  1. unit PanelPru;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Classes, Controls, ExtCtrls, Db, Graphics;
  7.  
  8. type
  9.   TPanelDataLink = class(TDataLink)
  10.   private
  11.     FOnEditingChange: TNotifyEvent;
  12.   protected
  13.     procedure EditingChanged; override;
  14.   public
  15.     property OnEditingChange: TNotifyEvent read FOnEditingChange write FOnEditingChange;
  16.   end;
  17.  
  18.   TMiPanel = class(TPanel)
  19.   private
  20.     FDataLink : TPanelDataLink;
  21.     function GetDataSource: TDataSource;
  22.     procedure SetDataSource(const Value: TDataSource);
  23.   protected
  24.     procedure EditingChanged(Sender: TObject);  virtual;
  25.     procedure Notification(AComponent: TComponent; Operation: TOperation);  override;
  26.   public
  27.     constructor Create(AOwner: TComponent);  override;
  28.     destructor Destroy;  override;
  29.   published
  30.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  31.   end;
  32.  
  33. procedure Register;
  34.  
  35. implementation
  36.  
  37. { TPanelDataLink }
  38.  
  39. procedure TPanelDataLink.EditingChanged;
  40. begin
  41.   if Assigned(FOnEditingChange) then FOnEditingChange(Self);
  42. end;
  43.  
  44. { TMiPanel }
  45.  
  46. constructor TMiPanel.Create(AOwner: TComponent);
  47. begin
  48.   inherited Create(AOwner);
  49.   FDataLink := TPanelDataLink.Create;
  50.   FDataLink.OnEditingChange := EditingChanged;
  51. end;
  52.  
  53. procedure TMiPanel.EditingChanged(Sender: TObject);
  54. begin
  55.   if Assigned(FDataLink.DataSet) then
  56.     if FDataLink.DataSet.State in [dsEdit,dsInsert] then Self.Color := clGreen
  57.     else Self.Color := clBlue;
  58. end;
  59.  
  60. destructor TMiPanel.Destroy;
  61. begin
  62.   FDataLink.Free;
  63.   inherited Destroy;
  64. end;
  65.  
  66. function TMiPanel.GetDataSource: TDataSource;
  67. begin
  68.   result := FDataLink.DataSource;
  69. end;
  70.  
  71. procedure TMiPanel.Notification(AComponent: TComponent;
  72.   Operation: TOperation);
  73. begin
  74.   inherited Notification(AComponent, Operation);
  75.   if (Operation = opRemove) AND (FDataLink <> nil) AND
  76.     (AComponent = DataSource) then DataSource := nil;
  77. end;
  78.  
  79. procedure TMiPanel.SetDataSource(const Value: TDataSource);
  80. begin
  81.   if NOT (FDataLink.DataSourceFixed AND (csLoading in ComponentState)) then
  82.     FDataLink.DataSource := Value;
  83.   if Assigned(Value) then Value.FreeNotification(Self);
  84. end;
  85.  
  86. procedure Register;
  87. begin
  88.   RegisterComponents('Standard', [TMipanel]);
  89. end;
  90.  
  91. end.



Esta vez lo he probado y cambia de color según el estado del DataSet  (y)

Saludos

NOTA: Le he quitado de la cláusula uses la referencia a Dbctrls, que antes hacía falta para acceder a la clase TFieldDataLink, también he añadido la comprobación "If Assigned(FDataLink.DataSet)" en el método EditingChanged de TMiPanel para evitar que salte algún error de access violation.
  • 0

#6 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 mensajes
  • LocationEspaña

Escrito 03 febrero 2011 - 01:36

Lo prometido es deuda y aquí esta mi componente, muchísimas gracias Andres  sin tu ayuda no se si lo hubiese terminado



delphi
  1. //**************************************************************************************************************
  2. //  NewPanelDb                                          03/02/2011
  3. //
  4. // Panel en el que no tenemos que controlar si esta enble o visible ya que lo detecta del Estado del datasourse
  5. // puede controlar en estado norma (cuando esta editando o insertando) esta en enable o visible, por ejemplo,
  6. // panel de los datos panel con los botones grabar y cancelar, etc.. O en inverso (cuando esta editando o
  7. //  insertando) no esta enable o visible, por ejemplo panel con la botonera para movernos por los registros,
  8. //  busqueda, nuevo, modificar, borrar, salir, imprimir, etc...
  9. //--------------------------------------------------------------------------------------------------------------
  10. // Aunque la idea Original es mía J.L.G.T. debo agradecer muchisimo la ayuda de Andres1569 de DelphiAccess sin
  11. // el cual no estaría terminado este componente
  12. //
  13. // Como todos mis componentes, puedes usarlo gratuitamente sin restricciones de ningún tipo
  14. //--------------------------------------------------------------------------------------------------------------
  15. //
  16. // Propiedades
  17. //
  18. //  Action: TTypeAction;          //Tipo de Acción a usar panel enabled o Visible
  19. //  InverseAction: Boolean;    //Si queremos que trabaje al revés, lo normal es State  es DsEdit Panel seria
  20. //                  Enabled, si esta opción esta seleccionada seria no enabled
  21. //  UseColor: Boolean;          //Usamos Color En Edit y Color En no Edit
  22. //  ActiveColor: Tcolor;          //Color a usar cuando este en edit o insert
  23. //  ColorNotActive:Tcolor;        //Color a usar cuando no
  24. //
  25. //***************************************************************************************************************
  26.  
  27.  
  28.  
  29. unit NewPanelDB;
  30.  
  31. interface
  32.  
  33. uses
  34.   SysUtils, Classes, Controls, ExtCtrls, Db, Graphics, Dialogs;
  35.  
  36. type
  37.   TTypeAction= (xEnable,xVisible);
  38.   TPanelDataLink = class(TDataLink)
  39.   private
  40.     FOnEditingChange: TNotifyEvent;
  41.   protected
  42.     procedure EditingChanged; override;
  43.   public
  44.     property OnEditingChange: TNotifyEvent read FOnEditingChange write FOnEditingChange;
  45.   end;
  46.  
  47.   TNewPanelDB = class(TPanel)
  48.   private
  49.     FDataLink : TPanelDataLink;
  50.     FAction: TTypeAction;                    //Tipo de Acción a usar panel enabled o Visible
  51.     FInverseAction: Boolean;                  // Si queremos que trabaje al reves, lo normal es
  52.                                               //State  es DsEdit Panelñ seria Enabled, si esta
  53.                                               //opcion esta seleccionada seria  no enabled
  54.     FUseColor: Boolean;                      //USamos Color En Edit y Color En no Edit
  55.     FActiveColor: Tcolor;                    //Color a usar cuando este en edit o insert
  56.     FColorNotActive:Tcolor;                  //Color a usar cuando no
  57.     function GetDataSource: TDataSource;
  58.     procedure SetDataSource(const Value: TDataSource);
  59.     procedure SetAction(const Value: TTypeAction);
  60.     procedure SetInverseAction(const Value: Boolean);
  61.     procedure SetUseColor(const Value: Boolean);
  62.     procedure SetActiveColor(const Value:Tcolor);
  63.     procedure SetColorNotActive(Const Value:Tcolor);
  64.   protected
  65.     procedure EditingChanged(Sender: TObject);  virtual;
  66.     procedure Notification(AComponent: TComponent; Operation: TOperation);  override;
  67.   public
  68.     constructor Create(AOwner: TComponent);  override;
  69.     destructor Destroy;  override;
  70.   published
  71.     property DataSource: TDataSource read GetDataSource  write SetDataSource;
  72.     property Action: TTypeAction    read FAction        write SetAction          default xEnable;
  73.     property InverseAction: Boolean  read FInverseAction  write SetInverseAction  default False;
  74.     property UseColor: Boolean      read FUseColor      write SetUseColor        default False;
  75.     property ActiveColor: Tcolor    read FActiveColor    write SetActiveColor    default clMoneyGreen;
  76.     property ColorNotActive:Tcolor  read FColorNotActive write SetColorNotActive  default clBtnFace;
  77.   end;
  78.  
  79. procedure Register;
  80.  
  81. implementation
  82.  
  83. { TPanelDataLink }
  84.  
  85. procedure TPanelDataLink.EditingChanged;
  86. begin
  87.   if Assigned(FOnEditingChange) then FOnEditingChange(Self);
  88. end;
  89.  
  90. { TMiPanel }
  91.  
  92. constructor TNewPanelDB.Create(AOwner: TComponent);
  93. begin
  94.   inherited Create(AOwner);
  95.   FDataLink := TPanelDataLink.Create;
  96.   FDataLink.OnEditingChange := EditingChanged;
  97.   FAction:=xEnable;
  98.   FInverseAction:=False;
  99.   FUseColor:=False;
  100.   FActiveColor:=clMoneyGreen;
  101.   FColorNotActive:=clBtnFace;
  102.   Color:=clGray;                    //Esta y la segunda linea es para asegurar los cambios posteriores
  103.   Color:=clBtnFace;
  104.   Enabled:=False;
  105. end;
  106.  
  107. procedure TNewPanelDB.EditingChanged(Sender: TObject);
  108. begin
  109.   if Assigned(FDataLink.DataSet) then
  110.     if FDataLink.DataSet.State in [dsEdit,dsInsert] then
  111.     begin    //Caundo insertamos o editamos
  112.       if FAction=xEnable then              //Usando Enabled
  113.       begin
  114.           if FInverseAction=False then  Self.Enabled:=True
  115.                                   else  Self.Enabled:=False;
  116.       end else
  117.       begin                                //Usando Visible
  118.           if FInverseAction=false then  Self.Visible:=True
  119.                                   else  Self.Visible:=False;
  120.       end;
  121.       if FUseColor=true then  self.Color:=FActiveColor;
  122.     end else
  123.     begin  //Caundo no insertamos o editamos
  124.       if FAction=xEnable then              //Usando Enabled
  125.       begin
  126.           if FInverseAction=False then  Self.Enabled:=False
  127.                                   else  Self.Enabled:=True;
  128.       end else
  129.       begin                                //Usando Visible
  130.           if FInverseAction=false then  Self.Visible:=False
  131.                                   else  Self.Visible:=True;
  132.       end;
  133.       if FUseColor=true then  self.Color:=FColorNotActive;
  134.     end;
  135. end;
  136.  
  137. destructor TNewPanelDB.Destroy;
  138. begin
  139.   FDataLink.Free;
  140.   inherited Destroy;
  141. end;
  142.  
  143. function TNewPanelDB.GetDataSource: TDataSource;
  144. begin
  145.   result := FDataLink.DataSource;
  146. end;
  147.  
  148. procedure TNewPanelDB.Notification(AComponent: TComponent;
  149.   Operation: TOperation);
  150. begin
  151.   inherited Notification(AComponent, Operation);
  152.   if (Operation = opRemove) AND (FDataLink <> nil) AND
  153.     (AComponent = DataSource) then DataSource := nil;
  154. end;
  155.  
  156. procedure TNewPanelDB.SetDataSource(const Value: TDataSource);
  157. begin
  158.   if NOT (FDataLink.DataSourceFixed AND (csLoading in ComponentState)) then
  159.     FDataLink.DataSource := Value;
  160.   if Assigned(Value) then Value.FreeNotification(Self);
  161. end;
  162.  
  163. procedure TNewPanelDB.SetAction(const Value: TTypeAction);
  164. begin
  165.     if FAction<>Value then  FAction:=Value;
  166.     if FAction=xEnable then
  167.     begin
  168.         if FInverseAction=false then Enabled:=false
  169.                                 else Enabled:=True;
  170.         Visible:=True;
  171.     end else
  172.     begin
  173.         if FInverseAction=false then Visible:=false
  174.                                 else Visible:=True;
  175.         Enabled:=true;
  176.     end;
  177. end;
  178.  
  179. procedure TNewPanelDB.SetInverseAction(const Value: Boolean);
  180. begin
  181.     if FInverseAction<>value then FInverseAction:=Value;
  182.     if FAction=xEnable then
  183.     begin
  184.         if FInverseAction=false then Enabled:=false
  185.                                 else Enabled:=True;
  186.         Visible:=true;
  187.     end else
  188.     begin
  189.         if FInverseAction=false then Visible:=false
  190.                                 else Visible:=True;
  191.         Enabled:=true;
  192.     end;
  193. end;
  194.  
  195. procedure TNewPanelDB.SetUseColor(const Value: Boolean);
  196. begin
  197.     if FUseColor<>value then  FUseColor:=Value;
  198.     color:=FColorNotActive;
  199. end;
  200.  
  201. procedure TNewPanelDB.SetActiveColor(const Value: TColor);
  202. begin
  203.     if FActiveColor<>value then  FActiveColor:=Value;
  204. end;
  205.  
  206. procedure TNewPanelDB.SetColorNotActive(const Value: TColor);
  207. begin
  208.     if FColorNotActive<>value then  FColorNotActive:=Value;
  209.     color:=FColorNotActive;
  210. end;
  211.  
  212. procedure Register;
  213. begin
  214.   RegisterComponents('Standard', [TNewPanelDB]);
  215. end;
  216.  
  217. end.


  • 0

#7 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 04 febrero 2011 - 11:09

Me alegro de que te haya servido, Jose Luís, y aunque te agradezco que me pongas en los créditos, francamente el componente es idea y realización tuya, si no yo cualquier otro compañero de DelphiAccess te hubiera indicado la mejor manera de vincular con el DataSource.

Para casos como este valdría poner simplemente lo de "DelphiAccess Inside", o bien "Powered by DelphiAccess"  (y) , tal como se comentó en este hilo cuando se eligió el nombre de nuestra mascota

Por otro lado, en los componentes que has ido mostrándonos en el foro veo que siempre prima de forma especial el aspecto visual, ahora me acuerdo de aquél en que hacías un zoom (o le aumentabas el tamaño de letra) al control que estaba activo ... (y)

Saludos
  • 0

#8 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 mensajes
  • LocationEspaña

Escrito 04 febrero 2011 - 11:37

Gracias Andres, en Cuanto a lo de que suelo optar por el aspecto visual, es cierto suelo intentar que mis programas tengan un aspecto diferente, pero casí siempre basándome en la posible utilidad del componente y el ahorro de código.
  • 0