- Crear la aplicación Inversiones (menú File | New | Application) y guardar sus ficheros en un directorio nuevo
- Formulario principal: fichero FormInversiones.cpp
- Proyecto: fichero Inversiones.bpr
- Configurar el formulario principal de la aplicación:
- Name = frmInversiones
- Caption = Gestión de inversiones
- Crear un módulo de datos dmInversiones (menú File | New | Data Module):
- Fichero DataInversiones.cpp.
- Name = dmInversiones.
Insertar dos componentes de tipo TTable (pestaña BDE) y dos de tipo TDataSource (pestaña Data Access):
- Tabla maestro:
- Name = TableClients
- DatabaseName = BCDEMOS
- TableName = clients.dbf
- DataSource maestro:
- Name = dsClients
- DataSet = TableClients
- Tabla detalle:
- Name = TableHoldings
- DatabaseName = BCDEMOS
- TableName = holdings.dbf
- DataSource detalle:
- Name = dsHoldings
- DataSet = TableHoldings
- Añadir controles al formulario principal:
- Botón para terminar (BitBtn): Kind=bkClose.
- TDBNavigator personalizado (pestaña Data Controls): Hints & VisibleButtons.
- 2 componentes de tipo TDBGrid (pestaña Data Controls), a los que llamaremos gridClients y gridHoldings.
- Para poder usar el módulo de datos desde el formulario, hemos de incluir fichero de cabecera correspondiente al módulo de datos, a mano (#include "DataInversiones.h") o utilizando la opción correspondiente del menú (menú File | Include Unit Hdr, Alt+F11).
- Enlazar los controles "data-aware" a los correspondientes componentes de tipo TDataSource (que están en el módulo de datos):
- DBNavigator: DataSource = dmInversiones->dsClients.
- Rejilla "gridClients": DataSource = dmInversiones->dsClients.
- Rejilla "gridHoldings": DataSource = dmInversiones->dsHoldings.
- Como la foto del cliente no podemos verla en la rejilla, añadimos un componente de tipo TDBImage (pestaña Data Controls y lo enlazamos al DataSource correspondiente, de forma que nuestro formulario queda de la siguiente forma:
- Hasta ahora hemos estado trabajando son dos tablas independientes. Para establecer la relación maestro/detalle entre las dos tablas tenemos que hacer lo siguiente:
- Establecer la propiedad MasterSource de la tabla detalle: TableHoldings->MasterSource = dsClients
- Fijar la condición de la reunión a través de la propiedad MasterFields de la tabla detalle:
- Ya sólo nos falta hacer que se visualicen los datos cuando se ejecute la aplicación:
- En tiempo de diseño: Table...->Active = true.
- En tiempo de ejecución: Table...->Open() & Table...->Close() al producirse los eventos adecuados...
- Finalmente, podemos compilar nuestro proyecto para obtener la siguiente aplicación:
Posibles mejoras
- Personalizar la presentación de las rejillas: columnas visibles, encabezados, colores, justificación, etc..
- Facilitar la introducción de datos mediante el uso de campos de tipo "lookup" (el símbolo SYMBOL hace referencia a una empresa de las que figuran en la tabla master.dbf):
De esta forma conseguimos que el usuario no tenga que saberse de memoria los códigos de las empresas que cotizan en bolsa para poder utilizar nuestra aplicación (un fallo más común en la realidad de lo que gustaría pensar).
Manipulación de conjuntos de datos
Calcular y mostrar, bajo demanda, el número de acciones y el importe total de las inversiones del cliente actual:
Pasos a seguir:
- Insertar un botón (Name = buttonResumen, Caption = Resumen)
- Insertar 6 etiquetas con los valores siguientes para las propiedades (Name,Caption): (Label1, Cliente:), (Label2, Acciones:), (Label3, Importe:), (LabelNombre,-vacío-), (LabelAccsiones,-vacío-), (LabelImporte,-vacío-).
- Insertar un control de tipo Bevel que enmarque a todas las etiquetas anteriores.
- Implementar el gestor del evento que se desencadena al pulsar sobre el botón "buttonResumen":
Evento correspondiente a la pulsación del botón void __fastcall TfrmInversiones::buttonResumenClick(TObject *Sender) { int acciones; float importe; dmInversiones->CalcularResumen (acciones, importe); Label1->Visible = true; Label2->Visible = true; Label3->Visible = true; LabelNombre->Caption = dmInversiones->TableClients->FieldByName("FIRST_NAME")->AsString + " " + dmInversiones->TableClients->FieldByName("LAST_NAME")->AsString; LabelAcciones->Caption = IntToStr(acciones); LabelImporte->Caption = FloatToStr(importe); }
donde CalcularTotal() lo definimos como un método de la clase que define el módulo de datos:
Recorrido secuencial de un conjunto de datos void TdmInversiones::CalcularResumen (int &acciones, float &importe) { acciones = 0; importe = 0; TableHoldings->First(); while (!TableHoldings->Eof) { acciones += TableHoldings->FieldByName("SHARES")->AsInteger; importe += TableHoldings->FieldByName("SHARES")->AsInteger * TableHoldings->FieldByName("PUR_PRICE")->AsFloat; TableHoldings->Next(); } }Posibles mejoras
- Conforme se está realizando un cálculo, la pantalla parpadea conforme recorremos el conjunto de datos. Por este motivo es aconsejable utilizar los métodos DisableControls & EnableControls del conjunto de datos que estemos manipulando.
- Así mismo, es aconsejable deshabilitar el botón que da lugar a la realización de una tarea costosa computacionalmente (usemos o no usemos hebras): propiedad Enabled.
- Al cambiar de celda en la rejilla de clientes deberá borrarse el resumen para no dar lugar a confusiones. Buscar cómo hacerlo desde el propio formulario.
- Una posible solución al problema anterior es convertir la información que obtenemos manualmente en campos calculados del conjunto de datos. Dichos campos se calculan automáticamente para cada registro y podemos acceder a ellos como a cualquier otro campo del conjunto de datos (algo especialmente útil si empleamos controles "data-aware"):
Evento OnCalcFields void __fastcall TdmInversiones::TableClientsCalcFields(TDataSet *DataSet) { QueryResumen->ParamByName("ACCT_NBR")->Value = TableClientsACCT_NBR->Value; QueryResumen->Open(); TableClientsACCIONES->AsInteger = QueryResumen->Fields->FieldByNumber(1)->AsInteger; TableClientsIMPORTE->AsFloat = QueryResumen->Fields->FieldByNumber(2)->AsFloat; QueryResumen->Close(); TableClientsNOMBRE->AsString = TableClientsFIRST_NAME->AsString.Trim() + " " + TableClientsLAST_NAME->AsString.Trim(); }
donde QueryResumen es una consulta SQL auxiliar que utilizamos para calcular los totales en función del número de cuenta del cliente:
Consulta SQL auxiliar select sum(shares), sum(shares*pur_price) from holdings.dbf where acct_nbr=:acct_nbrDe esta forma el usuario no tiene que hacer nada para que la aplicación muestre toda la información:
- Código fuente -