PDA

Ver la Versión Completa : Error en Componente TDBGridPlus


HombreSigma
25-07-2003, 04:43:14
Tengo un componente propio llamado TDBGridPlus descendiente de TDBGrid como se muestra en el código siguiente:

unit DBGridPlus;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, DBGrids, DB;

type
TDBGridPlus = class(TDBGrid)
private
procedure MiDrawDataCell
(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
public
{ Public declarations }
property InplaceEditor;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

implementation

constructor TDBGridPlus.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Self.OnDrawDataCell:=MiDrawDataCell;
end;

procedure TDBGridPlus.MiDrawDataCell
(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
var
Grid : TStringGrid;
Texto : String;
Rectangulo : TRect;
begin
Rectangulo:=Rect;
Grid := TStringGrid(Sender);
if Field.IsBlob
then begin
Grid.Canvas.FillRect(Rect);
Texto := Field.AsString;
DrawText( Grid.Canvas.Handle,
PChar(Texto),
StrLen(PChar(Texto)),
Rectangulo,
DT_WORDBREAK);
end;
end;

destructor TDBGridPlus.Destroy;
begin
inherited Destroy;
end;


end.

Como se ve trato de llamar cierto código nuevo dentro del evento OnDrawDataCell.

Pero no me funciona. No me interesa que el nuevo código funcione (que en este caso muestra un campo memo cualquiera y funciona) sino que se active cuando use el evento OnDrawDataCell. Pero ni siquiera se ejecuta.

Alguna sugerencia ?

Gracias por la información.

marto
26-07-2003, 03:13:05
Hola,

No estoy seguro, pero se me ocurre que si creas el componente en diseño, el puntero al evento deje de apuntar a tu función, ya que normalmente todos aparecen apuntando a Nil.
De todas maneras, en todos los componentes que he creado a partir de otros de la VCL, donde he tenido que hacer cosas parecidas a la que tú haces he usado otra técnica que me parece mejor.
Te cuento, normalmente, para cada evento de un componente existe un procedimiento protected y virtual que es el que se encarga de llamar al evento si este esta asignado. Borland decidió hacer esto así pensando en que si alguien quería heredar de un componente pudiese modificar el comportamiento ante cierto evento sin tener que usar directamente el propio puntero. Además, esto es peligroso, ya que si se instancia el nuevo componente y se quiere programar dicho evento, perderás la funcionalidad que habías añadido al heredar. No sé si me he explicado, pero me permito modificar tu código como ejemplo.
De todas formas, ten en cuenta que no tengo el Delphi a mano y no estoy seguro que el procedimiento que voy a sobreescribir exista o se llame así, pero como ejemplo creo que servirá.


unit DBGridPlus;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, DBGrids, DB;

type
TDBGridPlus = class(TDBGrid)
private
procedure MiDrawDataCell(Sender: TObject; const Rect: TRect;Field: TField; State: TGridDrawState);
protected
procedure DoDrawDataCell(const Rect: TRect;Field: TField; State: TGridDrawState);override;
//este es el procedimiento que llama al evento, comprueba que es así

public
{ Public declarations }
property InplaceEditor;
//me he cargado el constructor y destructor porque no hacian falta
end;

implementation

procedure TDBGridPlus.DoDrawDataCell(const Rect: TRect;Field: TField; State: TGridDrawState);
begin
MiDrawDataCell(Self, Rect, Field, State);
inherited DoDrawDataCell(Rect, Field, State);
{

llamar antes o despues al inherited depende si quieres que se ejecute antes tu código o el de el evento.
Piensa que el codigo de este procedure será algo asi:
procedure TDBGrid.DoDrawDataCell(const Rect: TRect;Field: TField; State: TGridDrawState);
begin
if Assigned(FOnDrawDataCell) then
FOnDrawDataCell(Rect, Field, State);
end;

}
end;

procedure TDBGridPlus.MiDrawDataCell(Sender: TObject; const ect: TRect; Field: TField; State: TGridDrawState);
var
Grid : TStringGrid;
Texto : String;
Rectangulo : TRect;
begin
Rectangulo:=Rect;
Grid := TStringGrid(Sender);
if Field.IsBlob
then begin
Grid.Canvas.FillRect(Rect);
Texto := Field.AsString;
DrawText( Grid.Canvas.Handle,
PChar(Texto),
StrLen(PChar(Texto)), Rectangulo, DT_WORDBREAK);
end;
end;



end.

HombreSigma
26-07-2003, 22:28:28
Gracias por tu sugerencia.

Sin embargo, la nueva versión tampoco funciona:

unit DBGridPlus;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, DBGrids, DB;

type
TDBGridPlus = class(TDBGrid)
private
protected
procedure DefaultDrawColumnCell
(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
public
{ Public declarations }
property InplaceEditor;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure MiDefaultDrawColumnCell
(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
end;

implementation

uses DBTables;

constructor TDBGridPlus.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;

destructor TDBGridPlus.Destroy;
begin
inherited Destroy;
end;

procedure TDBGridPlus.MiDefaultDrawColumnCell(const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
P:array [0..50] of char;
BS:tBlobStream;
S:String;
begin
if Column.Field is TMemoField then begin
with Self.Canvas do begin
BS:=tBlobStream.Create(TMemoField(Column.Field), bmRead);
FillChar(P,SizeOf(P),#0); {termina la cadena nula}
BS.Read(P, 50); {Lee 50 caracteres del memo dentro del blobStream}
BS.Free;
S:=StrPas(P);
while Pos(#13, S) > 0 do {remueve caracteres retornos de carro y nueva linea}
S[Pos(#13, S)]:=' '; {Retorno de carro}
while Pos(#10, S) > 0 do
S[Pos(#10, S)]:=' '; {Nueva linea}
FillRect(Rect); {Limpia la celda}
TextOut(Rect.Left, Rect.Top, S); {Llena celda con dato memo}
end;
end;
end;

procedure TDBGridPlus.DefaultDrawColumnCell(const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
MiDefaultDrawColumnCell
(Rect, DataCol, Column, State);
inherited DefaultDrawColumnCell
(Rect, DataCol, Column, State);
//if Assigned(FOnDrawDataCell) then
// FOnDrawDataCell(Rect, Field, State);
end;

end.


Notarás que cambie el método a 'DefaultDrawColumnCell' porque Delphi 7 sugiere que trabaje con este, pues el otro es obsoleto.

El código que pretendo ejecutar escribe un memo en una rejilla. Este algoritmo funciona si lo coloco en el evento OnDrawColumCell del TDBGrid del formulario. Mi idea es que este pueda ejecutarse siempre que use TDBGridPlus que es heredado de TDBGrid. Pero ni siquiero logro que se ejecute !!

Deje el construct y el destructor por si acaso los necesito, en otra versión modificada del código.

DE nuevo acepto sugerencias.

Gracias de antemano

andres1569
27-07-2003, 08:18:00
Hola:

Creo que deberías redefinir el método DrawColumnCell que es dynamic (virtual), y no DefaultDrawColumnCell, este método es estático y parece estar pensado para llamarlo desde nuestro código si queremos conseguir el funcionamiento por defecto y así evitarnos trabajo extra.

En realidad, a partir de Delphi 4.0, están obsoletos los métodos DrawDataCell y DefaultDrawDataCell, y se deben emplear DrawColumnCell y DefaultDrawColumnCell (cambia lo de Data por Column), pero ten en cuenta lo que te he dicho anteriormente, el método DefaultDrawColumnCell no se debe sobreescribir (en tu caso lo que has hecho ha sido definir uno nuevo con el mismo nombre, no te habrá dejado ponerle override porque es estático). El método que interesa es DrawColumnCell.

La cosa quedaría así:

type

TDBGridPlus = class(TDBGrid)
protected
procedure DrawColumnCell (const Rect: TRect; DataCol: Integer;
Column: TColumn; State: TGridDrawState); override;
public
// el procedure MiDefaultDrawColumnCell ahora sobra
end;

...

procedure TDBGridPlus.DrawColumnCell(const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
// aquí pones el código que antes se hacía en MiDefaultDrawColumnCell
// y te evitas ese método, puesto que reciben los mismos parámetros
inherited DrawColumnCell;
end;
Como muy bien te explicó Marto, dependiendo de lo quieras lograr, colocarás antes o después la llamada a inherited DrawColumnCell;

HombreSigma
27-07-2003, 17:47:01
Gracias por las anotaciones, ambas sirvieron.

He aquí el componente TDBGridplus que permite visualizar (aunque no modificar, pero este codigo es mucho más facil de agregar) campos memo, por si a alguien de foro le interesa.

unit DBGridPlus;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, DBGrids, DB;

type
TDBGridPlus = class(TDBGrid)
private
protected
procedure DrawColumnCell
(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);override;
public
{ Public declarations }
property InplaceEditor;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

implementation

uses DBTables;

constructor TDBGridPlus.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;

destructor TDBGridPlus.Destroy;
begin
inherited Destroy;
end;

procedure TDBGridPlus.DrawColumnCell(const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
P:array [0..50] of char;
BS:tBlobStream;
S:String;
begin
if Column.Field is TMemoField then begin
with Self.Canvas do begin
BS:=tBlobStream.Create(TMemoField(Column.Field), bmRead);
FillChar(P,SizeOf(P),#0); {termina la cadena nula}
BS.Read(P, 50); {Lee 50 caracteres del memo dentro del blobStream}
BS.Free;
S:=StrPas(P);
while Pos(#13, S) > 0 do {remueve caracteres retornos de carro y nueva linea}
S[Pos(#13, S)]:=' '; {Retorno de carro}
while Pos(#10, S) > 0 do
S[Pos(#10, S)]:=' '; {Nueva linea}
FillRect(Rect); {Limpia la celda}
TextOut(Rect.Left, Rect.Top, S); {Llena celda con dato memo}
end;
end;
inherited DrawColumnCell(Rect, DataCol, Column, State);
end;


end.


Gracias a mis asesores estrellas y al sitio de donde saque el código para ver el memo. Todos aportaron un poco, asi que eso se llama trabajar en en equipo, ;)