AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi Tabellarisches Dateneditieren mit "Typ-Bewusstsein"
Thema durchsuchen
Ansicht
Themen-Optionen

Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

Ein Thema von domi.neuberger · begonnen am 1. Jul 2014 · letzter Beitrag vom 2. Jul 2014
Antwort Antwort
Seite 1 von 2  1 2      
domi.neuberger

Registriert seit: 1. Jul 2014
5 Beiträge
 
#1

Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 12:00
Sperriger Begriff, ich weiß...

Vorweg: bin Delphi-7-Routinier beim Umstieg auf XE6. Für die Lösung darf also XE6 und ein bisschen Delphi-Erfahrung vorausgesetzt werden

Ich habe die Aufgabe, Daten in einem Grid zu editieren, das etwa so aussieht:

Code:
Eigenschaft   |Wert         |Datentyp    |Aktiv
Kunde         |Mustermann AG |String      |JA      
AutomVerbinden |JA           |Bool        |JA      
Port          |16387         |Int         |NEIN    
...
Die Funktionalität ähnelt dem des Windows Registry Editors, nur dass ich alle Daten direkt im Grid editieren möchte, und keine Popup-Eingabefenster haben will.
Nun möchte ich pro Datentyp der jeweiligen Zelle (der teils vom Inhalt einer anderen Zelle abhängt, siehe "Wert"!) nur gemäß Datentyp zulässige Daten eingeben lassen, d. h. in Integer-Feldern keine Buchstaben etc., und boolsche Felder am liebsten durch eine Checkbox ein- und ausgeben.

* Die Daten kommen nicht (immer) aus der Datenbank, d. h. ich vermute TDbGrid zu verwenden wäre ungünstig. Ich tendiere dazu TStringGrid zu verwenden (bin aber für Vorschläge offen)
* Ich möchte keine Third-Party-Komponenten (DevExpress & Co.) einsetzen, da wir ein großes Entwickler-Team sind welches eine große Zahl an Anwendungen mit einem recht langen Lebenszyklus betreut, und Reproduzierbarkeit der Binaries ist extrem wichtig. Diese Aspekte beißen sich damit, dass diese Third Parties (im Grunde dankenswerterweise) öfters mal neue Versionen ihrer Komponenten veröffentlichen, dass man aber den Sourcecode dieser Komponenten nur begrenzt zusammen mit seinem eigenen Sourcecode "konservieren" kann. Somit ein anderes Binary je nach aktuell installierter Version der Third Party Komponenten.
* Aus ähnlichen Gründen möchte ich keine eigenen Komponenten entwickeln. Dieses Konzept hatten wir bislang verfolgt (seit Delphi 1) und sind immer unglücklicher damit geworden, wollen so nicht weitermachen, insbesondere weil XE6 nun sehr strikt ist was den Grundsatz angeht, eine Unit immer nur in einem Package zu verwenden. Prinzipbedingt mussten wir unsere Komponenten auf etwa 6 Packages aufteilen...
* Ich hatte gehofft, dass XE6 bereits alles an Bord hat, was man hierzu braucht (dem ist aber wohl nicht so). Trotzdem möchte ich möglichst viel mit "Bordmitteln" erschlagen.
* Das Implementieren der Funktionalität direkt in der jeweiligen Unit ist unerwünscht, da ich Grids in der Art wie oben beschrieben an etlichen Stellen benötige.
* Aus genau diesem Grund möchte ich das Grid per Formular-Designer auf meine Form ziehen und im Objekt-Inspektor die Eigenschaften zuweisen können, d. h. kein dynamisches Erzeugen von Controls.

Mein Lösungsansatz sieht so aus:

