[gelöst] TStringGrid und TComboBox neue Inhalte einfügen

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

[gelöst] TStringGrid und TComboBox neue Inhalte einfügen

Beitrag von Michl »

Hallo, folgendes Problem:

Ich möchte gern mittels einer ComboBox in einem StringGrid diverse Werte eingeben. Diese sollen in der ComboBox gewählt werden, falls dieser Wert jedoch noch nicht in der TStringlist der ComboBox enthalten ist, soll dieser neu eingefügt werden. Dies habe ich mit Hilfe dieses Forums und einigem Suchen/Probieren auch soweit hinbekommen.

Nun habe ich folgende 2 Fragen:

1. Wie bekomme ich es hin, dass nach Schließen der ComboBox der Fokus (rote Umrandung) wieder auf diese bzw. auf die nächste zu bearbeitende Zelle im Stringgrid gesetzt wird?!

2. Wie kann ich dem Stringgrid sagen, dass als nächstes die Zelle unterhalb der derzeit aktiven Zelle bearbeitet werden soll (die Zellen neben der bearbeitenden Zelle werden nach Auswahl in der ComboBox automatisch gefüllt)?!

Code: Alles auswählen

 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer; var Editor: TWinControl);
begin
  if aCol=1 then begin
    ComboBox1.BoundsRect:=StringGrid1.CellRect(aCol,aRow);
    ComboBox1.Text:=StringGrid1.Cells[StringGrid1.Col,StringGrid1.Row];
    ComboBox1.ItemIndex:=-1;
    Editor:=ComboBox1;
  end;
  if acol>1 then Editor:=nil;
end;
 
procedure TForm1.ComboBox1Exit(Sender: TObject);
begin
  StringGrid1.Cells[StringGrid1.Col,StringGrid1.Row]:=ComboBox1.Text;
//  StringGrid1.Row:=StringGrid1.Row+1;   
//  ComboBox1.EditingDone;
//  ComboBox1.Hide;
//  StringGrid1.EditingDone;
//  StringGrid1.SetFocus;
 
end;
 
procedure TForm1.ComboBox1EditingDone(Sender: TObject);
begin
  ComboBox1.Hide;
end;
 
procedure TForm1.ComboBox1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if key=13 then begin
    if ComboBox1.ItemIndex<0 then ComboBox1.ItemIndex:=ComboBox1.Items.Add(ComboBox1.Text);
    ComboBox1.EditingDone;
  end;
end;             
 
Wer möchte kann sich auch die Mühe machen und das einfache Beispiel einmal ausführen, dort ist wohl am besten verständlich, was ich meine?!

Vielen Dank im Voraus!!!
Dateianhänge
Projekt1.ZIP
(126.3 KiB) 85-mal heruntergeladen
Zuletzt geändert von Michl am Fr 25. Jan 2013, 20:22, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

wp_xyz
Beiträge: 5203
Registriert: Fr 8. Apr 2011, 09:01

Re: TStringGrid und TComboBox neue Inhalte einfügen

Beitrag von wp_xyz »

Die Bearbeitung von StringGrid-Zellen mit Hilfe einer Kombobox ist bereits in TStringGrid eingebaut - siehe http://wiki.lazarus.freepascal.org/Grid ... ll_Editors.

Code: Alles auswählen

 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
  var Editor: TWinControl);
begin
  if aCol=1 then begin
    Editor := TStringGrid(Sender).EditorByStyle(cbsPickList);
    if (Editor is TCustomComboBox) then
      with Editor as TCustomComboBox do begin
        if (aRow mod 2=0) then
          Style := csDropDown
        else
          Style := csDropDownList;
        case aRow of
          1:
            Items.CommaText := 'ONE,TWO,THREE,FOUR';
          2:
            Items.CommaText := 'A,B,C,D,E';
          3:
            Items.CommaText := 'MX,ES,NL,UK';
          4:
            Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
        end;
      end;
  end;
end;
 
Damit sich die markierte Zellen nach der Eingabe automatisch um eins weiterbewegt, kann man die Eigenschaft AutoAdvance benutzen.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: TStringGrid und TComboBox neue Inhalte einfügen

Beitrag von Michl »

AutoAdvance war das was ich für den nächten Schritt gesucht habe - Danke dafür!!! :D

