Abbruch Edit eines Grids

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

Abbruch Edit eines Grids

Beitrag von Michl »

Ein fröhliches Hallo in die Runde,

ich hatte mir ein Inplace-Editor für Grids geschrieben bzw. bin immer noch bei Anpassungen. Dabei ist bei mir möglich eine Eingabe eines Strings per <ESC> abzubrechen. Jetzt will ich das Verhalten so nah wie möglich an den vorhandenen Editoren anpassen und habe festgestellt, dass so ein Vorgehen dort nicht implementiert ist.

Z.B. der TStringCellEditor der auf einem TCustomMaskEdit basiert gibt diese Cancel-Anweisung nicht durch.

Folgende Fragen habe ich dazu:

1. Gibt es eine Option im Grid, die so ein Verhalten dann erlaubt (und ich habe Tomaten auf den Augen - meine weiteren Fragen wären dann hinfällig)?
2. Haltet Ihr generell so ein Verhalten für sinnvoll?

Ich glaube, dass ich das implementiert bekäme.
3. Wo greift Ihr bei einem TDrawGrid den eingegenen Editor-Text ab?
z.B. so?:

Code: Alles auswählen

procedure TForm1.DrawGridEditingDone(Sender: TObject);
var
  PC: PChar;
begin
  PC:=StrAlloc(DrawGrid.Editor.GetTextLen);
  DrawGrid.Editor.GetTextBuf(PC, DrawGrid.Editor.GetTextLen + 1);
  Caption:=PC;
end; 
oder bei

Code: Alles auswählen

procedure TForm1.DrawGridSetEditText(Sender: TObject; ACol,
  ARow: Integer; const Value: string);
begin
  Caption:=Value;
end;
So hätte man das Problem, dass man (ohne Verbiegungen) nicht weiss, ob ein Abbruch stattgefunden hat.

4. Wäre es von Vorteil bei einem TDrawGrid auch ein Ereignis OnValidateEntry zu erstellen, was nicht gefeuert würde bei einem Abbruch?

Danke

Michael

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Hier die wesentlichen Methoden, so wie ich sie für ESC im WorksheetGrid von fpspreadsheet eingebaut habe - ich hoffe ich habe nichts vergessen...:

Code: Alles auswählen

 
function TsCustomWorksheetGrid.GetCellText(ACol, ARow: Integer): string;
begin
  .... // ermittelt aus der zugrundeliegenden Datenstruktur den Text für diese Zelle
end;
 
procedure TsCustomWorksheetGrid.PutCellText(ACol, ARow: Integer; AText: String);
begin
  ... // eingegebenen Text in die zugrundeliegende Datenstruktur schreiben
end;
 
procedure TsCustomWorksheetGrid.SelectEditor;
begin
  FOldEditText := GetCellText(Col, Row);
  inherited;
end;
 
function TsCustomWorksheetGrid.GetEditText(ACol, ARow: Integer): string;
begin
  Result := GetCellText(aCol, aRow);
  if Assigned(OnGetEditText) then OnGetEditText(Self, aCol, aRow, Result);
end;
 
procedure TsCustomWorksheetGrid.SetEditText(ACol, ARow: Longint; const AValue: string);
begin
  FEditText := AValue;
  FEditing := true;
  inherited SetEditText(aCol, aRow, aValue);
end;
 
procedure TsCustomWorksheetGrid.KeyDown(var Key : Word; Shift : TShiftState);
begin
  if (Key = VK_ESCAPE) and FEditing then begin
    SetEditText(Col, Row, FOldEditText);
    EditorHide;
    exit;
  end;
  inherited;
end;
 
procedure TsCustomWorksheetGrid.EditingDone;
var
  oldText: String;
  cell: PCell;
begin
  if (not EditorShowing) and FEditing then
  begin
    oldText := GetCellText(Col, Row);
    if oldText <> FEditText then     // hier könnte man noch weitere Prüfungen einbauen
    begin
      PutCellText(Col, Row, FEditText);   // FEditText in die zugrunde liegende Datenstrukur übernehmen
      FEditText := '';
    end;
    inherited EditingDone;
  end;
  FEditing := false;
end;
 

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Danke! Ja, eben, das ist das Problem. Für jedes Grid muss so ein Verhalten separat implementiert werden. Ich würde den Ansatz eine Ebene höher versuchen und in den Editoren und nicht in den Grids implementieren. Daher meine Frage, bevor ich für den TStringCellEditor, TPickListCellEditor und TCompositeCellEditor einen Patch versuche zu schreiben, ob etwas gegen diese Implementierung spricht und deshalb dieser abgelehnt würde. Ein Abbruchimplementierung je Grid wäre dann bei allen Grids, die auf die vorgenannten Celleditoren zurückgreifen, hinfällig.

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Bei den Editoren würde ich das nicht implementieren, weil es dann für jeden Editor separat wiederholt werden muss. Ich denke aber, dass man den Code mindestens in TCustomDrawGrid einbauen kann, dann erben es all praktisch verwendeten Grids. Evtl. würde ich noch eine Option a la "goCancelEditing" vorsehen, mit der man das ESC-Verhalten ausschalten kann.

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Wäre es denn dann aber nicht von Vorteil im TCustomGrid zu verankern?!

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Ja

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Ok, werde mich mal in den nächsten Tagen daran versuchen...