Ich erschaffe in einer (oder mehreren) Unit(s) "Interceptor-Klassen" (Beispiel: type TStringGrid = class(Grids.TStringGrid) ) für TStringGrid und TEdit und erweitere sie so um Eigenschaften "DataType" (fürs StringGrid einmal ein Array über alle Spalten und einmal zweidimensional über alle Zellen). Dann überlagere ich die Ereignisroutinen.
Bei "meinem" TEdit wird bei jedem Change -Ereignis geprüft (das überlagere ich), ob Text in den vorgegebenen Datentyp gewandelt werden kann, andernfalls nehme ich die letzte Änderung (z. B. Eingabe eines Buchstabens) wieder zurück. Weiterhin kann ich so gegen hinzugefügte Eigenschaften "MinValue", "MaxValue" prüfen usw.. Das funktioniert prima. Nachteil (aber damit kann ich leben): "DataType" muss ich den TEdit s zur Laufzeit, d. h. im Code zuweisen - der Objektinspektor weiß nicht, dass ich TEdit überlagert habe.
Bei "meinem" TStringGrid habe ich es immerhin geschafft, durch Überlagern von DrawCell eine Checkbox statt des aktuellen Zelleninhalts zu malen (angehakt wenn Inhalt="T", kein Haken wenn Inhalt="F", ansonsten ausgegraut), und durch Überlagern von GetEditText kann ich den Zelleninhalt toggeln. Selber Nachteil wie bei TEdit : DataType kann nur zur Laufzeit zugewiesen werden, aber das ist ok. Was mir schon nicht gefällt: eigentlich bräuchte ich ein Ereignis "Jetzt wird die Zelle bearbeitet". GetEditText kommt dem zwar nahe, aber nicht zu 100%.

Jetzt würde ich gerne eine Integer-Zelle handhaben. Dafür könnte ich ja meine "typbewusste" TEdit als Editor einsetzen. Jetzt wird's aber doof: ich beginne zu editieren, GetEditText wird gerufen, ich erzeuge meine "typbewusste" TEdit (oder mache sie sichtbar), dadurch verliert das Grid den Fokus und ruft SetEditText . An OnSetEditText hängt sich aber meine übergeordnete Anwendung ran um beim bzw. nach dem Bearbeiten eines Zelleninhalts die Daten z. B. wegzuspeichern. Ja, SetEditText ist nicht wirklich das Ereignis "Bearbeiten fertig", aber das gibt es halt nicht bei einem StringGrid.
Noch doofer: das überlagerte TEdit.Change wird bei dieser Editbox nicht mehr gerufen. Ich kann also völligen Blödsinn eingeben. Es wirkt fast so, als würde es nur begrenzt unterstützt werden, innerhalb einer Interceptor-Klasse eine weitere "aufgeborte" Klasse zu verwenden. Methoden und Eigenschaften sind kein Problem, aber es werden wohl "nur" die Ereignisse der Basisklasse gerufen...

Ich habe auch schon versucht, den Inplace-Editor des StringGrids zu überlagern (dadurch würden vermutlich meine ganzen Ereignisroutinen-Missbrauchs-Versuche wegfallen), aber da bin ich nicht wirklich weitergekommen. Es gibt zwar Anleitungen dazu, aber die ersetzen den Inplace-Editor eines StringGrids für das gesamte Grid. Ich brauche halt je nach Zelle die eine oder die andere Eingabemethode.

Was würdet ihr mir raten? Wie realisiert man am besten ein Grid, das verschiedene Datentypen pro Zelle unterstützt, inklusive jeweils dazu passendem Editor?
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#2

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 15:21
Besorge Dir ein entsprechendes Grid, das das alles schon kann: Das ist 1000x billiger, als es selbst zu machen. Ausnahme: Du machst es für dich und hast viel Zeit und Lust.

Ich upgrade bei einem meiner Kunden das Delphi6 Projekt mit DevExpress schon seid Jahren nicht. Bei einem anderen Kunden mit aktuellerem Delphi (ja, auch schon bald 10 Jahre alt ) mach ich das schon. Aber die aktuellen Subskriptionen habe ich eh nicht mehr.

Ein eigenes Framework zu erstellen ist typischerweise ein ziemlich bescheuertes Hobby von Entwickler-Teams. Ich muss das mal so deutlich sagen: LASST ES.

