www.delphientwickler.de

Du bist Delphientwickler und suchst nach Projekten?

 

Sie suchen eine Firma zur Unterstützung oder Betreuung Ihres Delphi Projektes?
Klicken Sie hier!

Nils Eilers, 09.05.2017

Grid Spaltenbreite

Wie in der kurzen Beschreibung vom 05.05.2017 geht es in diesem Artikel auch um die Ergänzung von Delphis DBGrid. Eines der Features, die man bei der Nutzung des DBGrids vermissen kann, ist die automatische Anpassung der Spaltenbreiten auf Knopfdruck. In diesem Beispiel wird eine Lösungsvariante für diese Aufgabenstellung vorgestellt und demonstriert, wie der entsprechende Code mit dem aus „Spaltensortierung mit DBGrid“ gemeinsam ohne Konflikte genutzt werden kann.

Wie für die Sortierfunktionalität gilt auch hier, dass der folgende Weg nur eine von vielen Möglichkeiten darstellt und je nach Anforderungen die Lösungen anders aussehen können. Das folgende Beispiel ist mit Delphi XE5 geschrieben worden und nutzen die FireDAC-Komponenten.

Folgende Komponenten wurden hier auf dem Formular platziert:

  • TDBGrid
  • TDBNavigator
  • TFDConnector
  • TDataSource
  • TFDQuery
  • TFDGUIxWaitCursor und TFDPhysMySQLDriverLink

TFDGUIxWaitCursor und TFDPhysMySQLDriverLink sind hier für FireDAC notwendig, da eine MySQL-Datenbank genutzt wird und können für alles Weitere ignoriert werden.

In der TFDConnection-Komponente wird die Verbindung zum Server eingerichtet. Das TDBGrid und der TDBNavigator werden mit der TDatasource und diese mit der TFDQuery verknüpft, die wiederum mit der TFDConnection verbunden ist.

Die TFDQuery enthält ein sehr schlichtes Statement für den Zugriff auf die Testdatenbank:

SELECT *
FROM Artikel

 

Um eine Spalte automatisch an die breite seiner breitesten Zelle anzupassen, wird das OnTitleClick-Event des DBGrids genutzt. Der Code, der bei dem Event ausgeführt wird, kann nun so aussehen:

procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
  i: integer;
  TmpRecNo: Integer;
  NewColumnWidth: Integer;
  ColumnTitleWidth: Integer;
  ColumnCellWidth: Integer;
  ColTitleAdd: Integer;
  ColCellAdd: Integer;
  ColTitleMult: Real;
  ColCellMult: Real;
begin
  //Festlegen der Modifikatoren für zusätzliche Anpassung der Spaltenbreite
  //Spaltenbreite = Round(Basiswert * Multiplikator) + Summand
  ColTitleAdd := 0;
  ColTitleMult := 1.10;
  ColCellAdd := 0;
  ColCellMult := 1.10;

  //Anpassung der Spaltenbreite
  ColumnTitleWidth := Round(DBGrid1.Canvas.TextExtent(Column.Title.Caption).cx * ColTitleMult) + ColTitleAdd;
  ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;
* ColCellMult) + ColCellAdd;

  //Die breiteste Zelle finden
  TmpRecNo := FDQuery1.RecNo;
  FDQuery1.First;
  FDQuery1.DisableControls;
  For i := 0 to FDQuery1.RecordCount - 1 do
  begin
    If ColumnCellWidth < Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColTitleMult) + ColTitleAdd then
      ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;
    FDQuery1.Next;
  end;
  FDQuery1.RecNo := TmpRecNo;
  FDQuery1.EnableControls;

  //Falls der Titel breiter ist, als die breiteste Zeile, an diesem orientieren
  If ColumnTitleWidth > ColumnCellWidth then
    NewColumnWidth := ColumnTitleWidth
  Else
    NewColumnWidth := ColumnCellWidth;

  Column.Width := NewColumnWidth;
end;


Mit dieser Einrichtung des Codes wird er nun bei einem einfachen Mausklick auf einem Titelfeld des DBGrids ausgeführt. Damit dieser allerdings nur bei einem Doppelklick ausgelöst wird und kompatibel mit dem Sortiercode aus dem Artikel „Spaltensortierung im DBGrid“ ist, müssen noch ein paar Ergänzungen vorgenommen werden.

Wiese man einfach den Code für die Spaltenbreite dem OnDblClick-Event des DBGrids zu, würde mit jedem Doppelklick auch das OnTitleClick-Event, also der Sortiercode, zweimal ausgeführt werden. Um dieses Problem zu lösen, wird für beide Funktionalitäten das OnTitle-Click-Event ausgewählt und ein Doppelklick unabhängig von den Vorgaben des DBGrids definiert.

Um das zu erreichen wird zusätzlich eine TTimer-Komponente auf dem Formular benötigt. Außerdem müssen zwei globale Variablen definiert werden, die für die Übergaben zwischen der OnTitleClick- und der Timer-Prozedur erforderlich sind. In diesem Beispiel sehen diese Variablen so aus:

private
   SingleClick: boolean = true;
   SelectedColumn: TColumn;

Objektinspector

 

 

 

Ist der TTimer auf dem Formular platziert, muss er mit dem Umstellen seiner Eigenschaft „Enabled“ auf „False“ deaktiviert werden. Mit Hilfe seiner „Interval“-Eigenschaft kann gesteuert werden, wie schnell geklickt werden muss, damit das Programm einen Doppelklick registriert. In diesem Beispiel wird ein Interval von 200 gewählt. Nun müssen dem OnTimer-Event noch folgende Zeilen hinzugefügt werden:

 

