Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Añadir propiedades a TField (https://www.clubdelphi.com/foros/showthread.php?t=4065)

altp 30-09-2003 20:38:58

Añadir propiedades a TField
 
Estoy intentando añadir propiedades a TField pero me da error.

He hecho lo siguiente y me da error:

type
TmyField = class(TField)
private
FGrid: String;
protected
procedure setGrid(const value : string);
public
constructor Create(AOwner: TComponent); override;
published
property Grid: string read FGrid write SetGrid;
end;

implementation

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

procedure TmyField.setGrid(const value : string);
begin
FGrid := Value;
end;

Me da error al asignar la variable value a FGrid.

Alguien me puede ayudar? Gracias de antemano

roman 30-09-2003 20:46:08

Probé tu código y no me marca ningún error.

¿Cuál es el error que te marca?

// Saludos

altp 30-09-2003 21:20:02

Yo tengo una aplicación en la cual me crea los campos en tiempo de ejecución y le quiero añadir una propieda para que me diga si el campo lo muestro en el grid o no. Yo pongo '0' para no y '1' para que sí lo muestre.

Tengo este código que es el que me crea el campo y el error me lo da al asignar un valor a esa nueva propiedad.

var
Field : TmyField;
begin
Field := TmyField.Create(Nil);
TField(Field) := TStringField.Create(Nil);
Field.FieldKind := (fkData);
Field.FieldName := 'NOMBRE';
···

/* Aquí es donde me dá el error */
Field.Grid := 1;

Field.DataSet := Tabla;
end;


Gracias.

roman 30-09-2003 21:24:53

Aún no nos dices cuál es el error

A juzgar por el código que pones tendría que ser un error durante la compilación ya que intentas asignar a Field.Grid un entero siendo que Field.Grid es de tipo String.

Cita:

Posteado originalmente por altp

/* Aquí es donde me dá el error */
Field.Grid := 1;

Si las comillas te faltaron por un error al pegar el código aquí entonces el error será otro pero ayudaría saber cuál es.

// Saludos

altp 30-09-2003 21:31:09

Efectivamente

Field.Grid := '1';

y el error que me da es

Access violation at address 0040463E in module 'nombre_programa'. Read of address 000000F8.

No se si te servirá de algo.

Gracias

roman 30-09-2003 21:38:38

Hey! No me había dado cuenta de algo, en tu código pones

Código:

Field := TmyField.Create(Nil);
TField(Field) := TStringField.Create(Nil);
Field.FieldKind := (fkData);
Field.FieldName := 'NOMBRE';

Esto no está bien. En la primera línea creas un objeto TMyField y en la segunda vuelves a asignar a Field otro objeto (ahora un TStringField).

No sé qué intentabas hacer pero la primera referencia (a TMyField) se pierde de manera que Field, aunque esté declarado como TMyField es un TStringField, que no tiene la propiedad Grid, de ahí que al tratar de usar Field.Grid se intente referenciar una propiedad inexistente y por tanto se genera el "Access Violation"

Si necesitas un StringField quizá te convenga más derivar tu clase de TStringField:

Código:

TMyField = class(TStringField)
  ...
end;

// Saludos

altp 30-09-2003 21:46:19

Es cierto no me había dado cuenta yo tampoco de que me creaba 2 veces el Field.

Pero ahora mi pregunta es la siguiente, ¿cómo puedo hacer que redeclarándome en un solo objeto de tipo TField pueda crear campos TStringField, TIntegerField, TMemoField, etc?

Si me respondes sería fantástico.

Gracias otra vez.

roman 30-09-2003 22:11:49

Cita:

Posteado originalmente por altp
...¿cómo puedo hacer que redeclarándome en un solo objeto de tipo TField pueda crear campos TStringField, TIntegerField, TMemoField, etc?

No creo que esto sea posible ya que es como añadir propiedades a clases que ya existen.

Sin embargo, para tu caso particular, si lo único que requieres es una propiedad que te indice si se muestra o no en un Grid, puedes usar la propiedad Tag.

Todos los descendientes de TComponent, que incluye a TField y todos sus descendientes, tienen una propiedad Tag que Delphi no usa, es para que el programador la use como desee. Es de tipo LongInt de manera que basta que veas si su valor es 1 ó 0 para saber si lo muestras o no en el Grid.

// Saludos

altp 01-10-2003 12:28:59

Buenas de nuevo roman.

La propiedad Tag no me sirve puesto que no solo quiero añadir una propiedad a TField sino que quiero añadir unas cuantas.

Estuve pensando y creo que lo que busco o no se puede o es muy dificil.

He encontrado otra posible solución que sería la siguiente:

Field := TmyField.Create(Nil);
Field.SetDataType(ftString); /* o cualquier otro tipo */

el problema que ahora me surge es que no le puedo dar un tamaño a ese campo que me creo.

Field.Size := 5; /* da error */

también he probado con otras dos posiblidades:

Field.SetSize(5);
Field.CheckTypeSize(5);

pero también me fallan.

Si tienes alguna solución a mi problema encantado de poder realizarlo.

Gracias.

andres1569 01-10-2003 13:04:00

Hola:

Creo que para lo que pretendes hacer, no es buena idea heredar nuevas clases puesto que esos objetos TField te los crea Delphi por defecto y por muchos casts que hagas a tus clases definidas, las propiedades que hay son las que hay.

Puedes utilizar la propiedad tag que te comenta Román de dos formas para lo que pretendes. En primer lugar te defines un tipo record que almacenará las variables (aquí no le llamo propiedades) que quieres asociar a cada TField. Luego te propongo dos ideas:

1) Defines un tipo puntero que apunte a dicho record, y aprovechas la propiedad tag, que es de tipo integer y ocupa 4 bytes, lo mismo que cualquier puntero, para almacenar una referencia a dicho puntero (haciendo un moldeado de tipo de Integer a Pointer para que el compilador dé su visto bueno). De hechjo, es una de las funcionalidades que se indican en la ayuda de Delphi para la propiedad tag:

