PDA

Ver la Versión Completa : ¿Cómo publicar DBGridColumns?


DarKraZY
20-02-2007, 14:53:13
Buenas a todos!

Estoy realizando un componente que sirve para lanzar un formulario de búsqueda. Para simplificar el código voy a poner un ejemplo:


{ Este formulario además de otras cosas contiene un DBGrid }
TfrmSeleccion = class(TForm)
dbgDatos: TDBGrid;
end;

{ El componente a utilizar }
TSeleccion = class(TComponent)
private
FForm: TfrmSeleccion;
public
constructor Create(AOwner: TComponent); override;
published
property Columns: TDBGridColumns read FColumns write FColumns stored False;
end;

En el Create del componente hago lo siguiente
inherited Create(AOwner);

{ Creación del formulario de búsqueda }
FForm := TfrmSeleccion.Create(nil);
FColumns := FForm.dbgDatos.Columns;


Pero esto no funciona, ya que al asignar Fields a las Columns o borrarlas Delphi suelta excepciones.

¿Hay otras formas de hacerlo? ¿O es esta la correcta y estoy haciendo otras cosas mal?

El objetivo del componente es poder configurar las columns en tiempo de diseño. Lo que me interesaría es como poder referenciar o tener acceso desde un componente a la propiedad(TDBGridColumns) de un objeto contenido dentro del componente.

waly2k1
20-02-2007, 16:58:08
Que hay otra forma, seguro hay, y muchas supongo, si bien no es un componente lo mio, hice un form de búsqueda y redefino todas las columnas con la cantidad de campos de una consulta especifica. Mediante Clear, y Add de cada col

Levanto una consulta SQL pasada por parametro, cuando llamo al form, y éste genera nuevamente los campos y asigna la consulta como datasource.
si mal lo recuerdo a cada column de la grid le digo que el datafield := Query.Datafield, errores nada.

Estoy en el trabajo, y acá no existe Delphi, sino te mostraba el ejemplo

Lo único que se me complica es que no puedo pasar un array con los headers de las columnas.

Lepe
20-02-2007, 16:58:24
Pero eso te obliga a crear una ventana de Selección para cada búsqueda que tienes que hacer, una para clientes, otra para proveedores.... ¿no sería mejor poder usar la misma ventana para todo tipo de búsquedas (http://www.clubdelphi.com/foros/showpost.php?p=176444&postcount=2)?

Así no pierdes tiempo en diseño, además, los datos que tu tienes de prueba en tu programa, seguro que difiere de los datos reales que haya, y por otra parte, al usuario quizás le guste usar otros anchos de columnas.

saludos

waly2k1
21-02-2007, 16:26:05
Adjunto un form de búsqueda, a modo ejemplo creo sirve, igualmente estoy modificándolo todavía, de acuerdo al tipo de campos. Por ej. con fechas genera un error si la fecha tipeada no es una fecha válida.

A todos: Los que puedan mejorarlo, bienvenido sea!!!. Si quieren hacer un componente o lo que fuera, me resultaría muy útil.

En el attach está el form y un .txt la forma de invocarlo.

Saludos

DarKraZY
22-02-2007, 19:00:53
Perdón por no haber contestado anteriormente, pero he estado liado con otras cosas y dejado de lado este componente.

Actualmente usamos usamos un único formulario para realizar las búsquedas de todo tipo. Ya que creamos procedimientos almacenados con los mismos parámetros de entrada y de características similares.

Y realizamos una llamada con el array de los campos a visualizar y otro array con los Caption a mostrar.

La idea del componente era englobar todo este proceso. Definir en el componente el stored procedure (http://www.clubdelphi.com/foros/showthread.php?t=37971), y ahora solo me faltaba crear dinámicamente las columns o algún proceso similar.

waly2k1
22-02-2007, 20:11:55
Y bueno, lo de las columnas dinámicas lo tenés en el ejemplo.
No te conviene usar directamente un SP en el componente, porque te serviría nada mas para la base de datos en cuestión, que en tu caso creo que es MS-SQL Server, te conviene siempre un string que a su vez puede ser un SP con sus parámetros. por ej: dbo.SP_Clientes_FIND( 'UnCampo', 'OtroCampo' ). Veamos como seguir que la idea está muy buena.

Saludos

DarKraZY
23-02-2007, 10:10:18
Hola waly2k1

La verdad es que utilizo Firebird 1.5. Y este es un poco del código del componente para que te hagas una idea:


{...}
published
property Caption: string read FCaption write SetCaption;
property CharCase: TEditCharCase read FCharCase write SetCharCase default ecUpperCase;
property FormColor: TColor read FFormColor write FFormColor default clBtnFace;
property IBDatabase: TIBDatabase read FIBDatabase write SetIBDatabase;
property IBTransaction: TIBTransaction read FIBTransaction write SetIBTransaction;
property ProcedureName: string read FProcedureName write SetProcedureName;
end;

Como puedes ver ProcedureName es un string pero tengo definida la propiedad de la siguiente manera

TStoredProcedureProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
procedure GetValues(Proc: TGetStrProc); override;
end;

{...}

procedure Register;
begin
RegisterComponents('TabComponente', [TFormSeleccion]);
RegisterPropertyEditor(TypeInfo(string), TFormSeleccion, 'ProcedureName', TStoredProcedureProperty); {do not localize}
end;

function TStoredProcedureProperty.GetAttributes: TPropertyAttributes;
begin
Result := [paValueList, paSortList];
end;

procedure TStoredProcedureProperty.GetValues(Proc: TGetStrProc);
var
StoredProc: TIBStoredProc;
i: Integer;
NamesList: TStrings;
begin
StoredProc := TIBStoredProc.Create(nil);
try
StoredProc.Database := (GetComponent(0) as TFormSeleccion).IBDatabase;
StoredProc.Transaction := (GetComponent(0) as TFormSeleccion).IBTransaction;
NamesList := StoredProc.StoredProcedureNames;
for I := 0 to NamesList.Count - 1 do
Proc(NamesList[i]);
finally
StoredProc.Free;
end;
end;


De esta forma en mi componente aparece una lista desplegable con los procedimientos almacenados.

Por cierto, en tu ejemplo no he visto nada de DBGridColumns dinámicas. Porque me gustaría que estuviesen todas las propiedades de una TColumn.

DarKraZY
23-02-2007, 11:09:44
Ya lo tengo!

El resultado es el siguiente, y a ver si así lo dejo claro ;) (ya que sé que resultaba lioso).

Tengo un formulario. Dentro de este un DBGrid para mostrar los resultados.

La idea era crear un componente que mostrase ese formulario. Y para que el componente fuese muy útil se me ocurrió tener una propiedad de tipo TDBGridColumns.

Hice muchas pruebas y cuando estaba apunto de crear nuevas clases heredadas de TCollection y TCollectionItem me di cuenta de mi error. En el OnDestroy del componente estaba liberando antes el formulario (que contiene el DBGrid) y después las columns!! He ahí mi error. :)