Danke für die Unterstützung :) :) :)

Trotzdem bleibt für mich noch die Frage, wie man ein Abbruch bei einem DrawGrid signalisiert (Frage 3 und 4 ganz oben - mit einem neuen Event, z.b. OnValidateEntry)?!

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Frage 3: Eigentlich sollte SetEditText als Gegenstück von GetEditText dafür geeignet sein, die Daten aus dem Editor abzuholen und in die zugrundeliegende Datenstruktur zu übernehmen. Allerdings kommt SetEditText nach jedem Tastendruck, das ist mir zu früh. Daher habe ich EditingDone verwendet, das erst aufgerufen wird, wenn ENTER gedrückt wird bzw. die Zelle verlassen wird.

Frage 4: Nein, bei einem Cancel-Event würde ich ValidateEntry außen vor lassen, es hat sich ja nichts geändert. Allerdings ist es vielleicht sinnvoll, ein weiteres Ereignis a la OnCancelEntry zur Verfügung zu stellen.

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

wp_xyz hat geschrieben:Frage 3: Eigentlich sollte SetEditText als Gegenstück von GetEditText dafür geeignet sein, die Daten aus dem Editor abzuholen und in die zugrundeliegende Datenstruktur zu übernehmen. Allerdings kommt SetEditText nach jedem Tastendruck, das ist mir zu früh. Daher habe ich EditingDone verwendet, das erst aufgerufen wird, wenn ENTER gedrückt wird bzw. die Zelle verlassen wird.
Ja eben, deshalb die Frage.
wp_xyz hat geschrieben:Frage 4: Nein, bei einem Cancel-Event würde ich ValidateEntry außen vor lassen, es hat sich ja nichts geändert. Allerdings ist es vielleicht sinnvoll, ein weiteres Ereignis a la OnCancelEntry zur Verfügung zu stellen.
Zur Zeit gibt es nur bei einem TStringGrid das Event OnValidateEntry. Daher hatte ich mir überlegt, ein Ereignis OnValidateEntry für ein TDrawGrid einzufügen, dass eben genau nur einmal aufgerufen wird (nicht wie SetEditText bzw. dann OnSetEditText aus eben den vorbenannten Gründen), wenn die Eingabe eines Strings beendet wird. Diese würde dann bei einem Abbruch eben nicht aufgerufen (wenn ich das für das TCustomGrid einfüge, dann beim TStringGrid auch nicht mehr - bei gesetztem "goCancelEditing").

Werde es mal versuchen, bei weiteren Fragen werde ich mich melden. Weitere Anregungen und Hinweise sind dennoch gern gesehen.

Danke

Michael

