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
domi.neuberger

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

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 15: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
Benutzerbild von Uwe Raabe
Uwe Raabe

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

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 16: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
 
#3

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 16: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 16:25 Uhr)
  Mit Zitat antworten Zitat
Sailor

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

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 1. Jul 2014, 16: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
 
#5

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 2. Jul 2014, 09: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
 
#6

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 2. Jul 2014, 10: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
domi.neuberger

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

AW: Tabellarisches Dateneditieren mit "Typ-Bewusstsein"

  Alt 2. Jul 2014, 11:43
Grund eben gefunden, Ursache aber noch nicht ganz klar:

Sobald TEdit als Parent nicht eine TForm (oder ein TPanel) sondern wie in meinem Fall ein TStringGrid hat, "versackt" das OnChange-Event.
Das ist vermutlich auch der Grund, warum der Formular-Designer verweigert, ein TEdit als "Kind" einem StringGrid zuzuweisen. Im Code ist es erlaubt, und prinzipiell funktioniert's ja auch.

Liegt also gar nicht an meiner Interceptor-Geschichte! Ohne passiert genau das selbe.

Delphi-Quellcode:
procedure TForm3.Edit1Change(Sender: TObject);
begin
  MessageDlg(Edit1.Text, mtInformation, mbOKCancel, 0);
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  Edit1.Parent := self;
  Edit1.Text := 'Mein Papa ist eine Form!//Dialog erscheint
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
  Edit1.Parent := StringGrid1;
  Edit1.Text := 'Mein Papa ist ein Grid!//Dialog erscheint NICHT
end;
  Mit Zitat antworten Zitat
Antwort Antwort

 

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 02:04 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz