Curso de C++ Builder


Acceso a bases de datos

Controles de acceso a datos



Todos ellos tienen un control homólogo estándar en la paleta de componentes. Se diferencian de los controles estándar en que están enlazados a datos (son componentes data-aware). Dicho enlace se establece mediante un DataSource.

Estos componentes trabajan con los datos seleccionados por las propiedades DataSource y, en su caso, DataField:

Propiedad Descripción
DataSource Indica el datasource del que se obtienen y al que se envían los datos.
DataField Es el campo o columna del datasource al que accede cada control.

A continuación, presentaremos los distintos controles de datos del C++ Builder

Controles orientados a conjuntos de datos
Control Descripción
TDBGrid Rejilla de datos: Muestra un DataSource con aspecto de tabla. Mediante la propiedad Columns se pueden especificar qué campos del conjunto de datos se pueden ver en la rejilla y cuáles son sus propiedades.
TDBCtrlGrid Rejilla que permite incluir controles: Como un TDBGrid, aunque cada fila es en realidad un panel en el que se pueden colocar controles de datos "replicables".
TDBNavigator Control de navegación: Sirve para controlar el cursor, así como la inserción, edición y borrado de datos. La propiedad VisibleButtons se utiliza para seleccionar qué botones podrá pulsar el usuario. Por su parte, la propiedad Hints permite especificar mensajes de ayuda para cada botón (que habrá que traducir).
TDBChart Permite construir gráficos a partir de un conjunto de datos. La propiedad SeriesList se emplea para seleccionar qué datos deseamos representar y cómo
 
Controles orientados a campos
Control Descripción
TDBText Como el componente estándar TLabel, pero enlazado a datos. Muestra el valor de un campo, el cual no se puede editar. Igual que las etiquetas normales, no consume recursos en Windows.
TDBEdit Presenta un cuadro de edición para un campo concreto. Análogo a TEdit.
TDBMemo Caja de edición con múltiples líneas. Ideal para textos sin formato.
TDBCheckBox Se emplea en campos que sólo permiten dos valores diferentes. Con las propiedades ValueChecked y ValueUnchecked se establecen listas de valores para que el TDBCheckBox esté activado o desactivado. Dichas listas se especifican mediante una serie de valores separados por punto y coma.
TDBRadioGroup Se utiliza para campos que sólo permiten varios valores. En la propiedad Items se fijan los identificadores que el usuario ve en el grupo de botones. En la propiedad Values se indican los valores que en realidad se almacenan en la base de datos.
TDBListBox Establece una lista de posibles valores para un campo. En la propiedad Items se fijan los valores permitidos, que son fijos.
TDBComboBox TComboBox enlazado a datos. Igual que en el control TDBListBox, las distintas posibilidades se colocan en la propiedad Items.
TDBLookupListBox Ideal para claves externas: Como TDBListBox, aunque la lista de valores permitidos no la establece el programador. Los valores de la lista se obtienen a partir de los valores de un campo de otro conjunto de datos. El DataSource que se consulta se establece con la propiedad ListSource. El campo que se muestra en la lista de valores viene dado por la propiedad ListField. La propiedad KeyField indica el campo cuyos valores se almacenan en la base de datos.
TDBLookupComboBox Igual que TDBLookupListBox, aunque la lista aparece como un TComboBox.
TDBRichEdit Igual que TDBMemo, con la capacidad adicional de dar formato al texto del campo (en formato RTF).
TDBImage Para mostrar imágenes contenidas en una base de datos (en formato BMP o WMF).

El comportamiento de todos los controles anteriores ante modificaciones se puede alterar con las propiedades AutoEdit y ReadOnly.

Bases de datos con fechas

Se puede adaptar TDateTimePicker (página Win32 de la paleta de componentes) para que funcione con un TDataSource especificando sus eventos OnStateChange, OnDataChange y OnUpdateData. También es necesario personalizar el evento OnExit del componente TDateTimePicker.

Ejemplo
class TFormDate : public TForm
{
  ...
  private:
    bool FCambiando;
  ...
};

void __fastcall TFormDate::DataSourceDataChange(TObject *Sender, TField *Field)
{
  if (! FCambiando)
     try {
       FCambiando = True;
       DateTimePicker->DateTime = Table->FieldValues["HireDate"];
     } __finally {
       FCambiando = False;
     }
}

void __fastcall TFormDate::DataSourceUpdateData(TObject *Sender)
{
  Table->FieldValues["HireDate"] = DateTimePicker->DateTime;
}