[Edit] mit 32 Optionen ist das Limit für das Einfügen von Optionen erreicht (siehe http://free-pascal-lazarus.989080.n3.na ... 39097.html), ich werde es daher ohne Extraoption versuchen.

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Ich denke, dass es so funktioniert, habe einige Tests gemacht. Falls noch jemand testen will, anbei der Patch (Lazarus 1.5 r47402M FPC 3.1.1 i386-win32-win32/win64) und ein einfaches Projekt mit einem Draw- und einem StringGrid. TDBGrid habe ich auch getestet, da hat meine Implementierung keine Auswirkung und das ist auch gut so. Ich werde noch eine Nacht darüber schlafen und wenn mir oder Euch nichts Neues auffällt das morgen Abend im Bugtracker hochladen.

@wp: Eigentlich hättest Du gar nicht den Umweg über ein eigenes "FOldEditText" gehen müssen, im TCustomGrid gibt es schon eine Variable "FEditorOldValue".
Dateianhänge
TestGrids.zip
(4.77 KiB) 65-mal heruntergeladen
grids.pas.patch
(1.39 KiB) 75-mal heruntergeladen

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Hmm... Beeindruckend kurz, aber ich glaube, es ist nicht richtig, wenn du in EditorGetValue einen Typecast zum TCustomEdit machst - ich könnte mir vorstellen, dass der Editor auch ein ganz anderer Typ sein könnte, z.B. eine Combobox, die nicht TWinControl, aber nicht von TCustomEdit abstammt, TDateTimePicker, der von TCustomControl abgeleitet ist, etc.

Ich hatte so eine ähnliche Situation mal und konnte mir helfen, indem ich mir ein Interface deklariert habe, das in diesem Fall den Edit-Text annehmen bzw. zurückgeben muss. Jeder Editor muss eine Methode für dieses Interface zur Verfügung stellen. Dann sollte es gehen ohne Type-Cast:

Code: Alles auswählen

 
const
  GUID_GridEditor = '{1EE4FE54-BD68-4BD9-9B6D-9C1151698DA0}';  // Wert mit SHIFT+CTRL+G erzeugen
 
type
  IGridEditor = interface [GUID_GridEditor]
    function GetEditValue: String;
    procedure SetEditValue(AValue: String);
  end;
 
  TStringCellEditor = class(TCustomMaskEdit, IGridEditor)
  ...
    function GetEditValue: String;
    procedure SetEditValue(AValue: String);
  end;
 
  TPickListCellEditor = class(TCustomCombobox, IGridEditor)
  ...
    function GetEditValue: String;
    procedure SetEditValue(AValue: String);
  end;
 
  // ... genauso auch mit den anderen
 
 
function TStringCellEditor.GetEditValue: String;
begin
  Result := Text;
end;
 
procedure TStringCellEditor.SetEditValue(AValue: String);
begin
  Text := AValue;
end;
 
function TPickListCellEditor.GetEditValue: String;
begin
  Result := Text;
end;
 
procedure TPickListCellEditor.SetEditValue(AValue: String);
var
  idx: Integer;
begin
  idx := Items.IndexOf(AValue);
  if idx <> -1 then ItemIndex := idx else Text := AValue;
end;
 
// etc. -- mir ist nicht ganz klar, wie das beim TButtonCellEditor zu machen ist...
 
procedure TCustomGrid.KeyDown(...);
begin
  ...
   VK_ESCAPE:
     begin
       Editor.SetEditvalue(FEditorOldValue);
       EditorHide;
       Key := 0;
     end;
...
end;
 
function TCustomGrid.EditorGetValue(validate:boolean=false): boolean;
begin
  if validate then begin
    CurValue := Editor.GetEditValue;
  ...
end;
 
Danke für den Hinweis auf FEditorOldValue.

[EDIT]
Wobei... Bei dem TButtoncellEditor sieht man, dass ein String vielleicht nicht die richtige Übergabegröße ist. Was ist, wenn der Editor eine Checkbox ist? Die naheliegende Größe wäre da ein boolean. Oder eine ColorBox? Da wäre ein TColor auf jeden Fall besser geeignet als ein String. Vielleicht muss man den Code oben so umschreiben, dass das GridEditor-Interface mit Hilfe von variants kommuniziert. Aber dann kommt das nächste Problem: ValidateEntry vergleicht die Eingabe, also den Rückgabewert des Interfaces, mit einem String! Das wird der tiefere Grund sein, warum das ValidateEntry nur für das StringGrid existiert. Ich fürchte fast, das alles führt zu einem massiven Umbau der Grid-Komponente, was der Betreuer des Grids nicht mitmachen wird... Natülich können diese Spezial-Editoren auch mit Strings arbeiten, aber wiegesagt, das erscheint mir etwas unsauber. Vielleicht solltest du dein Vorhaben zuerst in der Mailing-Liste mit den Entwicklern diskutieren.

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Ja, bei einigen Controls gibt es tatsächlich Probleme. Habe Deine Variante mal probiert, bin noch etwas unentschlossen, ich versuche noch einen anderen Weg. Melde mich später. Danke fürs mitdenken.

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von Michl »

Habe jetzt einfach die Möglichkeit der Messages genutzt, so wie allgemein mit den Editoren im Grid kommuniziert wird. Damit funktioniert mein Beispiel und auch das Bsp. Lazarus\examples\gridexamples\columneditors\stringgrideditor problemlos.
wp_xyz hat geschrieben:Wobei... Bei dem TButtoncellEditor sieht man, dass ein String vielleicht nicht die richtige Übergabegröße ist. Was ist, wenn der Editor eine Checkbox ist? Die naheliegende Größe wäre da ein boolean. Oder eine ColorBox? Da wäre ein TColor auf jeden Fall besser geeignet als ein String.
Da zur Zeit alles im CustomGrid auf Strings (zu sehen an der TGridMessage) und nicht auf Variants oder Sonstiges ausgerichtet ist, sehe ich keine größeren Probleme diesen Patch zu verwenden.

Werde es mal hochladen, mal sehen, was Jesus Reyes Aguilar dazu sagt?!

Neuer Patch anbei und nochmal danke :D

Edit: http://bugs.freepascal.org/view.php?id=27328
Dateianhänge
grids.pas.patch
(3.93 KiB) 85-mal heruntergeladen

Code: Alles auswählen

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

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

Re: Abbruch Edit eines Grids

Beitrag von wp_xyz »

Ja, sieht gut aus.

Antworten