Kauft euch DevExpress und entweder ihr macht die Upgrades mit oder eben nicht. Wenn ihr viele Kunden und Anwendungen habt, dann macht es einfach: DevExpress ist da sehr sehr vorsichtig, was Änderungen am Code hat. Ich hab 5 Jahre mit denen gearbeitet und musste 2x an den Code ran. Und das war auch nicht sooo schlimm.

Geändert von Dejan Vu ( 1. Jul 2014 um 15:35 Uhr)
  Mit Zitat antworten Zitat
Sailor

Registriert seit: 20. Jul 2008
Ort: Balaton
112 Beiträge
 
Delphi 2010 Professional
 
#3

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 15:28
Ist eigentlich ziemlich einfach:

- Einen Editor (TEdit,TComboBox...) pro Datentyp anlegen, Visible := False

- Im Ereignis OnDrawCell den dem Typ der Zelle entsprechenden Editor über der Zelle positionieren, denselben mit dem Zelleninhalt vorbelegen und sichtbar machen

- Übernahme der Editorinhalte in die Zelle bei Abschluß des Editierens, z.B. beim TEdit die Entertaste drücken, den Eingabewert überprüfen und evtl. eine Fehlermeldung ausgeben
  Mit Zitat antworten Zitat
domi.neuberger

Registriert seit: 1. Jul 2014
5 Beiträge
 
#4

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 16:23
@Dejan Vu:
Zeit, Geld und Lust sind natürlich triftige Argumente
Und ich weiß auch, dass die DevExpress-Leute vorsichtig sind, wir haben diese für manche Projekte eingesetzt und bis auf eine "Schweinerei" mit der eigentlich unnötigen versteckten Vorgabe eines für uns ungünstigen Threading-Modells keine negativen Erfahrungen was die Qualität angeht gemacht.
Dummerweise arbeiten wir unter anderem für die Pharma-Industrie, und die ist mehr als pingelig was Reproduzierbarkeit angeht...
Kann man irgendwie sicherstellen dass in Projekt X immer genau Version Y der verwendeten Packages in mein Binary einfließt, egal welche Version dieser Packages ich aktuell auf meinem Rechner installiert habe? Ist damit sichergestellt, dass unabhängig von der installierten Version dieser Packages immer genau das selbe Binary herauskommt?

@Sailor:
Prinzipiell klar, aber so weit waren meine Experimente auch schon gediehen (trotzdem danke für die Antwort!). Das Hin- und Her-Übergeben der Daten zwischen dem TStringGrid und dem TEdit funktioniert auch so halbwegs, inklusive der Ermöglichung dass die Anwendung auf Datenänderungen im StringGrid reagieren kann (das angesprochene Problem, dass GetEditText und SetEditText halt nicht wirklich bedeutet "Daten haben sich geändert", und das Ereignis gibts nicht wirklich beim StringGrid).
Was ich gerne anders lösen würde ist den Eingabewert des TEdit beim Drücken der Enter-Taste zu prüfen. "Mein" TEdit macht das alles schon bei der Eingabe, man kann erst gar nichts falsches eingeben, das tut wunderbar - so lange bis ich das modifizierte TEdit innerhalb meines modifizierten TStringGrid verwende, dann wird Change nicht mehr angesprungen...
Wenn ich nur wüsste warum... dann wäre ich schon fast mit meiner Lösung zufrieden...
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#5

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 16:38
Ich würde wenn dann ein TDrawGrid als Basisklasse verwenden.

Für jeden Datentyp (String, Boolean, ...) würde ich eine spezielle Klasse erstellen, die folgende Routinen implementiert:
Delphi-Quellcode:
procedure Draw(Grid: TDrawGrid; ItemIndex: integer; ItemRect: TRect; Canvas: TCanvas);
procedure MouseDown(Grid: TDrawGrid; ItemIndex: integer; ItemRect: TRect; ...);
procedure MouseUp(Grid: TDrawGrid; ItemIndex: integer; ItemRect: TRect; ...);
procedure KeyDown(Grid: TDrawGrid; ItemIndex: integer; ItemRect: TRect; ...);

