Combobox auf DBGrid: Eingabeverhalten

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
sierdolg
Beiträge: 66
Registriert: Mi 24. Okt 2012, 15:50

Combobox auf DBGrid: Eingabeverhalten

Beitrag von sierdolg »

Werte Forengemeinde,

wenn man im zugrundeliegenden Dataset ein Nachschlagefeld definiert hat, zeigt das DBGrid ebenso erfreulicher- wie magischerweise ja zum Editieren eine Combobox an. Falls das Dataset sortiert ist, sind es auch die angezeigten Werte, wenn man es aufklappt. So weit, so gut.

Nun hätte ich gerne das von gängigen Dropdown-Controls "gewohnte" Verhalten eingeschaltet oder nachgerüstet
(Wer nicht sicher ist, was ich meine: Dasselbe Verhalten wie z.B. die Dropdown-Box links neben "Los" im "SUCHE"-Abschnitt links in der Mitte auf
http://www.lazarusforum.de/. Hier reicht es auch, "Y" zu tippen, um zum Eintrag "Yahoo" zu springen.)

(A) durch Eingabe der ersten paar Buchstaben den ersten in der längeren Liste passenden Eintrag automatisch "anspringen" zu können
# oder, noch eleganter wäre: -
(B) eine Substringsuche, in dem die Eingabe einiger Zeichen die angezeigte Liste einengt, so daß dann schnell mit den Pfeiltasten das Gemeinte erreicht und ausgewählt werden kann. Klar ist mir, daß dann der bereits eingegebene String als Filter für das der Liste zugrundende Datensetz herhalten kann.

Nun gehen die Fragezeichen los: auf DBGrid und den einzelnen TCoumns finde ich nichts, wo man das direkt einstellen könnte.
Da es auf dem Formular mehrere Grids und auf diesen wiederum etliche Nachschlagefelder gibt, die sich alle gleich verhalten sollten, wäre eine "zentrale" Lösung auch sinnvoller.

Nachdem ich keine Beispiele finden konnte, kam ich auf die Idee, es formularweit mit

Code: Alles auswählen

procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
  WriteLn('Name:',ActiveControl.Name, ' Caption:',ActiveControl.Caption);
end;     
 

zu versuchen.

Interessanterweise fördert das beim Blättern durch die Dropdownliste Ausgaben wie

Code: Alles auswählen

Name:PickListEditor Caption:Drucker

zustande. Allerdings wird das Ereignis gar nicht ausgelöst, wenn ich (wie bei einer alphanumerischen Eingabe in das Dropdown-Textfeld) einfach Buchstaben drücke, sondern erst, wenn es verlassen wurde.

Vielleicht ist das auch ein ungeschickter Ansatz - hat jemand einen besseren Vorschlag, einen Zeiger auf ein existierendes Beispiel oder gar eine komfortable Eingabe für die Combobox selber schon mal umgesetzt?

MmVisual
Beiträge: 1445
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von MmVisual »

Ich mache das mit mehreren Feldern und verschiedene Eingaben:

Code: Alles auswählen

procedure TfrmMain.grdPPosSelectEditor(Sender: TObject; Column: TColumn;
  var Editor: TWinControl);
begin
  if bReadOnly Or Column.Field.ReadOnly or not cboPPosBauteil.Enabled then
    Exit;
  ErrMessagesAdd('grdPPosSelectEditor');
  if grdSelectEditor(Sender, Column, Editor) then
  begin
    ErrMessagesRemove();
    Exit;
  end
  else if SameText(Column.Field.FieldName, 'Bauteil_ID') Or
    SameText(Column.Field.FieldName, 'Bauteil') then
  begin
    Editor := TComboBox.Create(Self);
    CopyObject(cboPPosBauteil, Editor);
    TComboBox(Editor).ItemIndex := cboPPosBauteil.ItemIndex;
    TComboBox(Editor).OnChange := cboPPosBauteil.OnChange;
    Editor.BoundsRect := grdRect;
  end
  else if SameText(Column.Field.FieldName, 'BauteilLager_ID') Or
    SameText(Column.Field.FieldName, 'BauteilLager') then
  begin
    If cboPPosCase.Enabled Then
    Begin
      Editor := TComboBox.Create(Self);
      CopyObject(cboPPosCase, Editor);
      TComboBox(Editor).ItemIndex := cboPPosCase.ItemIndex;
      TComboBox(Editor).OnChange := cboPPosCase.OnChange;
      Editor.BoundsRect := grdRect;
    end;
  end
  else if SameText(Column.Field.FieldName, 'SMD') then
  begin
    Editor := TComboBox.Create(Self);
    TComboBox(Editor).Items.Add(slLanguage[1]); // 'Nein'
    TComboBox(Editor).Items.Add(slLanguage[0]); // 'Ja'
    Editor.BoundsRect := grdRect;
    TComboBox(Editor).ItemIndex := Column.Field.AsInteger;
    TComboBox(Editor).OnChange := @qPPosSMDEdit;
    TComboBox(Editor).Style := csDropDownList;
  end
  else if SameText(Column.Field.FieldName, 'Top') then
  begin
    Editor := TComboBox.Create(Self);
    TComboBox(Editor).Items.Add(slLanguage[1]); // 'Nein'
    TComboBox(Editor).Items.Add(slLanguage[0]); // 'Ja'
    Editor.BoundsRect := grdRect;
    TComboBox(Editor).ItemIndex := Column.Field.AsInteger;
    TComboBox(Editor).OnChange := @qPPosTopEdit;
    TComboBox(Editor).Style := csDropDownList;
  end
  else if SameText(Column.Field.FieldName, 'Variante') then
  begin
    Editor := TDBComboBox.Create(Self);
    CopyObject(cboPPosVariante, Editor);
    TDBComboBox(Editor).DataSource := cboPPosVariante.DataSource;
    TDBComboBox(Editor).DataField := cboPPosVariante.DataField;
    Editor.BoundsRect := grdRect;
  end
  else if SameText(Column.Field.FieldName, 'Art') then
  begin
    Editor := TComboBox.Create(Self);
    TComboBox(Editor).Items.Delimiter := '|';
    TComboBox(Editor).Items.QuoteChar := '"';
    TComboBox(Editor).Items.DelimitedText := slLanguage[84];
    Editor.BoundsRect := grdRect;
    if Column.Field.AsInteger < TComboBox(Editor).Items.Count then
      TComboBox(Editor).ItemIndex := Column.Field.AsInteger
    else
      TComboBox(Editor).ItemIndex := 0;
    TComboBox(Editor).OnChange := @qPPosArtEdit;
    TComboBox(Editor).Style := csDropDownList;
  end;
  ErrMessagesRemove();
end;     


Das Freigeben der erzeugen Komponente "Editor" macht Lazarus alleine.
EleLa - Elektronik Lagerverwaltung - www.elela.de

sierdolg
Beiträge: 66
Registriert: Mi 24. Okt 2012, 15:50

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von sierdolg »

Hallo MmVisual,

vielen Dank für die Anregung! Zuallererst muß ich mich für meinen Anfängerstatus in der Angelegenheit entschuldigen und den endlos langen Schlauch, auf dem ich seit einigen Tagen zu stehen scheine - Du hast, wie ich sehe, mindestens 10 Jahre Vorsprung mit Lazarus, während mir zwar Grundlagen der Informatik klar sind, aber trotzdem die meisten Kniffe noch unbekannt, die man im Detail kennen sollte, um sein Ziel mit vertretbarem Aufwand zu erreichen.

Also: in meinem Fall wäre eine möglichst einfache und "zentrale" Lösung das beste. (Fallunterscheidungen nach Feld brauche ich keine, das Nachschlagefeld sollte sich stets analog verhalten - eben wie bei LibreOffice Base und dergleichen.)

Ich nehme an, idealerweise würde man dazu eine Komponente verwenden, die all die benötigte Funktionalität kapselt, sich bei Nachschlagefeldern also genau so verhält wie ein Listenfeld in Libreoffice, das mit Feld 1 an einen Fremdschlüssel gebunden und "SELECT bezeichner, id FROM tabelleX ORDER BY bezeichner ASC" als Listeninhalt eingestellt hat. (Vielelicht bringt das mein Anliegen nochmals auf den Punkt).

So eine Erweiterung von TDBGrid zu erstellen (gefunden habe ich trotz langer Suche keine), würde allerdings meine Möglichkeiten um ein Vielfaches übersteigen. (Und ich fürchte, wenn's für Profis halbwegs einfach wäre, hätte es längst schon jemand getan.)

Ersatzweise könnte ich also in die OnSelectEditor aller betreffenden DBGrids den Aufruf einer einzigen Funktion einfügen, z.B.

Code: Alles auswählen

procedure TForm1.DBGridArtikelSelectEditor(Sender: TObject; Column: TColumn;  var Editor: TWinControl);
begin
  AnyGridSelectEditor(Sender, Column, Editor);
end;
 
procedure TForm1.DBGridLieferscheinSelectEditor(Sender: TObject;   Column: TColumn; var Editor: TWinControl);
begin
   AnyGridSelectEditor(Sender, Column, Editor);
end;
 
procedure TForm1.DBGridPositionSelectEditor(Sender: TObject; Column: TColumn; var Editor: TWinControl);
begin
     AnyGridSelectEditor(Sender, Column, Editor);     
end;
 


und in dieser dann tun, was zu erledigen wäre:

Code: Alles auswählen

Procedure TForm1.AnyGridSelectEditor(Sender: TObject; Column: TColumn;  Editor: TWinControl);
begin
   if Column.ReadOnly or not Column.Visible then Exit;
   //ErrMessagesAd('AnyGridSelectEditor'); //wozu?
   if Editor.Name <> 'PickListEditor' then Exit;
   WriteLn( TComponent(Sender).Name,' mit Spalte ', Column.FieldName, ' nutzt ', Editor.Name );
end;
 

So... soweit funktioniert es auch, aber jetzt müßte man wohl das Dataset, aus dem Column sich speist, mit .Filter:=Editor.Text (sofern es das gibt, gemeint ist: der in diesem Feld bereits eingegebene String) versehen, .Filtered:=True setzen und dann vielleicht auch noch irgendwie eine Aktualisierung der PickList veranlassen. (Immer nach dem gleichen Schema, allenfalls muß falls nötig noch nach Datentyp differenziert werden, also ob das Nachschlagefeld numerisch oder String ist; eine Fallunterscheidung nach Feldnamen bräuchte ich nicht).

Und hier komme ich mit Deinem Beispiel doch nicht weiter, weil sich mir viele Details aus dem Zusammenhang nicht erschließen.
* Was ist der Sinn hinter ErrMessagesAdd('...') und ErrMessagesRemove() - gehört das zu irgendeiner Unit, die ich nicht eingebunden habe?
* Was prüft grdSelectEditor(Sender, Column, Editor)?


Ich packe mal ein
minimalbeispiel.7z
Minimalbeispiel mit SQLITE3.db und drei DBGrids, von denen jeweils eins ein Nachschlagefeld enthält. (ZEOS und sqlite3 erforderlich)
(66.81 KiB) 83-mal heruntergeladen
Minimalbeispiel bei, das eine sinnfreie SQLIte3-Datenbankdatei erstellt und seine drei Grids an deren Tabellen bindet.

MmVisual
Beiträge: 1445
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von MmVisual »

ErrMessagesAdd('...') und ErrMessagesRemove()

Das sind Funktionen die ich selbst geschrieben habe, du kannst die löschen, die haben keine relevante Funktion für Dich.

Ich habe eine EXE mit nahezu 100000 Codezeilen, damit ich den Usern auch ohne debuggen helfen kann zeichnet meine EXE jeder Einsprung und jedes Verlassen auf. Bei Exception wird ein Log geschrieben und ich weiß somit genau was zuvor und vor allem bei welchem Codeblock die Exception ausgelößt wurde. Diese Methode ist schon ziemlich aufwändig weil wirklich jede Funktion das braucht, aber sehr effizient in der Fehleranalyse. Die Lazarus Ausgaben mit Stack trace sind absolut unbrauchbar - ich habe sicher schon über 500 EXE Versionen veröffentlicht.

Du musst "Editor: TWinControl" als "Var" übergeben, denn bei dem Aufruf erwartet das TDBGrid dass du dynamisch ein Steuerelement zur Laufzeit erzeugst das wiederum das Grid eingebettet in der Zelle darstellt.

In TForm1.Create kannst du auch das machen:
Begin
DBGridArtikel.OnSelectEditor := @AnyGridSelectEditor;
DBGridLieferschein.OnSelectEditor := @AnyGridSelectEditor;
DBGridPosition.OnSelectEditor := @AnyGridSelectEditor;
End;

Also direkt deine Funktion zuweisen, Du brauchst keine 3 Zwischenfunktionen. (Oder deine Funktion im Objektinspector zuweisen, die muss aber in der deklaration gleich aussehen.)
EleLa - Elektronik Lagerverwaltung - www.elela.de

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von mse »

sierdolg hat geschrieben:Ich nehme an, idealerweise würde man dazu eine Komponente verwenden, die all die benötigte Funktionalität kapselt, sich bei Nachschlagefeldern also genau so verhält wie ein Listenfeld in Libreoffice, das mit Feld 1 an einen Fremdschlüssel gebunden und "SELECT bezeichner, id FROM tabelleX ORDER BY bezeichner ASC" als Listeninhalt eingestellt hat. (Vielelicht bringt das mein Anliegen nochmals auf den Punkt).

Da bietet sich TDBEnumeditDB in einem TDBWidgetGrid an (MSEgui).

sierdolg
Beiträge: 66
Registriert: Mi 24. Okt 2012, 15:50

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von sierdolg »

Hallo mse,
lassen sich TDBWidgetGrid und TDBEnumeditDB in Lazarus einbinden?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von mse »

Leider nicht.

sierdolg
Beiträge: 66
Registriert: Mi 24. Okt 2012, 15:50

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von sierdolg »

Um meine wortreiche Umschreibung der gewünschten Funktion durch ein Minimal-Beispiel zu ersetzen, habe ich mal aus einem Edit und einem DBGrid die entsprechende Funktionalität nachgebildet. (Die zugehörige SQLite-Datei sollte beim ersten Start des Programms im Projektverzeichnis erstellt werden.)

Nach der Eingabe einiger Zeichen sollte die Liste gefiltert werden; die Endauswahl (d.h.Eingabe) eines Eintrags kann dann wahlweise durch Enter-Taste im Editor oder auf der mit PfeilAuf/PfeilAb angesteuerten Zeile des Popups (hier durch Grid simuliert) erfolgen - halt so, wie man das fast überall sonst auch macht :o)

Genau darum wundert es mich ja, daß es ein solches Steuerelement für Lazarus augenscheinlich nicht gibt...
Dateianhänge
mockup.7z
Pseudo-Kombination des gesuchten Controls
(80.75 KiB) 96-mal heruntergeladen

sierdolg
Beiträge: 66
Registriert: Mi 24. Okt 2012, 15:50

Re: Combobox auf DBGrid: Eingabeverhalten

Beitrag von sierdolg »

Hier wären noch zwei Beispiele (für ein isoliertes TDBLookupComboBox, das wie beschrieben das dynamische Filtern unterstützt):
Sieht jemand Möglichkeiten, das auch für Lazarus umzusetzen? Für die DataControls-Palette wäre es eine riesige Bereicherung.

Gerne würde ich daran mithelfen, doch ich bin kein Profi wie die meisten von Euch - habe zwar versucht, die Quelltextdateien von TDBCombobox zu verstehen, aus denen die klassische TDBLookupComboBox entsteht, muß aber feststellen, daß für mein Amateurniveau so etwas korrekt (und vor allen Dingen geschickt) alleine anzugehen ein hoffnungsloses Unterfangen wäre.

Antworten