procedure TForm1.Timer1Timer(Sender: TObject);
begin
SingleClick := true;
Timer1.Enabled := false;
DBGrid1TitleClick(SelectedColumn);
end;


Der kombinierte Code aus dem Artikel vom 05.05.2017 und der Spaltensortierung mit Berücksichtigung der Doppelklickproblematik kann nun so aussehen:

procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
   OldOrderString: String;
   NewOrderString: String;
   i: integer;
   TmpRecNo: Integer;
   NewColumnWidth: Integer;
   ColumnTitleWidth: Integer;
   ColumnCellWidth: Integer;
   ColTitleAdd: Integer;
   ColCellAdd: Integer;
   ColTitleMult: Real;
   ColCellMult: Real;
begin
   //Ausgewählte Spalte für den Timer speichern
   SelectedColumn := Column;

   //-- Spaltenbreite

//Der Timer erkennt den Doppelklick
If Timer1.Enabled = true then
begin
   //Festlegen der Modifikatoren für zusätzliche Anpassung der Spaltenbreite
   //Spaltenbreite = Round(Basiswert * Multiplikator) + Summand
   ColTitleAdd := 0;
   ColTitleMult := 1.10;
   ColCellAdd := 0;
   ColCellMult := 1.10;

   //Anpassung der Spaltenbreite
   ColumnTitleWidth := Round(DBGrid1.Canvas.TextExtent(Column.Title.Caption).cx * ColTitleMult) + ColTitleAdd;
   ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;

   //Die breiteste Zelle finden
   TmpRecNo := FDQuery1.RecNo;
   FDQuery1.First;
   FDQuery1.DisableControls;
   For i := 0 to FDQuery1.RecordCount - 1 do
   begin
       If ColumnCellWidth < Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColTitleMult) + ColTitleAdd then
          ColumnCellWidth := Round(DBGrid1.Canvas.TextExtent(Column.Field.DisplayText).cx * ColCellMult) + ColCellAdd;
       FDQuery1.Next;
       end;
       FDQuery1.RecNo := TmpRecNo;
       FDQuery1.EnableControls;

       //Falls der Titel breiter ist, als die breiteste Zeile, an diesem orientieren
       If ColumnTitleWidth > ColumnCellWidth then
          NewColumnWidth := ColumnTitleWidth
       Else
          NewColumnWidth := ColumnCellWidth;

          Column.Width := NewColumnWidth;

       Timer1.Enabled := false;
end
Else If SingleClick = false then
   Timer1.Enabled := true;
//-- Sortierung
If SingleClick = true then
begin
   //Speichern der aktuell ausgewählten Zeile
   //Nach dem Reaktivieren der Query wird sonst immer die erste Zeile markiert
   TmpRecNo := FDQuery1.RecNo;
   
   //Prüfung, ob bereits eine Sortierung vorhanden ist
   If pos('ORDER BY', Uppercase(FDQuery1.SQL.Text)) <> 0 then
   begin
       NewOrderString := 'ORDER BY ' + Column.FieldName;

       //Ersetze eine bereits vorhandene Sortierung durch die neue Spalte
       For i:= 0 to DBgrid1.Columns.Count - 1 do
       begin
          OldOrderString := 'ORDER BY ' + DBGrid1.Columns[i].FieldName;
          FDQuery1.SQL.Text := StringReplace(FDQuery1.SQL.Text, OldOrderString, NewOrderString, [rfReplaceAll, rfIgnoreCase]);

          //Prüfe, ob dieselbe Spalte noch einmal angeklickt wurde
          //Falls ja, wechsel zwischen auf- und absteigend
          If OldOrderString = NewOrderString then
          begin
              FDQuery1.SQL.Text := FDQuery1.SQL.Text;

              If pos(Uppercase(NewOrderString + ' ASC'), Uppercase(FDQuery1.SQL.Text)) <> 0 then
                FDQuery1.SQL.Text := StringReplace(FDQuery1.SQL.Text, NewOrderString + ' ASC', NewOrderString + ' DESC', [rfReplaceAll, rfIgnoreCase])
              Else
                FDQuery1.SQL.Text := StringReplace(FDQuery1.SQL.Text, NewOrderString + ' DESC', NewOrderString + ' ASC', [rfReplaceAll, rfIgnoreCase]);

           end;
        end;
   end
   Else
      FDQuery1.SQL.Append(' ORDER BY ' + Column.FieldName + ' ASC');

   FDQuery1.Active := true;

   //Springe zur markierten Zeile zurück
   FDQuery1.RecNo := TmpRecNo;

   //Die Spalte, nach der sortiert wird, fett machen und alle anderen wieder normal darstellen
   For i := 0 to DBGrid1.Columns.Count - 1 do
   begin
      If DBGrid1.Columns[i].Title.Font.Style = [fsBold] then
           DBGrid1.Columns[i].Title.Font.Style := [];
   end;

   If DBGrid1.Columns[Column.Index].Title.Font.Style <> [fsBold] then
           DBGrid1.Columns[Column.Index].Title.Font.Style := [fsBold];
       SingleClick := false;
   end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   SingleClick := true;
   Timer1.Enabled := false;
   DBGrid1TitleClick(SelectedColumn);
end;

Cookies erleichtern die Bereitstellung unserer Dienste. Mit der Nutzung unserer Dienste erklären Sie sich damit einverstanden, dass wir Cookies verwenden.
Weitere Informationen Ok