Código:

PInfoField = ^TInfoField;
TInfoField = record
  EnGrid : Boolean;
  EnVerde : Boolean;
  NumColumna : Integer;
  ...
  end;

// Para asociar esa información a un objeto TField

procedure AsiociaInfo (AField: TField; EG, EV: Boolean; NC: Integer);
var
  PInfo : PInfoField;
begin
  GetMem (PInfo, SizeOf(TInfoField));
  PInfo^.EnGrid := EG;
  PInfo^.EnVerde := EV;
  PInfo^.NumColumna := NC;
  ...
  AField.Tag := Integer(PInfo);
end;

procedure MuestraDatos (AField: TField);
var
  P : PInfoField;
begin
  P := PInfoField(AField.Tag);
  if P.EnGrid then ShowMessage ('Engrid');
  if P.EnVerde then ShowMessage ('EnVerde');
  ShowMessage (IntToStr(P.NumColumna));
end;

// Acordarse de liberar memoria de estos registros al cerrar aplicación
procedure LiberarMemoriaFields;
var
  j : Integer;
  P : PInfoField;
begin
  for j:=0 to DataSet.FieldCount - 1 do
    // esta es una manera algo burda de comprobar que hay Info asociada
    if DataSet.Fields[j].Tag <> 0 then
    begin
        P := PInfoField(DataSet.Fields[j].Tag);
        FreeMem (P, SizeOf(TInfoField));
    end;
end;

2) Otra forma de hacer todo esto sería, usando el mismo tipo record que antes, crear un array dinámico que almacene todas las infos, y la propiedad tag indicaría el índice a la info correspondiente en dicho array, de esta forma te ahorras trabajar con punteros y cuando terminas con todo es Delphi mismo quien se ocupa de liberar el array, no tienes que hacerlo tú a mano. Aquí te pongo el código:

Código:

TInfoField = record
  EnGrid : Boolean;
  EnVerde : Boolean;
  NumColumna : Integer;
  ...
  end;