Doch das mit der Picklist funktioniert in meinem Fall nicht. Diese hatte ich zuvor verwendet, doch leider hat sie für meinen Fall den entscheidenden Nachteil, dass ich bei der Eingabe von neuen Werten behindert werde:

Pickliste/Combobox soll folgenden Style (Benutzerfreundlich) haben:
Style:=csDropDown;
AutoComplete:=true;
AutoDropDown:=true;

Bsp:

In der Picklist (TStringList) stehen folgende Werte:

Laminat 6mm
Laminat 8mm
Laminat 9mm

will ich nun beim Laufen des Programms einen neuen Wert hinzufügen zB. "Parkett 8mm"

funktioniert dies -> "Parkett 8mm" wird in Stringgrid übernommen

füge ich allerdings den neuen Wert "Laminat 10mm" springt der Itemindex auf den ersten Eintrag, der zu Laminat passt und verweilt dort. Das bedeutet, egal, was ich nach Laminat schreibe, der AutoComplete bleibt auf dem Itemindex und überschreibt nach Beendigung meiner Eingabe die nicht identischen Werte. Aus "Laminat 10mm" wird "Laminat 6mm"!!!

Dieses nicht von mir gewünschte Verhalten habe ich mit der Combobox, die ich auf den Editor lege verhindert. Ich frage nach beendeter Eingabe (Taste >Enter< wurde gedrückt) den Index der Combobox ab und wenn nicht genau dieser Eintrag schon vorhanden ist, wird dieser neu in der Combobox erzeugt und genommen (egal wo der Itemindex der Combobox durch Autovervollständigen stehengeblieben ist!!!).

Das funktioniert ja auch alles prima, ich weiss nur nicht, wie ich die Combobox so schließe, dass die nächste Zelle im Stringgrid bearbeitet werden soll (rote Umrandung / Fokus auf Zelle im Stringgrid). Es ist zur Zeit so, dass ich auch in die nächste Zelle springen kann (Stringgrid.Col:=Stringgrid.Col+1 bzw. Stringgrid.Row:=Stringgrid.Row+1) und dort auch die Zelle bearbeite, wenn ich mit der Tastatur Werte eingebe, doch sehe ich dies einfach nicht, wo ich mich befinde. Erst wenn der Editor des Stringgrids wieder aktiviert wird, wird dies ersichtlich.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: TStringGrid und TComboBox neue Inhalte einfügen

Beitrag von Michl »

Hat noch jemand eine Idee, wie ich den Editor in der nächsten Zelle aktiviert bekomme?!

Eigentlich möchte ich nichts unübliches, hat evtl. jemand eine ganz andere Idee, wie ich meine Wunscheingabe hinbekomme?!

Ich stelle mir die Eingabe wie Im Lazarus-Editorfenster vor. Wenn man hinter einem Objekt/Klasse einen Punkt schreibt, öffnet sich nach einer kurzen Zeit eine Dropdownliste. Wenn man weiter schreibt, so bleiben nur noch die Werte in der Liste, die diesen String beinhalten. Schreibt man dann irgendetwas weiter, so wird nicht zwangsweise ein Wert der Klasse übernommen, sondern das Geschriebene bleibt stehen.


EDIT: Ich versuche jetzt das mal anders herum, nutze den normalen Editor, überprüfe bei KeyUp den geschriebenen Inhalt der Zelle und öffne statt einer Combobox eine TListBox unterhalb - mal sehen ob ich meinen gewünschten Effekt so näher komme.

EDIT_2: geht jetzt, allerdings ohne Combobox aber mit Listbox! Ich poste den Code, wenn ich das Beispiel überarbeit habe! :P :P :P

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: [gelöst] TStringGrid und TComboBox neue Inhalte einfügen

Beitrag von Michl »

Jetzt funktioniert es wie ich es mir vorgestellt habe:

Code: Alles auswählen

 var
  Vorlage: TStringList;
  AltRow, AltCol: Integer;
  AltStr: String;
 
procedure TForm1.SGCellExit(Sender: TObject);
begin
  ListBox.Hide;
end;
 
procedure TForm1.ZeigeListBox(Str:String; ARow:Integer);
var
  i: Integer;
  Rec: TRect;
  p1, p2:TPoint;