etc.
Das Grid bekommt eine Liste solcher Klassen, eine pro Spalte, und gibt dann, abhängig davon, welche Zelle gerade aktiv ist, entsprechende Events an die zuständige Klasse weiter.

Man könnte sich bei den Methoden oben alternativ auch überlegen, die Parameter Grid, ItemIndex und ItemRect wegzulassen und stattdessen als Felder aufzuführen, und dafür zwei Routinen ActivateCell(...) und LeaveCell(...) o.ä. einzuführen. Bei dem Modell wird ja vermutlich eh davon ausgegangen, dass immer nur eine Zelle gleichzeitig editiert werden kann.

Da müsste man halt mal etwas rumspielen und gucken, was sich als am sinnvollsten herausstellt.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#6

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 17:11
Kann man irgendwie sicherstellen dass in Projekt X immer genau Version Y der verwendeten Packages in mein Binary einfließt, egal welche Version dieser Packages ich aktuell auf meinem Rechner installiert habe? Ist damit sichergestellt, dass unabhängig von der installierten Version dieser Packages immer genau das selbe Binary herauskommt?
Compilierst du mit Packages? Dann obliegt es ja dir selbst, welche BPL du beim Kunden installierst. Wenn du ohne Packages compilierst, dann sind sowieso die entsprechenden DCU- bzw. PAS-Dateien im Suchpfad maßgebend und nicht, was in der IDE installiert ist. Natürlich kannst du diese mit versionieren und somit auch für einen älteren Versionsstand exakt dieselbe Version wieder herstellen.

Es ist allerdings denkbar, daß die DFM-Dateien unterschiedlich sind, wenn in der Delphi-IDE eine andere Version der Komponente installiert ist als zu den Sourcen passen würde. Das passiert z.B. immer dann, wenn neue Properties eingeführt wurden, die keinen sinnvollen Default-Wert haben und somit in die DFM wandern. Das merkst du beim Compilieren erstmal nicht, sondern gemeinerweise erst dann, wenn das Form, der Frame oder das Datenmodul geladen werden - also zur Laufzeit. Deswegen sollte man unerwartete Änderungen an den DFMs immer kritisch unter die Lupe nehmen und gegebenenfalls wieder zurücksetzen.

Um das zu vermeiden, müsstest du vorübergehend die IDE-Packages austauschen, was natürlich voraussetzt, daß auch alle Versionen verfügbar sind. Leider machen es einem die Bibliotheks-Hersteller manchmal recht schwer mal eben die Packages zu tauschen. Ich versioniere daher seit einiger Zeit immer die kompletten Bibliothekssourcen mit und habe die Projekte und Projektgruppen so angepasst, daß ich mit wenigen Klicks die entsprechenden Packages neu erstellen und installieren kann. (Ich glaube, Andreas Hausladen wollte mal ein Tool bauen das sowas automatisiert - habe aber noch nicht wieder davon gehört)

Übrigens kann dir das zumindest theoretisch auch schon mit Delphi-eigenen Komponenten passieren, wenn du z.B. zwischenzeitlich ein Update oder einen Hotfix für Delphi eingespielt hast.

Die EXE wird aber auch im Idealfall noch Unterschiede aufweisen - das kann dir auch passieren, wenn du einfach nur zweimal hintereinander compilierst.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#7

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 17:18
Na ja, wenn die Versionen sich nie ändern dürfen, bieten sich VMs an. Pro Kunde eine VM. Fertig.
Da kann man in Delphi 3, XE6, mit DevExpress Version 4, oder Ultimate Extreme 2015 (oder wie die Versionen jetzt heißen) arbeiten.