void __fastcall TFormDate::DateTimePickerChange(TObject *Sender)
{
  if (! FCambiando)
     try {
       FCambiando = True;
       DataSource->Edit();
     } __finally {
       FCambiando = False;
     }
}

void __fastcall TFormDate::DateTimePickerExit(TObject *Sender)
{
  Table->UpdateRecord();
}

Uso de rejillas

La tecla 'Insert' permite añadir tuplas, mientras que la combinación 'Control+Supr' se utiliza para eliminarlas. Por defectro, aparece un mensaje en inglés que se puede personalizar interceptando el evento BeforeDelete del conjunto de datos asociado:

Ejemplo de personalización de los mensajes mostrados al usuario
void __fastcall ...::TableBeforeDelete(TDataSet *DataSet)
{
  if ( MessageDlg ( "¿Desea eliminar la tupla actual?",
                    mtConfirmation, 
                    TMsgDlgButtons()<<mbYes<<mbNo, 0) != mrYes )
     Abort();
}

Interceptando el evento OnDrawColumnCell se puede personalizar la presentación de las celdas: cambiar el color, el tipo de letra e incluso mostrar imágenes o checkboxes sin tener que recurrir al componente TDBCtrlGrid.

Personalización del color de un campo determinado (según el cumplimiento de alguna condición)
void __fastcall TFormXXX::DBGridDrawColumnCell
    (TObject *Sender, const TRect Rect, int DataCol, TColumn *TColumn, TGridDrawState State)
{
  TDBGrid *grid = static_cast<TDBGrid*>(Sender);

  if (tbParts->FieldValues["OnOrder"] >= tbParts->FieldValues["OnHand"])
     grid->Canvas->Font->Color = clRed;

  grid->DefaultDrawColumnCell(Rect, DataCol, Column, State);
}
 
Propiedades del tipo de letra
void __fastcall TFormXXX::DBGridDrawColumnCell
    (TObject *Sender, const TRect& Rect, int DataCol, TColumn *Column, TGridDrawState State)
{
  TDBGrid *grid = static_cast<TDBGrid*>(Sender);
  TFontStyles FS;

  switch (grid->DataSource->DataSet->UpdateStatus()) {
    case usModified: FS << fsBold; break;
    case usInserted: FS << fsItalic; break;
    case usDeleted: FS << fsStrikeOut; break;
  }

  grid->Canvas->Font->Style = FS;
  grid->DefaultDrawColumnCell(Rect, DataCol, Column, State);
}
 
Imágenes en vez de texto
void __fastcall TFormXXX::DBGridDrawColumnCell
    (TObject *Sender, const TRect Rect, int DataCol, TColumn *TColumn, TGridDrawState State)
{
  TDBGrid *grid = static_cast<TDBGrid*>(Sender);

  if (Column->FieldName != "")
     grid->DefaultDrawColumnCell(Rect, DataCol, Column, State);
  else if (tbEmpleados->FieldValues["Salary"] >= 150000)
     grid->Canvas->StretchDraw(Rect, CaraAlegre->Picture->Graphic);
  else
     grid->Canvas->StretchDraw(Rect, CaraTriste->Picture->Graphic);
}
 
CheckBoxes (aunque es más cómodo y flexible utilizar TDBCtrlGrid)
void __fastcall TFormXXX::DBGridDrawColumnCell
    (TObject *Sender, const TRect Rect, int DataCol, TColumn *TColumn, TGridDrawState State)
{
  if (CompareText(Column->FieldName, "ACTIVO") == 0) {
     UINT check = 0;
 
     if (Table1->FieldValues["ACTIVO"])
        check = DFCS_CHECKED;
 
     DBGrid->Canvas->FillRect(Rect);
     DrawFrameControl(DBGrid1->Canvas->Handle, (RECT*) &Rect, DFC_BUTTON, DFCS_BUTTONCHECK | check);

  } else 
     DBGrid->DefaultDrawColumnCell(Rect, DataCol, Column, State);
}

Podemos seleccionar la columna por la que se ordenan los datos usando la propiedad IndexFieldNames del conjunto de datos. Esta propiedad la podemos establecer dinámicamente como respuesta al evento OnTitleClick.

Ordenación dinámica de un conjunto de datos
void __fastcall TFormXXX::DBGridTitleClick(TColumn *Column)
{
  try {
    if (Column->Field->FieldKind == fkLookup)
       Table->IndexFieldNames = Column->Field->KeyFields;
    else
       Table->IndexFieldNames = Column->FieldName;
  } catch(Exception&) {
  }
}

Pistas y trucos



Índice de la sección