DBGrid Spaltenbreite Anpassen a la Excel



  • Hi,

    ich möchte bei meinem DBGrid bei Doppelclick auf die Spaltenbegrenzung die Breite der entsprechenden Spalte an den längsten Eintrag in dieser Spalte anpassen. Also so, wie Excel das auch macht.
    Eine fertige Funktion habe ich nicht dafür gefunden, auch keine Möglichkeit, wie ich raus bekomme, wie breit meine Spalte werden muss...



  • heimchen schrieb:

    ...wie ich raus bekomme, wie breit meine Spalte werden muss...

    Das ergibt sich aus dem Font des Canvas, der Zeichenbreite sowie der Zeichenanzahl.
    Im WinApi gibts die Funktion GetTextExtentPoint32; im Forum auch mind. einen Thread dazu.

    mfg
    kpeter



  • In der VCL gibt es dazu TCanvas::TextWitdh, und zu *DBGrid* *Spaltenbreite* liefert die Suchfunktion auch einige Threads.



  • Ja, genau sowas hab ich gesucht.
    Aber nu hab ich noch ein anderes Problem: woran erkenn ich einen Doppelklick auf die Spaltenbegrenzung in der Titelzeile?
    Und wenn ich durch die Daten so durchgehe, ändert sich ja auch mein Scrolling. zwar verwende ich

    TBookmark MyBookmark = this->ADOQuery->GetBookmark();
    this->ADOQuery->GotoBookmark(MyBookmark);
    

    Aber dann steht eben der ausgewählte Eintrag in der Mitte vom Grid, während er zuvor vielleicht ganz oben oder unten im Grid stand...



  • Den jeweils aktuellen Datensatz kann man sich vor Beginn der Spaltenanpassung als Variable ablegen um diese
    Position dann wieder zu setzen.

    Hier mal ein Code-Schnipsel, der allerdings mit einem Klick in die Grid-Titelzeile geht.

    void __fastcall TForm1::DBGrid1TitleClick(TColumn *Column)
    {
       int wi = 0;
       int inx = Column->Field->Index;
    
       DBGrid1->DataSource->DataSet->DisableControls();
       // alle Zeilen der Spalte durchlaufen und Textlänge ermitteln;
       DBGrid1->DataSource->DataSet->First();
       for (int i = 0; i < DBGrid1->DataSource->DataSet->RecordCount; i++) {
          TField * field = DBGrid1->Fields[inx];
          String txt = field->Text;
          int len = DBGrid1->Canvas->TextWidth(txt);
          // grösste Textlänge speichern
          if (wi < len)
             wi = len;
          DBGrid1->DataSource->DataSet->Next();
       }
       DBGrid1->DataSource->DataSet->EnableControls();
       Column->Width = wi + 5;
       // zurück zum aktuellen Datensatz...
    }
    

    Demonstriert gleichzeitig die Methoden DisableControls()/EnableControls() aus deinem Parallel-Thread.

    mfg
    kpeter



  • alternativ kannst du auch ein separates Query nehmen und dort nur den Inhalt mit den meisten Buchstaben also der größten Länge ausgeben und damit dann die Spaltenbreite berechnen (SQL Beispiel für MSSQL) - ungetestet:

    void __fastcall TForm1::DBGrid1TitleClick(TColumn *Column)
    {
    try 
        {
        DBGrid1->DataSource->DataSet->DisableControls();
    
        TCanvas *c = DBGrid1->Canvas;
        String name = Column->FieldName;
        ADOCommand1->CommandText = "select top 1 "+name+", LEN("+name+") from optionen order by 2 desc";
        ADODataSet1->Recordset = ADOCommand1->Execute();
    
        TSize size = c->TextExtent(ADODataSet1->Fields->Fields[0]->AsString);
    
        Column->Width = size.cx + 5;
        ADODataSet1->Close();
        }
    __finally
        {
        DBGrid1->DataSource->DataSet->EnableControls();
        }
    
    }
    


  • Ok, das sind Varianten, wie ich die Spaltenbreite bestimm. Aber entweder seh ich es nicht, oder ich weiß noch immer nicht, wie ich meine Ansicht wieder so herstelle, wie sie vorher war (außer ich nehm Linea's Variante).
    Den Datensatz per Bookmark zu kennzeichnen kann ich ja, es ändert sich aber trotzdem das Scrolling.
    Und es bleibt noch die Erkennung des Doppelklicks auf die Begrenzungen in der Titelzeile...



  • heimchen schrieb:

    ...es ändert sich aber trotzdem das Scrolling...

    Erklär das bitte näher.
    Wenn, nach meinem geposteten Code, zB. Datensatz 1388 aktuell ist, ist er das nach Spaltenbreiten-Änderung immer noch und der Scrollbalken hat die gleiche Position.

    heimchen schrieb:

    ...Und es bleibt noch die Erkennung des Doppelklicks auf die Begrenzungen in der Titelzeile...

    Frage während des Doppelklicks die Mausposition innerhalb des Grid ab.
    Spaltenbreite und Höhe der Titelzeile geben die Grenzen vor, in denen der Code ausgeführt werden darf.

    MfG
    kpeter



  • kpeter schrieb:

    heimchen schrieb:

    ...es ändert sich aber trotzdem das Scrolling...

    Erklär das bitte näher.

    Ich verwende dazu ein Bookmark, dass ich auf den aktuell selektierten Eintrag lege und nach dem gewurschtel wieder aufrufe. Beim Aufruf des Bookmarks wird dieser Eintrag selektiert und vertikal mittig im DBGrid angezeigt. Dort stand der Eintrag aber vorher nicht, er war irgendwo (nach dem scrollen meist ganz oben oder unter). Zwar kommt man zum Eintrag zurück, der Anwender bekommt aber ein merkwürdiges Bild, weil der Eintrag auf dem Bildschirm nicht mehr an der gleichen Stelle steht.

    kpeter schrieb:

    Frage während des Doppelklicks die Mausposition innerhalb des Grid ab.
    Spaltenbreite und Höhe der Titelzeile geben die Grenzen vor, in denen der Code ausgeführt werden darf.

    Geht das wirklich nur so aufwendig?



  • versuch mal folgendes om Ereignis OnDblClick des TDBGrid (ungetestet):

    void __fastcall TForm2::DBGrid1DblClick(TObject *Sender)
    {
    try
    		{
    		DBGrid1->DataSource->DataSet->DisableControls();
    		int index   = DBGrid1->SelectedIndex;
    
    		TCanvas *c = DBGrid1->Canvas;
    		String name = DBGrid1->SelectedField->FieldName;
    		ADOCommand1->CommandText = "select top 1 "+name+", LEN("+name+") from optionen order by 2 desc";
    		ADODataSet1->Recordset = ADOCommand1->Execute();
    
    		TSize size = c->TextExtent(ADODataSet1->Fields->Fields[0]->AsString);
    
    		DBGrid1->Columns->Items[index]->Width = size.cx + 5;
    		ADODataSet1->Close();
    		}
    __finally
    		{
    		DBGrid1->DataSource->DataSet->EnableControls();
    		}
    }
    


  • Ja, so über den Weg bekomm ich schon was gescheites. Mit nen bissl Kosmetik siehts bei mir so aus:

    void __fastcall TMyForm::DBGridTitleClick(TColumn *Column)
    {
      auto_ptr<TADOQuery> ADOQuery(new TADOQuery(0));
    
      ADOQuery->SQL->Clear();
      ADOQuery->SQL->Add("SELECT TOP(1) ["+Column->FieldName+"]");
      ADOQuery->SQL->Add("FROM [MyTbl]");
      ADOQuery->SQL->Add("ORDER BY LEN(["+Column->FieldName+"]) DESC");
    
      ADOQuery->Connection = ADOConnection;
      ADOQuery->LockType = ltReadOnly;
    
      ADOQuery->Open();
    
      Column->Width = this->DBGrid->Canvas->TextWidth(ADOQuery->FieldByName(Column->FieldName)->AsString) + 5;
    
      ADOQuery->Close();
    }
    

    Das ist schon ganz nett. Kritisch wie ich bin seh ich aber zwei Haken: zum einen verursacht jede Breitenanpassung eine SQL-Abfrage und belastet den Server zusätzlich auch noch mit eine Aggregatfunktion (ich könnt mir vorstellen, dass LEN bei großen Tabellen nicht grade genügsam ist). Zum anderen seh ich grad keine Möglichkeit, mit einem Query alle Spalten in einer Abfrage zu kriegen. Also dass ich ein Record bekomm, in dem für jede Spalte der längste Eintrag steht. Ich könnt mir aber vorstellen, dass das das kleinere Problem ist 😉

    Und bleibt ja noch das OnResizeDblClick Problem...


Anmelden zum Antworten