Ich würde an euerer Stelle die Kosten bei Verwendung eines 3rd Party Frameworks evaluieren, also Lizenzen, VM, Upgrades etc.
Dann, auf der anderen Seite die Kosten für eine Eigenentwicklung, und zwar bis es richtig stabil läuft. Mir scheint, hier habt ihr vielleicht auch nicht genügend Wissen (zumindest gehen deine Fragen in diese Richtung). Trotzdem solltet ihr in der Lage sein, den Implementierungs- und Pflegeaufwand abzuschätzen. Diese Zahl nehmt ihr mal 2-3 (Denn bis es produktiv läuft, vergeht dann nochmal die gleiche Zeit). Und dokumentieren müsst ihr das ja auch, unit tests schreiben usw.

Dann schaut ihr euch die Kosten auf beiden Seiten an.

Und schluckt.

Und dann listet ihr die offenen Probleme bezüglich der Kundenvorgaben auf und schreibt dahinter, wie ihr sie lösen wollt. Da ihr nicht die Einzigen auf dieser Welt seid, die diese Vorgaben umsetzen müssen, finden sich hier bestimmt immer wieder Antworten.

Geändert von Dejan Vu ( 1. Jul 2014 um 17:25 Uhr)
  Mit Zitat antworten Zitat
Sailor

Registriert seit: 20. Jul 2008
Ort: Balaton
112 Beiträge
 
Delphi 2010 Professional
 
#8

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 17:21
Zitat:
so lange bis ich das modifizierte TEdit innerhalb meines modifizierten TStringGrid verwende, dann wird Change nicht mehr angesprungen...
Wenn ich nur wüsste warum... dann wäre ich schon fast mit meiner Lösung zufrieden...
Das habe ich nicht verstanden. Ich stoße die Datenübernahme in das Grid z.B. auch beim OnExit an oder OnMouseLeave an.
  Mit Zitat antworten Zitat
domi.neuberger

Registriert seit: 1. Jul 2014
5 Beiträge
 
#9

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 2. Jul 2014, 10:39
@Uwe: "Wenn du ohne Packages compilierst, dann sind sowieso die entsprechenden DCU- bzw. PAS-Dateien im Suchpfad maßgebend [...]" --> danke, genau das wollte ich wissen, ob das definitiv der Fall ist!
Das mit den DFMs hab ich auch schon bemerkt, das ist natürlich extrem hässlich. Muss ich mir mal überlegen ob ich das Risiko eingehen kann oder wie ich es ausschalten kann. Gern passiert es ja auch, dass automatisch neue Units ins "uses" gestreut werden, aber das merkt man dann zum Glück schon beim Versuch zu compilieren.
"Die EXE wird aber auch im Idealfall noch Unterschiede aufweisen - das kann dir auch passieren, wenn du einfach nur zweimal hintereinander compilierst." Nicht wirklich, oder? Warum? Lach jetzt nicht, aber zur guten alten Turbo Pascal Zeit war das nicht der Fall...

@Dejan Vu: das mit den VMs ist mir auch schon durch den Kopf gegangen. Ist zwar recht aufwändig, aber bis Uwe meine Illusionen zerstört hat (grins) war ich der Meinung, dass man damit die ultimative Reproduzierbarkeit erreichen würde. Und wenn man die haben will / muss, muss man Opfer bringen.
Bzgl. des Kostenfaktors sprichst Du mir aus der Seele. Mal schauen wie diejenigen auf die von Dir genannten Argumente reagieren, die bei uns die finanziellen Entscheidungen treffen
  Mit Zitat antworten Zitat
domi.neuberger

Registriert seit: 1. Jul 2014
5 Beiträge
 