var
  ListInfo : Array of TInfoField;

// Para asociar esa información a un objeto TField
procedure AsiociaInfo (AField: TField; EG, EV: Boolean; NC: Integer);
begin
  SetLength (ListInfo, Length(ListInfo) + 1);
  with ListInfo[High(ListInfo)] do
  begin
    EnGrid := EG;
    EnVerde := EV;
    NumColumna := NC;
    ...
  end;
  AField.Tag := High(ListInfo);
end;

procedure MuestraDatos (AField: TField);
var
  P : TInfoField;
begin
  P := ListInfo[AField.Tag];
  if P.EnGrid then ShowMessage ('Engrid');
  if P.EnVerde then ShowMessage ('EnVerde');
  ShowMessage (IntToStr(P.NumColumna));
end;

En esta segunda opción hay que tener en cuenta que todos los tags valen por defecto 0, que equivaldría al primer elemento de la lista.

Suerte

roman 01-10-2003 16:40:48

¡Ah! Esto es excelente andre1569, yo ya nada más añadiría que una tercera opción sería usar Tag para apuntar a un objeto:
Código:

type
  TMyFieldData = class
    { propiedades extra }
  end;

  ...

  { Crear los campos }
  Field := TStringField.Create(Self);
  MyFieldData := TMyFieldData.Create;
  MyFieldData.Propiedad := valor;

  Field.Tag := LongInt(MyFieldData);

  ...

  { Usar las propiedades }
  Valor := TMyData(Field.Tag).Propiedad;

  ...

  { Liberar objetos }
  TMyFieldData(Field.Tag).Free;

// Saludos

altp 01-10-2003 18:19:30

Gracias a los 2 por responderme.

Me han gustado mucho las tres propuestas, pero la que mejor me ha parecido es la de roman, ya la estoy poniendo en práctica y va genial.

Gracias a los dos.

Si encontrais una solución más eficiente no estaría de más ponerla.


Saludos

Al González 03-10-2003 04:06:37

¡Buen día a todos!

Acabo de leer todos los mensajes de este interesante tema planteado atinadamente por Altp.

Esta misma pregunta:

Cita:

...¿cómo puedo hacer que redeclarándome en un solo objeto de tipo TField pueda crear campos TStringField, TIntegerField, TMemoField...
me la hice yo hace tiempo, cuando buscaba definir nuevas clases de componentes campo a partir de las clases existentes (TStringField, TIntegerField, TCurrencyField, etc.), con propiedades y métodos que tenían practicamente la misma implementación en todas esas clases nuevas.

En aquel entonces me dije: "Debe haber una forma de aplicar esto en TField y que repercuta automáticamente en todos sus descendientes..."

Pues hasta ahora creo que tuve razón, porque siento que "debe haber una forma", sólo que todavía el lenguaje Object Pascal de Delphi no la tiene.

Para mi fue como descubrir ese enorme hueco que tiene la tabla períodica de los elementos químicos.

A esta característica inexistente hasta hoy en día (2 de octubre de 2003) le llamo herencia virtual. Y según el ejemplo que plantea en este caso, pienso que podría aplicarse de la siguiente forma (entre otras posibles):

Código:

Type
  Complement Class TField
    Private
      FGrid :String;
    Protected
      Procedure SetGrid (Const Value :String);
    Public
      Constructor Create (AOwner :TComponent); Override;
    Published
      Property Grid :String Read FGrid Write SetGrid;
  End;
...

A partir de esta declaración, el compilador tendría que asegurarse de que todas las clases descendientes del anterior TField, cumplan con la herencia de su nuevo padre: la clase TField complementada, la nueva TField.

Creo que esta característica elevaría significativamente la potencia del lenguaje Object Pascal (que ya de por si es potente). Estoy seguro de que ya se ha considerado por parte de los ingenieros de Borland.

Por lo pronto, a buscar otras alternativas para estos casos.

Espero esto sea de utilidad, seguimos en contacto.

Al González :).


La franja horaria es GMT +2. Ahora son las 14:42:43.

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