begin
  ListBox.Items.Clear;
  for i:=0 to Vorlage.Count-1 do
    if (length(Str)=0) or (pos(lowercase(Str), lowercase(Vorlage.Strings[i]))>0) then
      ListBox.Items.Add(Vorlage.Strings[i]);
 
  if ListBox.Items.Count>0 then begin
 
    Listbox.Parent:=SG;
    Rec:=SG.CellRect(SG.Col, SG.Row);
 
    inc(Rec.Top, SG.RowHeights[SG.Row]);
    inc(Rec.Bottom, SG.RowHeights[SG.Row]);
 
    ListBox.BoundsRect:=Rec;
    if ListBox.Items.Count>9 then ListBox.Height:=ListBox.ItemHeight*10
                             else ListBox.Height:=ListBox.ItemHeight*(ListBox.Items.Count+1);
 
    if Listbox.Height+Rec.Bottom>clientheight then begin
      Rec.Top:=Rec.Top - 2 * SG.RowHeights[SG.Row] - ListBox.Height;
      Rec.Bottom:=Rec.Bottom - 2 * SG.RowHeights[SG.Row];
      ListBox.BoundsRect:=Rec;
    end;
 
    ListBox.ItemIndex:=-1;
    ListBox.Show;
 
    Self.ActiveControl.OnExit:=@SGCellExit;
 
  end else ListBox.Hide;
end;
 
procedure TForm1.ListBoxClick(Sender: TObject);
var
  Stelle: integer;
begin
  Stelle := ListBox.ItemAtPos((Sender as TListBox).ScreenToClient(Mouse.CursorPos), True);
  SG.Cells[SG.Col, SG.Row]:=ListBox.Items[Stelle];
  SG.Editor:=nil;
end;
 
procedure TForm1.SGClick(Sender: TObject);
begin
  if (AltRow=SG.Row) and (AltCol=SG.Col) then
    ZeigeListBox(SG.Cells[SG.Col, SG.Row], SG.Row);
  AltRow:=SG.Row;
  AltCol:=SG.Col;
  AltStr:=SG.Cells[SG.Col, SG.Row];
end;
 
procedure TForm1.SGKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  AltCol:=(Sender as TStringGrid).Col;
  AltRow:=(Sender as TStringGrid).Row;
 
  if ListBox.Visible then case key of
    40:begin  //Pfeil runter
         if ListBox.ItemIndex<ListBox.Items.Count-1 then ListBox.ItemIndex:=ListBox.ItemIndex+1;
         key:=0;
       end;
    38:begin  //Pfeil hoch
         if ListBox.ItemIndex>0 then ListBox.ItemIndex:=ListBox.ItemIndex-1;
//                                else ListBox.Visible:=false;
         key:=0;
       end;
    13:begin  //Enter
         SG.Cells[SG.Col,SG.Row]:=ListBox.Items[ListBox.ItemIndex];
         ListBox.Visible:=false;
       end;
    34:begin  //Bild runter
         if ListBox.ItemIndex<ListBox.Items.Count-1 then ListBox.ItemIndex:=ListBox.ItemIndex+10
                                                    else ListBox.ItemIndex:=ListBox.Items.Count-1;
         key:=0;
       end;
    33:begin  //Bild hoch
         if ListBox.ItemIndex>10 then ListBox.ItemIndex:=ListBox.ItemIndex-10
                                 else ListBox.ItemIndex:=0;
         key:=0;
       end;
    27:Listbox.Hide;
  end else AltStr:=SG.Cells[SG.Col, SG.Row];
 
//  Caption:=inttostr(key);
end;
 
procedure TForm1.SGKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (SG.Col=1) and (AltCol=SG.Col) and (AltRow=SG.Row) then begin
    if SG.Cells[SG.Col, SG.Row] <> altStr then
      ZeigeListBox(SG.Cells[SG.Col, SG.Row], SG.Row);
    altStr:=SG.Cells[SG.Col, SG.Row];
  end;
end;
Für diejeingen, die das gleiche Problem haben, habe ich mal ein kleines Beispiel beigefügt.
Dateianhänge
Stringgrid mit Listbox.zip
(178.88 KiB) 100-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Antworten