#10

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 2. Jul 2014, 11:06
@Sailor: das Problem ist nicht das Übernehmen der Daten vom TEdit in die Cell, sondern das Verhindern dass ich in einer als "Integer" deklarierten Zelle "Otto ist blöd" eingeben kann... Ich will ihn schon daran hindern, das erste "O" zu schreiben. Das funktioniert auch wenn ich "mein" TEdit standalone benutze, aber nicht wenn ich es in "meinem" TStringGrid benutze.
Ich hab meine Unit mal auf das Wesentliche reduziert (hoffe so ist sie weiterhin compilierbar) und unten aufgeführt. Binde sie mal in ein leeres Beispielprojekt ein, das nur ein TEdit und ein TStringGrid enthält. Weise sowohl dem TEdit als auch einer Zelle des TStringGrid den Typ "ftInteger" zu. Probiere in das TEdit was "falsches" reinzuschreiben - geht nicht. Und jetzt probiere in die Zelle des StringGrids was "falsches" reinzuschreiben - geht leider. Grund: "mein" TEdit.Change wird nicht gerufen. Warum?

Delphi-Quellcode:
unit ControlImprovers;

interface
uses StdCtrls, Grids, Typinfo, Types, Db;

type
  TEdit = class(StdCtrls.TEdit)
  private
    FLastValidText : string;
    FEditType : TFieldType;
    FPrecision : integer;
    FDigits : integer;
    function GetValue : variant;
    procedure SetValue (AValue : Variant);
    procedure SetEditType(AType : TFieldType);
  protected
    procedure Change; override;
  public
    property Value : variant read GetValue write SetValue;
    property EditType : TFieldType read FEditType write SetEditType;
    property Precision : integer read FPrecision write FPrecision;
    property Digits : integer read FDigits write FDigits;
  end;

type TStringGrid = class(Grids.TStringGrid)
  private
    FCellType : array of array of TFieldType;
    FCustomInplaceEdit: TEdit;
    FCustomInplaceEditOnCol,
    FCustomInplaceEditOnRow : integer;
    function GetCellType(ix, iy : integer) : TFieldType;
    procedure SetCellType(ix, iy : integer; ACellType : TFieldType);
    procedure OnCustomInplaceEditExit(Sender : TObject);
  protected
    function GetEditText(ACol, ARow: Integer) : string; override;
  public
    property CellType[ix, iy : integer] : TFieldType read GetCellType write SetCellType;
end;


implementation

uses Windows, SysUtils, Classes, ComCtrls, Forms, Controls, Math;

procedure TStringGrid.OnCustomInplaceEditExit(Sender : TObject);
begin
  if assigned(FCustomInplaceEdit) then begin
    if FCustomInplaceEdit.Visible then begin
      Cells[FCustomInplaceEditOnCol, FCustomInplaceEditOnRow] := FCustomInplaceEdit.Value;
      if assigned(OnSetEditText) then begin
        OnSetEditText(Sender, FCustomInplaceEditOnCol, FCustomInplaceEditOnRow, FCustomInplaceEdit.Value);
      end;
      FCustomInplaceEdit.Hide;
    end;
  end;
end;

function TStringGrid.GetEditText(ACol, ARow: Integer): string;
var Inp : TInput;
    ARect : TRect;
begin
  if Assigned(OnGetEditText) then OnGetEditText(self, ACol, ARow, Result);

  if CellType[ACol, ARow] = ftUnknown then begin
    //no cell type specified - treat like normal stringgrid:
    //do nothing, and text for default inplace editor is current cell text
    result := Cells[ACol, ARow];
  end
  else begin
    InplaceEditor.EditText := Cells[ACol, ARow];
    InplaceEditor.Hide;
    if not assigned(FCustomInplaceEdit) then begin
      FCustomInplaceEdit := TEdit.Create(self);
      FCustomInplaceEdit.Parent := Self;
    end;
    FCustomInplaceEditOnCol := ACol;
    FCustomInplaceEditOnRow := ARow;

    //EditType of the TEdit gets assigned from the CellType:
    FCustomInplaceEdit.EditType := CellType[ACol, ARow];

    //Assign value of the cell to the TEdit
    FCustomInplaceEdit.Value := Cells[ACol, ARow];
    
    //positioning the TEdit directly onto the cell:
    ARect := CellRect(ACol, ARow);
    FCustomInplaceEdit.Left := ARect.Left;
    FCustomInplaceEdit.Top := ARect.Top;
    FCustomInplaceEdit.Width:= ARect.Width;
    FCustomInplaceEdit.Height:=ARect.Height;
    FCustomInplaceEdit.Font := Font; //use font of grid
    FCustomInplaceEdit.Color := Color;

    FCustomInplaceEdit.Show;
    FCustomInplaceEdit.BringToFront;
    FCustomInplaceEdit.SetFocus;

    FCustomInplaceEdit.OnExit := OnCustomInplaceEditExit;
    result := FCustomInplaceEdit.Value;
  end;