Y ahora algo de código por si sirve a alguien:

TFormSeleccion = class(TComponent)
private
FForm: TfrmSeleccion;
FColumns: TDBGridColumns;
procedure SetColumns(const Value: TDBGridColumns);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Columns: TDBGridColumns read FColumns write SetColumns;
end;

constructor TFormSeleccion.Create(AOwner: TComponent);
begin
inherited Create(AOwner);

{ Creación del formulario de búsqueda }
FForm := TfrmSeleccion.Create(nil);

FColumns := TDBGridColumns.Create(FForm.dbgSeleccion, TColumn);
end;

destructor TFormSeleccion.Destroy;
begin
FColumns.Free;

FForm.Close;
FForm.Free;

inherited;
end;

procedure TFormSeleccion.SetColumns(const Value: TDBGridColumns);
begin
FColumns.Assign(Value);
end;



NOTA: No pongo todo el código para no liar más la cosa, jeje.

DarKraZY
23-02-2007, 12:02:49
Mi gozo en un pozo :( (expresión utilizada después de una alegría, llega una decepción)

Todo el código anterior sigue fallando. Cuando se elimina una Column del Collection salta error de memoria.

Investigando más a fondo la VCL parece ser que TDBGridColumns tiene como Owner al DBGrid (como debe de ser). Y buscando en la unit Classes he encontrado la solución ¡TOwnedCollection!. Que es un descendiente de TCollection pero con Owner explícito en el Create.


Manos a la obra y funcionando (por ahora jeje)

TOwnedDBGridColumns = class(TDBGridColumns)
private
FOwner: TPersistent;
protected
function GetOwner: TPersistent; override;
public
constructor Create(AOwner: TPersistent; Grid: TCustomDBGrid;
ColumnClass: TColumnClass);
end;

constructor TOwnedDBGridColumns.Create(AOwner: TPersistent;
Grid: TCustomDBGrid; ColumnClass: TColumnClass);
begin
inherited Create(Grid, ColumnClass);
FOwner := AOwner;
end;

function TOwnedDBGridColumns.GetOwner: TPersistent;
begin
Result := FOwner;
end;

waly2k1
23-02-2007, 18:13:48
En realidad pensé que lo que necesitabas era armar las columnas en un DBGrid en forma dinámica (campos, titulos, etc.), eso está en el ej. que publiqué. El resto entiendo poco y nada, estoy muy verde en Delphi.

Después presto más atención a tu código, pero no entiendo bien para que creas vos una colección TColumn ???

Saludos