Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 30-09-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
Question 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
Responder Con Cita
  #2  
Antiguo 30-09-2003
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Probé tu código y no me marca ningún error.

¿Cuál es el error que te marca?

// Saludos
Responder Con Cita
  #3  
Antiguo 30-09-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
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.
Responder Con Cita
  #4  
Antiguo 30-09-2003
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
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
Responder Con Cita
  #5  
Antiguo 30-09-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
Question

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
Responder Con Cita
  #6  
Antiguo 30-09-2003
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
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
Responder Con Cita
  #7  
Antiguo 30-09-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
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.
Responder Con Cita
  #8  
Antiguo 30-09-2003
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
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
Responder Con Cita
  #9  
Antiguo 01-10-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
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.
Responder Con Cita
  #10  
Antiguo 01-10-2003
andres1569 andres1569 is offline
Miembro
 
Registrado: may 2003
Posts: 908
Poder: 22
andres1569 Va por buen camino
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
__________________
Guía de Estilo

Última edición por andres1569 fecha: 01-10-2003 a las 17:02:21.
Responder Con Cita
  #11  
Antiguo 01-10-2003
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
¡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
Responder Con Cita
  #12  
Antiguo 01-10-2003
altp altp is offline
Miembro
 
Registrado: sep 2003
Posts: 131
Poder: 21
altp Va por buen camino
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
Responder Con Cita
  #13  
Antiguo 03-10-2003
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
¡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 .
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 00:37:38.


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
Copyright 1996-2007 Club Delphi