end;

function TEdit.GetValue : variant;
begin
  if (FEditType in FieldTypesString) or (FEditType = ftUnknown) then begin
    result := Text;
  end
  else if FEditType in FieldTypesInteger then begin
    result := Round(StrToFloatDef(Text, 0.0));
      //trunc(StrToFloat) instead of just StrToInt; allows converting from float to integer
      //without loss of value (StrToIntDef('1.234', 0) --> 0!)
  end
  else if FEditType in FieldTypesFloat then begin
    result := StrToFloatDef(Text, 0.0);
  end
  else assert(false, 'Unsupported EditType!');
end;

procedure TEdit.SetValue(AValue: variant);
begin
  if (FEditType in FieldTypesString) or (FEditType = ftUnknown) then begin
    Text := AValue;
  end
  else if FEditType in FieldTypesInteger then begin
    Text := IntToStr(AValue);
  end
  else if FEditType in FieldTypesFloat then begin
    Text := FloatToStrF(AValue, ffFixed, FPrecision, FDigits);
  end
  else assert(false, 'Unsuppported EditType!');
end;

procedure TEdit.SetEditType(AType: TFieldType);
begin
  FEditType := AType;
  Value := Value; //converts "Text" to be of given type with given precision / digits if possible,
                      //otherwise sets value to 0 (or 0.0)
end;

procedure TEdit.Change;
var iDummy : integer;
    dDummy : double;
    iSelStart,
    iSelLength : integer;
begin
  iSelStart := SelStart;
  iSelLength := SelLength;
  if (FEditType in FieldTypesString) or (FEditType = ftUnknown) then begin
    //nothing to do
  end
  else if FEditType in FieldTypesInteger then begin
    if TryStrToInt(Text, iDummy) then begin //is Text a valid integer?
      Value := Value; //"014" --> "14"
      FLastValidText := Text; //store "14" as "last known good" to be able to revert to it
    end
    else if FLastValidText <> 'then begin
      Text := FLastValidText; //Text is not a valid integer, so revert to last valid text
      SelStart := iSelStart; //restore cursor pos to where it was before (gets misplaced by setting Text)
      SelLength := iSelLength;//restore selection to where it was before (gets misplaced by setting Text)
    end
    else begin
      Value := 0; //sets Text to "0"
      SelStart := iSelStart; //restore cursor pos to where it was before (gets misplaced by setting Text)
      SelLength := iSelLength;//restore selection to where it was before (gets misplaced by setting Text)
    end;
  end
  else if FEditType in FieldTypesFloat then begin
    if TryStrToFloat(Text, dDummy) then begin //is Text a valid float?
      Value := Value; //"014.3" --> "14.3"
      FLastValidText := Text; //store "14.3" as "last known good" to be able to revert to it
    end
    else if FLastValidText <> 'then begin
      Text := FLastValidText; //Text is not a valid float, so revert to last valid text
      SelStart := iSelStart; //restore cursor pos to where it was before (gets misplaced by setting Text)
      SelLength := iSelLength; //restore selection to where it was before (gets misplaced by setting Text)
    end
    else begin
      Value := 0.0; //sets Text to "0.000" (or whatever "Digits" says)
      SelStart := iSelStart; //restore cursor pos to where it was before (gets misplaced by setting Text)
      SelLength := iSelLength; //restore selection to where it was before (gets misplaced by setting Text)
    end;
  end
  else assert(false, 'Unsuppported EditType!');
  inherited;
  if Assigned(OnChange) then OnChange(Self);
end;


end.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz