Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi VirtualTreeView Editfelder, ComboBox und andere (https://www.delphipraxis.net/188689-virtualtreeview-editfelder-combobox-und-andere.html)

Jens Hartmann 28. Mär 2016 10:03

VirtualTreeView Editfelder, ComboBox und andere
 
Hallo zusammen,

ich versuche jetzt schon den ganzen Vormittag eine vernünftige Beschreibung zu den Möglichkeiten der Einbindung von Edits etc. in ein VirtualStringTree zu finden.

Es wird häufig auf die Advanced Demo des VST und auf die Editors.pas verwiesen. Ich bin allerdings ehrlich, verstehen tue ich das ganze aktuell noch nicht. Es stehen scheinbat auch verschiedene Demos zur Verfügung, die aber nicht mehr mit XE2 laufen.

Kann mir eventuell mal jemand einfach eräutern, wie das funktioniert.

Danke und Gruß Jens

Aviator 28. Mär 2016 10:56

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Hallo Jens,
also im Grunde ist der Verweis auf die Advanced Demo nicht falsch. Dort wird eigentlich alles gezeigt was man wissen muss. Problem hierbei ist allerdings, dass alles sehr stark zerpflückt wurde um die verwendeten Klassen bei mehreren Demos verwenden zu können. Ich hatte damals das gleiche Problem und musste mich da durch kämpfen.

Aber kommen wir mal zum eigentlich wichtigen Teil des Beitrages. Als allererstes solltest du dir eine neue Klasse erstellen die das Interface
Delphi-Quellcode:
IVTEditLink
implementiert. Das Interface ist die Grundlage dafür, dass der Editor überhaupt funktionieren kann. Es sei denn du machst wirklich alles von Hand. Und das willst du ja nicht (wäre aus schwachsinnig).

Die Definition des Interfaces von IVTEditLink steht in der VirtualTrees.pas, trotzdem poste ich diese hier, damit das alles etwas zusammenhängt.

Delphi-Quellcode:
  IVTEditLink = interface
    ['{2BE3EAFA-5ACB-45B4-9D9A-B58BCC496E17}']
    function BeginEdit: Boolean; stdcall;                 // Called when editing actually starts.
    function CancelEdit: Boolean; stdcall;                // Called when editing has been cancelled by the tree.
    function EndEdit: Boolean; stdcall;                   // Called when editing has been finished by the tree. Returns True if successful, False if edit mode is still active.
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
                                                           // Called after creation to allow a setup.
    function GetBounds: TRect; stdcall;                   // Called to get the current size of the edit window
                                                           // (only important if the edit resizes itself).
    procedure ProcessMessage(var Message: TMessage); stdcall;
                                                           // Used to forward messages to the edit window(s)-
    procedure SetBounds(R: TRect); stdcall;               // Called to place the editor.
  end;
Delphi-Quellcode:
  TEditEditLink = class (TInterfacedObject, IVTEditLink)
  private
    FEdit: TEdit;
    FTree : TVirtualStringTree;
    FNode : PVirtualNode;
    FColumn : Integer;
    procedure GetNodeText;
  public
    destructor Destroy; override;
    function BeginEdit: Boolean; virtual; stdcall;
    function CancelEdit: Boolean; virtual; stdcall;
    function EndEdit: Boolean; virtual; stdcall;
    function GetBounds: TRect; virtual; stdcall;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
    procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
    procedure SetBounds(R: TRect); stdcall;
  end;
Ich hoffe ich habe jetzt an dieser Stelle nichts vergessen da ich alles bei mir mit abgeleiteten Klassen gebaut habe. Ich habe mir eine BaseEditor Klasse gebaut wie man sie in der AdvancedDemo auch findet. Diese macht diverse grundlegene Dinge die man sonst bei jeder Art von Komponente noch einmal machen müsste. Kommen wir jetzt zur implementierung der einzelnen Methoden.

In der PrepareEdit Funktion werden zuerst einmal alle wichtigen Komponenten zwischengespeichert mit denen man arbeiten muss. Also der eigentliche Tree von dem der Editvorgang gestartet wurde, die Node die editiert werden soll und abschließen noch die Column in der editiert werden soll.
Delphi-Quellcode:
function TEditEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
begin
  Result := True;
  FTree  := Tree as TVirtualStringTree;
  FNode  := Node;
  FColumn := Column;
end;
Delphi-Quellcode:
function TEditEditLink.BeginEdit: Boolean;
begin
  // Hier kann man diverse Dinge tun, die direkt zu Beginn des Editvorganges gemacht werden sollen
  // Als Beispiel bei einem Editfeld könnte alles selektiert werden
  Self.FEdit.Show;
  Self.FEdit.SetFocus;
  Self.GetNodeText;
  Self.FEdit.SelectAll;
end;
Die Funktion GetNodeText kommt von mir und soll den Text der in der Node steht einfach mal in das neu erstellte Editfeld "kopieren".
Delphi-Quellcode:
procedure TEditEditLink.GetNodeText;
begin
  Self.FEdit.Text := FTree.Text[FNode, FColumn];
end;
Im EndEdit sollten die Änderungen dann gespeichert werden. Ich habe mir jetzt einfach mal einen kleinen Codeausschnitt aus meinen Editorklassen kopiert.
Delphi-Quellcode:
function TBaseDataEditLink.EndEdit: Boolean;
begin
  try
    SaveChanges;
  except
    on e: Exception do
      MessageBox(FTree.Parent.Handle, PChar(e.Message), 'Fehler', MB_OK or MB_ICONERROR or MB_TASKMODAL);
  end;
  Result := True;
end;
In der SetBounds Procedure wird der erstellten Komponente die Größe zugeteilt die diese haben soll. Die GetColumnBounds Procedure verlangt als zweiten Parameter einen Var-Parameter der die linke Position der Column zurückgibt. Ich weiß nicht mehr wieso ich hier eine Dummy Variable benutzt habe, kann sein, dass hier irgendwo sonst was überschrieben wurde. Kannst du aber gerne ändern bzw. ausprobieren.
Delphi-Quellcode:
procedure TBaseDataEditLink.SetBounds(R: TRect);
var
  Dummy : Integer;
begin
  FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
  R.Left := Dummy;// + FTree.Margin * 2;
  FEdit.Width := R.Width;
  R.Bottom := Abs(R.Top) + Abs(FTree.NodeHeight[FTree.FocusedNode]);
  InflateRect(R, 0, 1);
  FEdit.BoundsRect := R;
end;
GetBounds wird von der Treeklasse benötigt, um die "Ränder" der erstellten Komponente zu ermitteln um diese später im Tree richtig zu zeichnen.
Delphi-Quellcode:
function TBaseDataEditLink.GetBounds: TRect;
begin
  Result := FEdit.BoundsRect;
end;
Die ProcessMessages Procedure hatte ich mir auch nur aus der AdvancedDemo herauskopiert. Die soll wohl nur bewirken, dass alle Nachrichten des Trees an den Editor weitergeleitet werden.
Delphi-Quellcode:
procedure TBaseDataEditLink.ProcessMessage(var Message: TMessage);
begin
  if Assigned(FEdit) then
    FEdit.WindowProc(Message);
end;
Die CancelEdit Funktion macht eigentlich das was der Name sagt bzw. wird bei Abbrechen des Editiervorganges ausgeführt.
Delphi-Quellcode:
function TBaseDataEditLink.CancelEdit: Boolean;
begin
  Result := True;
  FEdit.Hide;
end;
Im Destructor noch das Editfeld aufräumen damit nichts mehr im Speicher zurückbleibt.
Delphi-Quellcode:
destructor TBaseDataEditLink.Destroy;
begin
  FEdit.Free;

  inherited Destroy;
end;


So! An dieser Stelle hätten wir unsere Klasse nun fertig sofern ich nichts vergessen habe. Falls doch, einfach darauf hinweisen. Jetzt müssen wir den Editor nur noch in unserem eigentlichen Programm verwenden. Hierzu müssen wir das OnCreateEditor Event des Trees einbinden.


Hier kann noch der Sender, die Column und die Node abgefragt werden um abhängig davon einen entsprechenden Editor zu erzeugen. Der EditLink Parameter ist ein out Parameter und verlangt jetzt eine Instanz eines Interfaces. Hier einfach eine Instanz der vorhin erstellten Klasser erzeugen.
Delphi-Quellcode:
procedure TfrmMain.vstCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  out EditLink: IVTEditLink);
begin
  EditLink := TEditEditLink.Create;
  // Hier sollte eigentlich ein Kommentar stehen den ich jetzt über die Methode gesetzt habe.
end;
Zusätzlich zum OnCreateEditor Event gibt es noch die beiden Events
Delphi-Quellcode:
OnEditing
,
Delphi-Quellcode:
OnEdited
und
Delphi-Quellcode:
OnEditCancelled
. Im
Delphi-Quellcode:
OnEditCancelled
Event kann darauf reagiert werden, was bei Abbruch des Editiervorganges passieren soll. Bspw. kann hier alles wieder auf einen bestimmten Status zurückgesetzt werden oder was auch immer. Im
Delphi-Quellcode:
OnEditing
Event wird eigentlich nur der Start des Editiervorganges mit Hilfe des
Delphi-Quellcode:
Allowed
Parameters erlaubt bzw. verweigert (abhängig von Node und Column und evtl. anderen Faktoren). Und schlussendlich im
Delphi-Quellcode:
OnEdited
Event kann dann der geänderte Status übernommen und gespeichert/verarbeitet werden.


Hoffentlich hat dir diese kleine Einführung geholfen das Thema besser zu verstehen. Ansonsten einfach hier nochmal nachhaken. Ich hatte mich damals auch ewig lange mit diese Thema auseinander gesetzt bis ich es endlich mal verstanden hatte.

Jens Hartmann 28. Mär 2016 20:07

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Aviator,

leider kann ich das noch nicht so ganz nachvollziehen. Ich habe daher im Anhang mal mein eigenes Demo-Projekt angefügt.

Leider bekomme ich die Klasse schon nicht ans laufen und das Editieren ist auch nicht möglich. Beim Compilieren bringt der Compiler mir schon folgende Fehler:

[DCC Fehler] TreeEditors.pas(17): E2137 Methode 'GetNodeText' nicht in Basisklasse gefunden
[DCC Fehler] TreeEditors.pas(17): E2252 Es gibt bereits eine Methode 'GetNodeText' mit identischen Parametern
[DCC Fehler] TreeEditors.pas(18): E2137 Methode 'PrepareEdit' nicht in Basisklasse gefunden
[DCC Fehler] TreeEditors.pas(25): E2137 Methode 'PrepareEdit' nicht in Basisklasse gefunden
[DCC Fehler] TreeEditors.pas(26): E2252 Es gibt bereits eine Methode 'PrepareEdit' mit identischen Parametern
[DCC Fehler] TreeEditors.pas(9): E2291 Implementierung der Interface-Methode IVTEditLink.PrepareEdit fehlt
[DCC Fehler] TreeEditors.pas(9): E2291 Implementierung der Interface-Methode IVTEditLink.SetBounds fehlt
[DCC Fehler] TreeEditors.pas(71): E2136 Definition für abstrakte Methode 'GetNodeText' nicht erlaubt
[DCC Fehler] TreeEditors.pas(73): E2003 Undeklarierter Bezeichner: 'FTree'
[DCC Fehler] TreeEditors.pas(73): E2003 Undeklarierter Bezeichner: 'FNode'
[DCC Fehler] TreeEditors.pas(91): E2003 Undeklarierter Bezeichner: 'SetBounds'
[DCC Fehler] TreeEditors.pas(95): E2003 Undeklarierter Bezeichner: 'FTree'
[DCC Fehler] TreeEditors.pas(95): E2066 Operator oder Semikolon fehlt
[DCC Fehler] TreeEditors.pas(95): E2003 Undeklarierter Bezeichner: 'FColumn'
[DCC Fehler] TreeEditors.pas(95): E2003 Undeklarierter Bezeichner: 'R'
[DCC Fehler] TreeEditors.pas(15): E2065 Ungenügende Forward- oder External-Deklaration: 'TEditEditLink.GetNodeText'
[DCC Fehler] TreeEditors.pas(18): E2065 Ungenügende Forward- oder External-Deklaration: 'TEditEditLink.PrepareEdit'
[DCC Hinweis] TreeEditors.pas(15): H2219 Das private-Symbol 'GetNodeText' wurde deklariert, aber nie verwendet
[DCC Fataler Fehler] fMain.pas(7): F2063 Verwendete Unit 'TreeEditors.pas' kann nicht compiliert werden

Aviator 28. Mär 2016 21:21

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Also im Prinzip hast du es schon richtig aufgebaut. Aber oben in deinen Methodendeklarationen hast du einige Fehler eingebaut die ich dir dooferweise in meinen Beitrag hineingeschrieben habe. Ich hatte mir das wie gesagt aus mehreren Klassen wieder zu einer zusammengeklickt und dadurch nicht gemerkt, dass die Methoden schon deklariert waren. Habe auch kein Testprogramm geschrieben um die von mir gepostete Klasse noch einmal zu testen.

Hier mal der Klassenheader mit den Methodendeklarationen (ich werde den in meinem Beitrag auch ersetzen):

Delphi-Quellcode:
  TEditEditLink = class (TInterfacedObject, IVTEditLink)
  private
    FEdit: TEdit;
    FTree : TVirtualStringTree;
    FNode : PVirtualNode;
    FColumn : Integer;
    procedure GetNodeText;
  public
    destructor Destroy; override;
    function BeginEdit: Boolean; virtual; stdcall;
    function CancelEdit: Boolean; virtual; stdcall;
    function EndEdit: Boolean; virtual; stdcall;
    function GetBounds: TRect; virtual; stdcall;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
    procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
    procedure SetBounds(R: TRect); stdcall;
  end;
Und das hier muss noch in die PrepareEdit Funktion hinein:

Delphi-Quellcode:
  Self.FEdit := TEdit.Create(Self.FTree);
  Self.FEdit.Visible := False;
  Self.FEdit.Parent := Self.FTree;
Ansonsten bekommst du beim Ausführen eine AV wegen einer nicht erzeugten Komponente.

jaenicke 29. Mär 2016 05:46

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Ein guter Ausgangspunkt sind auch die vorgefertigten Editoren für TEdit, TMemo, TSpinEdit, TDateTimePicker und TCheckBox:
http://www.soft-gems.net/index.php/c...rtual-treeview
(ganz unten "Virtual Treeview (editors)")

Die sind zwar schon länger nicht mehr aktualisiert, aber bis vor dem Split der VirtualTrees in mehrere Units funktionierte es damit noch problemlos, jetzt muss vermutlich auch kaum mehr als die uses angepasst werden.

Warum das nicht offiziell mitgepflegt wird, weiß ich nicht. Sinnvoll wäre es.

Aviator 29. Mär 2016 12:43

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von jaenicke (Beitrag 1334028)
Ein guter Ausgangspunkt sind auch die vorgefertigten Editoren für TEdit, TMemo, TSpinEdit, TDateTimePicker und TCheckBox:
http://www.soft-gems.net/index.php/c...rtual-treeview
(ganz unten "Virtual Treeview (editors)")

[...]

Warum das nicht offiziell mitgepflegt wird, weiß ich nicht. Sinnvoll wäre es.

Da gebe ich dir recht. Wäre wirklich sinnvoll die dann immer anzupassen und auch auf der neuen HomePage als Download zur Verfügung zu stellen. Ich habe mir mittlerweile allerdings meine eigenen Basisklassen erstellt und die auch zum VST gepackt als VirtualTrees.BaseEditors Unit.

Deshalb hatte ich auch im ersten Beitrag ein paar "Schwierigkeiten" da wieder eine funktionierende Klasse zusammenzubasteln. Hatte die wie gesagt nicht getestet und alles nur hier im Editor zusammengebaut. Aber die Demo die ich angehängt hatte sollte jetzt zumindest mal funktionieren.

jaenicke 29. Mär 2016 13:53

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Es ist ja ein Open Source Projekt. Insofern sollte es ja möglich sein dies zu ergänzen...
Leider werde ich kaum die Zeit finden...
Aber mal schauen, vielleicht geht es ja schnell die alten Units anzupassen.

Oder hättest du vielleicht Interesse und die Möglichkeit (lizenzrechtlich meine ich, falls du den nicht privat geschrieben hast) den Quelltext dort zur Verfügung zu stellen, vor allem wenn der noch mehr kann als die alte Demo?

Aviator 29. Mär 2016 16:29

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von jaenicke (Beitrag 1334058)
Es ist ja ein Open Source Projekt. Insofern sollte es ja möglich sein dies zu ergänzen...
Leider werde ich kaum die Zeit finden...
Aber mal schauen, vielleicht geht es ja schnell die alten Units anzupassen.

Oder hättest du vielleicht Interesse und die Möglichkeit (lizenzrechtlich meine ich, falls du den nicht privat geschrieben hast) den Quelltext dort zur Verfügung zu stellen, vor allem wenn der noch mehr kann als die alte Demo?

Also wenn ich ehrlich bin, dann habe ich mir die Funktionen der Editoren noch nie wirklich angesehen. Insofern kann ich nicht sagen ob meine Klassen mehr können als die schon existierenden. Ich scheue mich auch normalerweise nicht vor Arbeit (auch in diesem Fall nicht) nur hätte ich sehr wenig Zeit, diese Editoren immer auf dem neuesten Stand zu halten. Noch dazu arbeite ich auch nicht immer direkt mit den neuesten Versionen des VST sondern öfters auch mal noch mit einer alten Version. Aber ich werde mir die Demos mal bei Gelegenheit anschauen und kucken was sich da machen lässt. Habe allerdings zur Zeit privat auch ein größeres Projekt am Laufen bei dem ich gerne weiter kommen würde. :?

Jens Hartmann 29. Mär 2016 22:53

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Einen schönen guten Abend zusammen,

jetzt habe ich das ganze mal an Hand der vorgenannten Unterlagen und der Andvanced Demo (editors.pas und properties Forms) mir selbst zusammen gebastelt. Allerdings klappt dass mit dem Editieren absoulte noch gar nicht.

Anbei mal meine aktuell Version.

Gruß Jens

Aviator 29. Mär 2016 23:09

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1334101)
jetzt habe ich das ganze mal an Hand der vorgenannten Unterlagen und der Andvanced Demo (editors.pas und properties Forms) mir selbst zusammen gebastelt. Allerdings klappt dass mit dem Editieren absoulte noch gar nicht.

Ich glaube es wäre sinnvoll, wenn du mal alle deine Einschränkungen rausnehmen würdest die du eingebaut hast. Setz doch einfach Allowed in vstEditing für den Anfang immer auf True um dir diesen Fehler schonmal vom Hals zu schaffen. Und dann im PrepareEdit deiner Editor Klasse würde ich an deiner Stelle immer erst mal ein normales Editfeld erzeugen statt den NodeType abzufragen den du für die Nodes auf RootEbene gar nicht setzt. Und wenn du Children haben willst, dann müsstest du auch noch das InitChildren Event implementieren um deinen Nodes entsprechend die Anzahl der ChildNodes zuzuordnen. Zumindest dann wenn du alles über InitNode und InitChildren machen willst. Für den Beginn würde ich einfach mal mit
Delphi-Quellcode:
vst.AddNode(nil)
arbeiten damit du dich mal mit dem Tree ein bisschen auseinandersetzt. Ich will jetzt nichts falsches sagen, aber es kommt mir so vor als hättest du noch nicht wirklich viel mit dem VirtualTreeView gearbeitet und die Funktionen noch nicht so ganz verstanden. Ich glaube es wäre sinnvoll, wenn du dich erst einmal mit den Grundfunktionen auseinander setzt und dir mal alle Funktionen und Events anschaust die es gibt. So ganz ohne Grundkenntnisse über den Tree kommt man bei dieser Komponente nicht weit.

Aber bitte korrigiere mich wenn ich falsch liege. Ich habe auf jeden Fall kurz in dein Projekt reingeschaut und würde das alles etwas vereinfachen um nicht allzuviel einzuschränken. Lass erst mal alles zu und dann fang an einzuschränken wenn dein Editor funktioniert. Wenn ich die Zeit finde, dann stelle ich dir ein kleines Beispielprojekt zusammen und versuche das relativ einfach zu halten und evtl. auch noch den Code zu erklären. Komme da wohl aber erst am Wochenende dazu.

Jens Hartmann 31. Mär 2016 14:44

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Hallo Aviator,

Danke erstmal. Mach Dir aktuell mal keine Arbeit. Ich habe es fast gelöst. Stelle kürzlich ein Musterprojekt ein.

Eines fehlt mir aktuell noch. Im EditKeyDown, will ich mit der TAB Taste in die nächste Spalte (Column) springen und diese in den Editmode versetzen. Hierzu vermisse ich eine Methode wie NextVisibleColumn. Das selbe soll dann mit den Taste Links/Rechts passieren...

Delphi-Quellcode:
case Key of
  TAB:
     begin
     ....
     end;
   VK_Right, VK_Left:
     begin
     ...
     end;
Vielleicht hat hier noch jemand nee Idee...

Gruß Jens

Aviator 31. Mär 2016 17:08

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1334337)
Eines fehlt mir aktuell noch. Im EditKeyDown, will ich mit der TAB Taste in die nächste Spalte (Column) springen und diese in den Editmode versetzen

Also ich musste, damit das EndEdit des Trees sauber funktionierte (besonders in Verbindung mit der Return Taste) eine Message mit
Delphi-Quellcode:
PostMessage()
senden und im Formular mit dem VST darauf reagieren. Beim Drücken von Return schmierte das Programm nämlich mit einer AV ab, was auf den Tree zurückzuführen war und nur damit umgangen werden konnte. Wie es bei der ganz neuen Version aussieht weiß ich nicht, da ich aktuell noch die Version 5.5 verwende. Vielleicht wurde der Fehler darin ja behoben. Evtl. könntest du das auch so machen und dann beim PostMessage im Parameter den Key bzw. einen Wert mitgeben an dem du erkennst, dass du in die nächste Spalte springen willst und dort direkt wieder ein
Delphi-Quellcode:
BeginEdit(Node, Column)
ausführst. Eine andere evtl. schönere Lösung habe ich jetzt auf Anhieb beim Schreiben nicht parat. Vielleicht würde mir da noch etwas besseres einfallen, nur hatte ich das bisher eigentlich nicht gebraucht.

Jens Hartmann 31. Mär 2016 19:23

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Also das mit der "Enter" Taste funktioniert schon ganz gut.

Delphi-Quellcode:
procedure TEditEditLink.EditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  ANode : PVirtualNode;
begin
  case Key of
    VK_ESCAPE:
      begin
        Key := 0;
      end;
    VK_RETURN:
      begin
        FTree.InvalidateNode(FNode);
        if (ssShift in Shift) then
          ANode := FTree.GetPreviousVisible(FNode, True)
        else
          ANode := FTree.GetNextVisible(FNode, True);
        FTree.EndEditNode;
        if ANode <> nil then FTree.FocusedNode := ANode;
        Key := 0;
        if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
          TVirtualStringTreeHack(FTree).DoEdit;
      end;
    VK_TAB:
      begin

      end;
    VK_LEFT,
    VK_RIGHT:
      begin

      end;
    VK_UP,
    VK_DOWN:
      begin
        FTree.InvalidateNode(FNode);
        if Key = VK_UP then
          ANode := FTree.GetPreviousVisible(FNode, True)
        else
          ANode := FTree.GetNextVisible(FNode, True);
        FTree.EndEditNode;
        if ANode <> nil then FTree.FocusedNode := ANode;
          Key := 0;
        if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
          TVirtualStringTreeHack(FTree).DoEdit;
      end;
  end;
end;

Jens Hartmann 31. Mär 2016 22:06

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,

im Anhang mal das aktuelle Musterprojekt. Das Steuern mit den Tasten "Enter", "Shift & Enter" und "Auf & Ab" funktioniert schon mal ganz gut. Auch die verschiedenen Editoren (TEdit, TComboBox) gehen.

Das mit der "TAB-Taste" und den "Tasten Rechts & Links" bekomme ich allerdings nicht gelöst.

Jemand eine Idee.

Ansonsten wäre ich über Kommentare zum Musterprojekt dankbar.

Danke schon mal, einen schönen Abend und Gruß Jens

Aviator 1. Apr 2016 08:56

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1334373)
Delphi-Quellcode:
procedure TEditEditLink.EditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  ANode : PVirtualNode;
begin
  case Key of
    [...]
    VK_RETURN:
      begin
        [...]
        if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
          TVirtualStringTreeHack(FTree).DoEdit;        // Warum machst du das hier????
      end;
    [...]
  end;
end;

Warum machst du das im Code markierte mit einem Hack? Ruf doch einfach die Methode
Delphi-Quellcode:
FTree.EditNode(Node, Column)
auf und gut ist. Sollte genauso funktionieren, nur sauberer. Habe mir das Projekt jetzt nicht runtergeladen und getestet ob es wirklich funktioniert.

Zu der Sache mit der Tab Taste vermute ich, dass das Drücken von Tab das Control verlässt und du ggf. sogar im EditMode der aktuellen Node bleibst. Hier müsstest du (wahrscheinlich im KeyPress Event) die Tab Taste abfangen, sodass diese nicht vom System weiterverarbeitet wird.

Jens Hartmann 1. Apr 2016 12:34

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Delphi-Quellcode:
           
if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
  FTree.EditNode(FTree.FocusedNode, FTree.FocusedColumn);
Hab ich angepasst. Das mit dem Hack, habe ich aus einem Lazarus Forum.

Die TAB Taste, bekomme ich aber auch nicht im KeyPress abgefangen.

Jens Hartmann 2. Apr 2016 07:02

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Also das Ding bringt mich noch zur Verzweiflung. Es muss doch möglich sein, eine Komponente wie das VST mit der Tastatur annähernd wie Excel bedienen zu können.

Das mit dem "Enter" führt übrigens mittlerweile (nach dem Übernehmen der Daten in den Node) auch zu einer Exception.

Hier mal der aktuelle Editor...

Delphi-Quellcode:
unit TreeEditors;

interface

uses
  VirtualTrees, Vcl.StdCtrls, Winapi.Windows, System.SysUtils, Winapi.Messages, System.Classes, Vcl.Controls,
  Vcl.Forms, Vcl.Dialogs;

//Eigener Type für die verschiedenen Editfelder (
type
  TEditValueType =
    (evtNone { Kein Editiern möglich },
     evtNumber { Nur Zahlen - Ein TEdit mit "NumbersOnly = true },
     evtString { Text - Ein TEdit },
     evtPickString { Text mit fester Auswahlmöglichkeit - TComboBox }
     );

//Eigene Klasse zur Haltung der Daten
type
  TOMyClass = class
    private
      FAInt : integer;       //Spalte 1 im VST - eine Zahl
      FAPickString : string; //Spalte 2 im VST - Eine Text mit fester Auswahlmöglichkeit
      FAString : string;     //Spalte 3 im VST - Eine Text
      FBString : string;     //Spalte 4 im VST - Eine Text
      FBPickString : string; //Spalte 5 im VST - Eine Text mit fester Auswahlmöglichkeit
    public
      property AInt: integer read FAInt write FAInt;
      property APickString : string read FAPickString write FAPickString;
      property AString : string read FAString write FAString;
      property BString : string read FBString write FBString;
      property BPickString : string read FBPickString write FBPickString;
end;

//Der Datenrecord für das VST
type
  PMyData = ^TMyData;
  TMyData = record
    FValueType: array[0..4] of TEditValueType; //Die Var für den Typ des Editierfeld
    FObject : TObject;                        //Das Objekt der Daten
    FChanged : Boolean;
end;

//Die Interface-Klasse für den Editor
type
  TEditEditLink = class (TInterfacedObject, IVTEditLink)
  private
    FEdit: TWinControl;          //FEdit als TWinControl für die verschiedenen Editormöglichkeiten (TEdit, TComboBox etc.)
    FTree : TVirtualStringTree;  //Das VST
    FNode : PVirtualNode;        //Das Node
    FColumn : Integer;           //Der Column
  protected
    procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); //Für die Steuerung der Editorfelder über Tastatur
    procedure EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);   //Für die Steuerung der Editorfelder über Tastatur
  public
    destructor Destroy; override; //Zur Freigabe der Editor-Controls beim beenden

    function BeginEdit: Boolean; virtual; stdcall;   //Start Editorvorgang
    function CancelEdit: Boolean; virtual; stdcall;  //Abbruch Editorvorgang
    function EndEdit: Boolean; virtual; stdcall;     //Ende Editorvorgang
    function GetBounds: TRect; virtual; stdcall;     //Größe ermitteln
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; //Erstellen der jeweiligen Editor - Controls
    procedure ProcessMessage(var Message: TMessage); virtual; stdcall; //Für die Übergabe der Nachrichten des VST an den Editor
    procedure SetBounds(R: TRect); stdcall; //Größe der Editorfelder setzen
  end;

implementation

uses
  fMain;

{ TEditEditLink }

function TEditEditLink.BeginEdit: Boolean;
begin
  //Prüfung um welches Control es sich handelt und entsprechend aktivieren
  if FEdit is TEdit then
      begin
        FEdit.Show;                //Anzeigen
        FEdit.SetFocus;            //Focus zuweisen
        TEdit(FEdit).SelectAll;    //Text im TEdit komplett selektieren
      end
  else if FEdit is TComboBox then
      begin
        FEdit.Show;                //Anzeigen
        FEdit.SetFocus;            //Focus zuweisen
        TComboBox(FEdit).SelectAll; //Text im TEdit komplett selektieren
      end;
end;

function TEditEditLink.CancelEdit: Boolean;
begin
  Result := True;                  //Abbruch erfolgt
  FEdit.Hide;                      //Control auf Visible false
end;

destructor TEditEditLink.Destroy;
begin
  FEdit.Free;                      //Control nach beenden des Editors wieder freigeben
  inherited;
end;

procedure TEditEditLink.EditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  ANode : PVirtualNode;
  CanAdvance : Boolean;
  AColumn : TColumnIndex;
begin
  case Key of
    VK_ESCAPE:
      begin
        Key := 0;
      end;
    VK_RETURN:
      begin
        if FEdit is TEdit then
          begin
            FTree.InvalidateNode(FNode);
            if (ssShift in Shift) then
              ANode := FTree.GetPreviousVisible(FNode, True)
            else
              ANode := FTree.GetNextVisible(FNode, True);
            FTree.EndEditNode;
            if ANode <> nil then FTree.FocusedNode := ANode;
            Key := 0;
            if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
              FTree.EditNode(FTree.FocusedNode, FTree.FocusedColumn);
          end
        else
        if FEdit is TComboBox then
          begin
            FTree.InvalidateNode(FNode);
            if (ssShift in Shift) then
              ANode := FTree.GetPreviousVisible(FNode, True)
            else
              ANode := FTree.GetNextVisible(FNode, True);
            FTree.EndEditNode;
            if ANode <> nil then FTree.FocusedNode := ANode;
            Key := 0;
            if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
              FTree.EditNode(FTree.FocusedNode, FTree.FocusedColumn);
          end;
      end;
    VK_TAB:
      begin

      end;
    VK_LEFT,
    VK_RIGHT:
      begin

      end;
    VK_UP,
    VK_DOWN:
      begin
        if FEdit is TEdit then
          begin
            FTree.InvalidateNode(FNode);
            if Key = VK_UP then
              ANode := FTree.GetPreviousVisible(FNode, True)
            else
              ANode := FTree.GetNextVisible(FNode, True);
            FTree.EndEditNode;
            if ANode <> nil then FTree.FocusedNode := ANode;
              Key := 0;
            if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
              FTree.EditNode(FTree.FocusedNode, FTree.FocusedColumn);
          end
        else
        if FEdit is TComboBox then
          begin
            TComboBox(FEdit).DroppedDown := True;
          end;

      end;
  end;
end;

procedure TEditEditLink.EditKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_ESCAPE:
      begin
        FTree.CancelEditNode;
        Key := 0;
      end;
  end;
end;

function TEditEditLink.EndEdit: Boolean;
var
  Data: PMyData;
  Buffer: array[0..1024] of Char;
  S: UnicodeString;
  I: Integer;
begin
  Result := True;
  Data := FTree.GetNodeData(FNode);
  case FColumn of
    0:
    begin
      S := TEdit(FEdit).Text;
      if S <> IntToStr(TOMyClass(Data.FObject).FAInt) then
        begin
          TOMyClass(Data.FObject).FAInt := StrToInt(S);
          Data.FChanged := True;
        end;
    end;
    1:
    begin
      S := TComboBox(FEdit).Text;
      if S <> TOMyClass(Data.FObject).FAPickString then
        begin
          TOMyClass(Data.FObject).FAPickString := S;
          Data.FChanged := True;
        end;
    end;
    2:
    begin
      S := TEdit(FEdit).Text;
      if S <> TOMyClass(Data.FObject).FAString then
        begin
          TOMyClass(Data.FObject).FAString := S;
          Data.FChanged := True;
        end;
    end;
    3:
    begin
      S := TEdit(FEdit).Text;
      if S <> TOMyClass(Data.FObject).FBString then
        begin
          TOMyClass(Data.FObject).FBString := S;
          Data.FChanged := True;
        end;
    end;
    4:
    begin
      S := TComboBox(FEdit).Text;
      if S <> TOMyClass(Data.FObject).FBPickString then
        begin
          TOMyClass(Data.FObject).FBPickString := S;
          Data.FChanged := True;
        end;
    end;
  end;
  if Data.FChanged then
    begin
      FTree.InvalidateNode(FNode);
      { z.B. zusätzlich Update Datenbank }
    end;
  FEdit.Hide;
end;

function TEditEditLink.GetBounds: TRect;
begin
  Result := FEdit.BoundsRect; //Größe ermitteln
end;

function TEditEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex): Boolean;
var
  Data: PMyData;
  FValueType : TEditValueType;
begin
  Result := True;
  FTree := Tree as TVirtualStringTree;
  FNode := Node;
  FColumn := Column;

  FEdit.Free;
  FEdit := nil;

  Data := FTree.GetNodeData(FNode);

  FValueType := Data.FValueType[FColumn];

  case FValueType of
    evtNumber:
      begin
        FEdit := TEdit.Create(FTree);
        TEdit(FEdit).OnKeyDown := EditKeyDown;
        TEdit(FEdit).OnKeyUp := EditKeyUp;
        TEdit(FEdit).Text := IntToStr(TOMyClass(Data.FObject).FAInt);
        FEdit.Visible := False;
        FEdit.Parent := FTree;
      end;
    evtString:
      begin
        FEdit := TEdit.Create(FTree);
        TEdit(FEdit).OnKeyDown := EditKeyDown;
        TEdit(FEdit).OnKeyUp := EditKeyUp;
        case FColumn of
          2: TEdit(FEdit).Text := TOMyClass(Data.FObject).FAString;
          3: TEdit(FEdit).Text := TOMyClass(Data.FObject).FBString;
        end;
        FEdit.Visible := False;
        FEdit.Parent := FTree;
      end;
    evtPickString:
      begin
        FEdit := TComboBox.Create(FTree);
        TComboBox(FEdit).OnKeyDown := EditKeyDown;
        TComboBox(FEdit).OnKeyUp := EditKeyUp;
        FEdit.Visible := False;
        FEdit.Parent := FTree;
        case FColumn of
          1:
            begin
              TComboBox(FEdit).Text := TOMyClass(Data.FObject).FAPickString;
              TComboBox(FEdit).Items.Add('Listenauswahl Text 1');
              TComboBox(FEdit).Items.Add('Listenauswahl Text 2');
              TComboBox(FEdit).Items.Add('Listenauswahl Text 3');
              TComboBox(FEdit).Items.Add('Listenauswahl Text 4');
              TComboBox(FEdit).Items.Add('Listenauswahl Text 5');
            end;
          4:
            begin
              TComboBox(FEdit).Text := TOMyClass(Data.FObject).FBPickString;
              TComboBox(FEdit).Items.Add('Zweite Auswahl Text 1');
              TComboBox(FEdit).Items.Add('Zweite Auswahl Text 2');
              TComboBox(FEdit).Items.Add('Zweite Auswahl Text 3');
            end;
        end;
      end
    else
      begin
        Result := False;
      end;
  end;
end;

procedure TEditEditLink.ProcessMessage(var Message: TMessage);
begin
  if Assigned(FEdit) then
    FEdit.WindowProc(Message);
end;

procedure TEditEditLink.SetBounds(R: TRect);
var
  Dummy : Integer;
begin
  FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
  R.Left := Dummy;// + FTree.Margin * 2;
  FEdit.Width := R.Width;
  R.Bottom := Abs(R.Top) + Abs(FTree.NodeHeight[FTree.FocusedNode]);
  InflateRect(R, 0, 1);
  FEdit.BoundsRect := R;
end;

end.
Ich will ja eigendlich nur folgende Funktionen:

VK_ENTER - eine Zeile nach unten
VK_ENTER/SHIFT - eine Zeile nach oben
VK_TAB -eine Spalte nach Links
VK_TAB/Shift - eine Spalte nach Rechts
VK_UP - eine Zeile nach oben
VK_DOWN - eine Zeile nach unten
VK_LEFT - eine Spalte nach links
VK_RIGHT - eine Spalte nach links

blawen 2. Apr 2016 10:50

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von jaenicke (Beitrag 1334028)
Warum das nicht offiziell mitgepflegt wird, weiß ich nicht. Sinnvoll wäre es.

Zitat vom 13. Juni 2015
Zitat:

With the release 5.4.0 my part on Virtual Treeview's development is over.
It's now maintained by JAM-Software

Aviator 2. Apr 2016 11:39

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von blawen (Beitrag 1334504)
Zitat:

Zitat von jaenicke (Beitrag 1334028)
Warum das nicht offiziell mitgepflegt wird, weiß ich nicht. Sinnvoll wäre es.

Zitat vom 13. Juni 2015
Zitat:

With the release 5.4.0 my part on Virtual Treeview's development is over.
It's now maintained by JAM-Software

Das ist schon klar, aber wer sagt denn, dass die das nicht auch pflegen könnten. Die Editors wurden ja auch nicht von Mike Lischke selbst entwickelt sondern von irgendjemand anderen.

haentschman 2. Apr 2016 12:19

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Jens...:P

Um dir weiteren Frust zu ersparen hätte ich noch einen Alternativvorschlag. Wenn es denn nicht unbedingt die "im Grid editiererei" sein muß kannst du auch folgendes probieren. (Siehe Bild)

1. Die Reihen im Grid (hier Listview) sind quasi immer Readonly.
2. Zum Editieren ein Fenster ohne Rahmen modal auf die Zeile legen. -> Das Editieren kann nur definiert verlassen werden (Save oder Abbruch).
3. Im Design ist man völlig unabhängig von der Grideinteilung obwohl man z.B. die obere Zeile dem Grid nachempfinden kann.
4. So lassen sich auch komplexe Datenobjekte, welche an dem Listeneintrag hängen, bearbeiten ohne massig Spalten zu haben.
5. Man kann auf einer "Kopie" des Datenobjektes arbeiten und nur beim Save die Informationen übertragen.
...was vergessen? :gruebel: Nö. :P
doch:
6. Man kann alle Vorzüge eines Formulares nutzen incl. Tab Reihenfolgen, ENTER zum nächsten Control etc.
7. Die Editoren erben alle von einem Basiseditor mit den Basisfunktionalitäten, Events etc. und Buttons.
8. Irgendwann willst du ein anderes Control benutzen...kein Problem. Der Editor bleibt was er ist. :zwinker:

Jens Hartmann 2. Apr 2016 14:26

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo haentschman,

Danke für den Tip. Das ist auch meine aktuelle Lösung. Zwar nicht so hübsch wie bei Dir, aber siehe hierzu auch den Screenshot.

Ich hätte es halt nur gerne anders gelöst, und dachte, dass das VST das können muss.

Trotzdem danke. Ich denke, ich werde mein Formular ein wenig aufhübschen und es dann so realisieren.

Gruß Jens

haentschman 2. Apr 2016 17:17

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Gern geschehen...8-)
Ich denke, das wichtigste an diesem Konstrukt ist die Unabhängigkeit vom "Parent" darunter. Wie man das dann gestaltet ist die Kür. :P

Jens Hartmann 2. Apr 2016 21:19

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Hallo haentschman,

wie Positionierst Du die Form auf dem VST.

Ich habe das ganze wie folgt versucht, die Position passt aber nicht...

Delphi-Quellcode:
var
  CellRect : TRect;
  Node : PVirtualNode;
begin
...
  CellRect := vst.GetDisplayPosition(Node, Column, false);
  fForm2.Top := CellRect.Top;
  fForm2.Left := CellRect.Left;
  fForm2.ShowModal;
end;

haentschman 3. Apr 2016 06:19

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Moin...:P

aktuell habe ich noch eine TAdvListview drunter. Die Umstellung auf VT steht auf der TodoListe. :wink:

Vieleicht können dir die Brocken ein paar Ansätze liefern. :zwinker:

1. Positionierung Form
Delphi-Quellcode:
class procedure TdTools.SetEditorPositions(ParentListView: TAdvListView; Editor: TForm);
var
  I: Integer;
  CurrentPoint: TPoint;
  CurrentTop: Integer;
begin
  CurrentPoint:= ParentListview.ClientToScreen(Point(30,0)); // 30 = um erste Spalte eingerückt
  CurrentTop:= CurrentPoint.Y + ParentListview.Selected.Top; // beim Listview hat der selektierte Eintrag Top... VT ?
  if (CurrentTop + Editor.Height) > Screen.WorkAreaHeight then // klappt den Editor entweder nach unten oder nach oben wenn er nicht mehr auf den Screen paßt
  begin
    CurrentTop:= (CurrentTop + ParentListview.ItemHeight) - Editor.Height + 4;
  end;
  Editor.Top:= CurrentTop;
  Editor.Left:= CurrentPoint.X;
  Editor.Width:= 0;
  for I := 1 to ParentListView.Columns.Count -1 do
  begin
    Editor.Width:= Editor.Width + ParentListView.Column[I].Width;
  end;
  Editor.Width:= Editor.Width + conEditorOffsetWidth; // Offset = Feinjustierung für Liniendicke des Grids
end;
2. Im constructor bekommt der Editor(Form) den Parent mit:
Delphi-Quellcode:
constructor TfoInlineEditorProfiles.Create(Preferences: TdVAPreferences; Database: IdVA_Database; Profile: TProfile; ParentListview: TAdvListView);
begin
  inherited Create(nil);
  FLogic:= TInlineEditorProfile.Create(Preferences, Database, Profile); // die BL zum Form
  FLogic.OnGetProfileGroups:= DoOnGetProfileGroups;
  FLogic.OnGetParameters:= DoOnGetParameters;
  FLogic.GetData;

  pgrProfileProperties.ActivePageIndex:= 0;
  tvDevices.Width:= FLogic.Preferences.PositionsVA.SplitterEditorProfiles;
  TdTools.SetControlPosition(ParentListview, 1, True, edtProfileNameLong, daLeft); // Positionierung der 1. Reihe auf die Spalten
  TdTools.SetControlPosition(ParentListview, 2, True, cbbProfileGroup, daLeft);
  TdTools.SetControlPosition(ParentListview, 3, True, cbbBatchJob, daLeft);
  TdTools.SetControlPosition(ParentListview, 4, True, cbbVisualType, daLeft);
  TdTools.SetControlPosition(ParentListview, 5, True, cbbActive, daLeft);
  if FLogic.Preferences.PositionsVA.SplitterEditorProfiles > pnlDevices.Width then
  begin
    pnlDevices.Width:= FLogic.Preferences.PositionsVA.SplitterEditorProfiles;
  end
  else
  begin
    FLogic.Preferences.PositionsVA.SplitterEditorProfiles:= pnlDevices.Width;
  end;
end;
3. Positionierung auf Spalten
Delphi-Quellcode:
class procedure TdTools.SetControlPosition(ParentListView: TAdvListView; ColumnIndex: Integer; SetWidth: Boolean; Control: TControl; Align: TdAlign);
var
  I: Integer;
begin
  // Todo: Berücksichtigung wenn Control größer als Column
  Control.Left:= 0;

  for I := 1 to ColumnIndex do
  begin
    if I = 1 then
    begin
      Control.Left:= Control.Left;
    end
    else
    begin
      Control.Left:= Control.Left + ParentListView.Column[I - 1].Width;
    end;
    if (I = ColumnIndex) and not (ColumnIndex = 1) then
    begin
      Control.Left:= Control.Left - 1;
    end;
  end;

  if SetWidth then
  begin
    Control.Width:= ParentListView.Column[ColumnIndex].Width + 1;
    if Control is TAdvSmoothComboBox then
    begin
      TAdvSmoothComboBox(Control).DropDownWidth:= Control.Width;
    end;
  end;

  case Align of
    daLeft: Control.Left:= Control.Left;
    daRight: Control.Left:= Control.Left + (ParentListView.Column[ColumnIndex].Width - Control.Width);
    daCenter: Control.Left:= Control.Left + ((ParentListView.Column[ColumnIndex].Width - Control.Width) div 2);
  end;
end;

Aviator 4. Apr 2016 15:44

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1334514)
Hallo haentschman,
Danke für den Tip. Das ist auch meine aktuelle Lösung. Zwar nicht so hübsch wie bei Dir, aber siehe hierzu auch den Screenshot.
Ich hätte es halt nur gerne anders gelöst, und dachte, dass das VST das können muss.
Trotzdem danke. Ich denke, ich werde mein Formular ein wenig aufhübschen und es dann so realisieren.

Schade das du so früh schon "aufgibst". Ich hatte bestimmt zwei Wochen benötigt um mal eine halbwegs funktionierende Lösung zu haben. Und da ging dann noch nix mit unterschiedlichen Editoren für unterschiedliche Spalten. Ich bin leider noch nicht dazu gekommen ein Beispiel zusammen zu basteln was evtl. sogar dokumentiert ist im es besser zu verstehen. Des Weiteren meintest du ja, ich soll mir die Arbeit sparen. Aber wenn trotzdem noch Interesse ab der VST Lösung besteht, dann schau ich mir das mal an.

Jens Hartmann 4. Apr 2016 19:30

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

*Zitat von*Jens Hartmann:
Hallo haentschman,
Danke für den Tip. Das ist auch meine aktuelle Lösung. Zwar nicht so hübsch wie bei Dir, aber siehe hierzu auch den Screenshot.
Ich hätte es halt nur gerne anders gelöst, und dachte, dass das VST das können muss.
Trotzdem danke. Ich denke, ich werde mein Formular ein wenig aufhübschen und es dann so realisieren.

Zitat:

*Zitat von*Aviator:
Schade das du so früh schon "aufgibst". Ich hatte bestimmt zwei Wochen benötigt um mal eine halbwegs funktionierende Lösung zu haben. Und da ging dann noch nix mit unterschiedlichen Editoren für unterschiedliche Spalten. Ich bin leider noch nicht dazu gekommen ein Beispiel zusammen zu basteln was evtl. sogar dokumentiert ist im es besser zu verstehen. Des Weiteren meintest du ja, ich soll mir die Arbeit sparen. Aber wenn trotzdem noch Interesse ab der VST Lösung besteht, dann schau ich mir das mal an.

Ich hab doch noch gar nicht aufgegeben. Ich hatte nur meine aktuelle Lösung aufgezeigt. Die existiert ja bereits.
Aber leider bin ich was dass TAB und LEFT/RIGHT Thema angeht noch nicht wirklich weiter.

Jens Hartmann 7. Apr 2016 22:08

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Also so richtig weiter gekommen bin ich bislang noch. Noch jemand einen Vorschlag?

Aviator 8. Apr 2016 18:49

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Jens Hartmann (Beitrag 1334978)
Also so richtig weiter gekommen bin ich bislang noch. Noch jemand einen Vorschlag?

Also ich habe da jetzt mal was zusammengebastelt und hoffe, dass es dir weiter hilft. Im Prinzip ist das jetzt ein Konstrukt, dass du für alles immer wieder verwenden könntest wenn du willst. Speziell in diesem Programm habe ich die Funktion mit Pfeiltaste hoch, runter, links und rechts implementiert. Auch Enter führt dazu, dass die nächste Node unterhalb der aktuellen ausgewählt und direkt editiert wird. Die Sache mit der Tab-Taste habe ich nur beim ersten Mal beachtet da dies einfach nur ein(e) Haken/Property beim VirtualStringTree ist (WantsTabs). Sobald diese Property auf True steht, wird allerdings nur eine Message abgefangen und entsprechen "modifiziert". Das könnte man dan dementsprechend in den abgeleiteten Editorklassen einbauen. War ich jetzt aber zu faul zu. :stupid:

Das hier ist die Methode die bewirkt, dass beim Drücken der Tab-Taste die Komponente nicht verlassen wird. Das kann man dann ja analog dazu in seiner eigenen Editorklasse auch machen und stattdessen in die nächste Column springen.
Delphi-Quellcode:
interface
[...]
  procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
[...]
implementation
[...]
procedure TBaseVirtualTree.WMGetDlgCode(var Message: TWMGetDlgCode);

begin
  Message.Result := DLGC_WANTCHARS or DLGC_WANTARROWS;
  if FWantTabs then
    Message.Result := Message.Result or DLGC_WANTTAB;
end;

[...]
So, ich hoffe das du damit was anfangen kannst. Solltest du hierzu noch Fragen haben, dann meld dich einfach nochmal.

Jens Hartmann 9. Apr 2016 20:00

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Hallo Aviator,

ich spiele gerade ein wenig meiner Deiner Version. Das meiste davon habe ich ja bereits auch schon so gelöst. Allerdings bekomme ich z.B. wenn ich auf

Delphi-Quellcode:
ActiveControl

//z.B. wie bei Dir
  if (ActiveControl = vstMain) and (Ord(Key) = VK_TAB) then
    Key := #0;
bekomme ich eine Fehlermeldung.

Die
Delphi-Quellcode:
property ActiveControl;
befindet sich ja in der
Delphi-Quellcode:
vcl.Forms
. Doch auf diese habe ich scheinbar aus meiner eigenen Unit kein Zugriff. Ich habe den Editor in eine eigene Unit gelegt und dort die
Delphi-Quellcode:
vcl.Forms
eingebunden.

jaenicke 9. Apr 2016 21:05

AW: VirtualTreeView Editfelder, ComboBox und andere
 
ActiveControl ist eine Eigenschaft von TForm und funktioniert deshalb auch nur im Kontext des jeweiligen Formulars.

Aviator 10. Apr 2016 11:04

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von jaenicke (Beitrag 1335153)
ActiveControl ist eine Eigenschaft von TForm und funktioniert deshalb auch nur im Kontext des jeweiligen Formulars.

Korrekt. Und in dem Fall müsstest du dann die Form an deine Editorklasse übergeben und könntest dann wieder darauf prüfen. Nur kannst du dann immer nur auf die Klasse der Komponente prüfen und nicht direkt auf die Komponente da du ja sonst noch Zugriff auf deine Formklasse aus der Editorklasse bräuchtest.

Jens Hartmann 10. Apr 2016 14:07

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Das habe ich jetzt auch schon so hinbekommen. Ich habe jetzt Deine Editorklasse bei mir integriert und angepasst. Ich kann jetzt komplett navigieren wie gewünscht. Aber das mit dem TAB bekomme ich nicht hin.

Starte ich mein Programm ohne

Delphi-Quellcode:
procedure TfKabellisten.vstKabelInEditing(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
begin
  //Allowed := true;
end;
Dann geht das navigieren auch mit der TAB Taste. Anders nicht. Ich habe jetzt versucht meine Klasse zu erweitern um die TAB Taste abzufangen.

Delphi-Quellcode:
  ...
  TEditEditLink = class abstract(TBaseDataEditLink)
  private
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  ...

procedure TEditEditLink.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  Inherited;
    Message.Result := Message.Result or DLGC_WANTTAB;
end;
Aber ich bin auch ehrlich, die Funktion verstehe ich absolut nicht. Und im
Delphi-Quellcode:
OnKeyDown,
OnKeyUp
OnKeyPress
Kann man die TAB Taste nicht abfangen. Entnehme ich zumindestens der Delphi Hilfe...

Delphi-Quellcode:
  case Key of
    VK_TAB:
      begin
        //geht so nicht
      end;

jaenicke 11. Apr 2016 07:30

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1335173)
Aber ich bin auch ehrlich, die Funktion verstehe ich absolut nicht.
[...]
Und im
Delphi-Quellcode:
OnKeyDown,
OnKeyUp
OnKeyPress
Kann man die TAB Taste nicht abfangen.

Hast du denn einmal in die Doku geschaut? Da ist das eigentlich sehr gut beschrieben:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms645425(v=vs.85).aspx
Wenn du die Tab Taste selber behandeln willst, musst du in dem Control, das die entsprechenden Messages bekommt, WM_GETDLGCODE entsprechend des bereits geposteten Codes implementieren. Damit sagst du Windows, dass es die Standardbehandlung für Tab (zum nächsten Steuerelement springen) nicht ausführen und stattdessen die Messages zur Behandlung des Tastendrucks schicken soll.

Jens Hartmann 11. Apr 2016 19:56

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jaenicke
Hast du denn einmal in die Doku geschaut? Da ist das eigentlich sehr gut beschrieben:
https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
Wenn du die Tab Taste selber behandeln willst, musst du in dem Control, das die entsprechenden Messages bekommt, WM_GETDLGCODE entsprechend des bereits geposteten Codes implementieren. Damit sagst du Windows, dass es die Standardbehandlung für Tab (zum nächsten Steuerelement springen) nicht ausführen und stattdessen die Messages zur Behandlung des Tastendrucks schicken soll.


Schaue ich mir jetzt gleich mal an.


Im weiteren habe ich noch ein Problem gefunden. Die Tasten VK_UP, und VK_DOWN gehen. Allerdings, denke ich, das die Funktion ja folgendes machen soll... (siehe Kommentare

Delphi-Quellcode:
    VK_UP,
    VK_DOWN,
    VK_LEFT,
    VK_RIGHT:
      begin
        CanEndEdit := Shift = []; //CanEndEdit wird gesetzt, wenn keine Shift Taste gedrückt ist
        if FEdit is TComboBox then //Hier wird geprüft, ob FEdit vom Typ ComboBox ist
          CanEndEdit := CanEndEdit and not TComboBox(FEdit).DroppedDown;
          {CanEndEdit wird gesetzt, wenn vorab CanEndEdit gesetzt und TComboBox kein
           geöffnetes DroppedDown Menü hat. Wäre das DroppedDown Menü geöffnet, soll
           der nächste Eintrag im Menü gewählt werden. Das funktioniert so nicht. Der
           nächste Eintrag wird zwar gewählt, aber dann sofort das DroppedDown Menü geschlossen}
        if CanEndEdit then
          {Ist kein DroppedDown Menü geöffnet oder kein TComboBox oder kein Shift, müsste
           dann müsste hier CanEndEdit noch true sein} 
          begin
            PostMessage(FTree.Handle, WM_KEYDOWN, Key, 0);
            {Versteh ich wieder nicht so richtig. Ich denke hier wird geprüft, welcher
             Key und diese Message wird dann verarbeitet. Soll heißen, dass Bewegen mit den
             Taste Links, Rechts, Auf und Ab. Hier funktioniert die Navigation auch tadellos,
             allerdings, wird nur beim VK_UP und VK_DOWN die Funktion "SaveChanges" aufgerufen.
             Dies liegt ja vermutlich daran, das der Node nicht verlassen wird, sondern nur das
             Column gewechselt. Aber eine Idee zur Lösung fehlt mir trotzdem aktuell}
            Key := 0;
            { Abschließen wird der Key auf 0 gesetzt damit keine weitere Bearbeitung
              entsprechend der normalen Windows Funktionen erfolgt }
          end;
      end;
Ich hoffe, dass ist so und jemand kann mir mal erklären, woran das Problem liegt und wie ich das abstellen kann.

Danke nochmal und Gruß Jens

Jens Hartmann 11. Apr 2016 20:22

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jaenicke"
Hast du denn einmal in die Doku geschaut? Da ist das eigentlich sehr gut beschrieben:
https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
Wenn du die Tab Taste selber behandeln willst, musst du in dem Control, das die entsprechenden Messages bekommt, WM_GETDLGCODE entsprechend des bereits geposteten Codes implementieren. Damit sagst du Windows, dass es die Standardbehandlung für Tab (zum nächsten Steuerelement springen) nicht ausführen und stattdessen die Messages zur Behandlung des Tastendrucks schicken soll.

Hier habe ich überigens auch schon eine Idee, woran es liegen könnte. Ich habe das ganze jetzt wie folgt implementiert...

Delphi-Quellcode:
//es gibt ja den Basis Editor
type
  TBaseDataEditLink = class(TInterfacedObject, IVTEditLink)
  protected
    FEdit  : TWinControl;
    FTree  : TVirtualStringTree;
    FNode  : PVirtualNode;
    FColumn : Integer;
    IsCanceling: Boolean; // Wird auf True gesetzt, wenn der Bearbeitungsvorgang abgebrochen wird

    const WM_ENDEDITING = WM_USER + 1311;
    const FAILURE_CREATECHOICECONTROL = 'Fehler beim Erstellen der Auswahlobjekte!';
  protected
    procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); virtual;
    procedure EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); virtual;
    procedure EditKeyPress(Sender: TObject; var Key: Char); virtual;
    procedure SetBounds(R: TRect); virtual; stdcall;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;

    procedure GetNodeText; virtual; abstract;
    procedure SaveChanges; virtual; abstract;
  public
    destructor Destroy; override;

    function BeginEdit: Boolean; virtual; stdcall;
    function CancelEdit: Boolean; virtual; stdcall;
    function EndEdit: Boolean; virtual; stdcall;
    function GetBounds: TRect; virtual; stdcall;
    procedure ProcessMessage(var Message: TMessage); virtual; stdcall;

    /// <summary>
    /// Wird benötigt, damit FEdit von TBaseEditLink nicht auf nil zeigt
    /// Muss bei jeder Klasse die von TBaseEditLink abgeleitet wird im PrepareEdit ausgeführt werden
    /// </summary>
    procedure SetEditComponent(AEditComponent: TWinControl);
  end;

//In diesem Basis Editor habe ich im OnKeyDown die Abfrage der TAB-Taste einbunden
procedure TBaseDataEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  ...
begin
  CanEndEdit := True;
  ...
  VK_TAB:
    begin
      ShowMessage('tt');
    end;
  ...
end;

//Dann gibt es ja die abgeleiteten Editoren für TEdit und TComboBox
//TComboBox
  TComboBoxEditLink = class abstract(TBaseDataEditLink)
  protected
    FEdit: TComboBox;
    procedure GetNodeText; override;
    procedure EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); override;
    function FillOptions(Sender: TComboBox): Boolean; virtual; abstract;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
  public
    destructor Destroy; override;
    function BeginEdit : Boolean; override;
  end;

TEdit
  TEditEditLink = class abstract(TBaseDataEditLink)
  protected
    FEdit: TEdit;
    procedure GetNodeText; override; abstract;
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
  public
    destructor Destroy; override;
    function BeginEdit : Boolean; override;
  end;
{
Von diesen Editoren habe ich weitere Editoren abgeleitet, in denen ich dann noch ein paar Details eingebunden habe. z.B. verschiedene Properties etc.
}

{
Hier nehme ich mal als Beispiel eine von
}
type
  TKabelbezeichnungEditLink = class(TEditEditLink)
  private
    //Hier die implementation der message WM_GETDLGCODE
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  protected
    procedure GetNodeText; override;
    procedure SaveChanges; override;
  end;

{
und
}

procedure TKabelbezeichnungEditLink.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  Inherited;
    Message.Result := Message.Result or DLGC_WANTTAB;
end;
Jetzt gehe ich aktuell davon aus, dass die TAB Taste nicht erkannt wird, weil entweder die Implementation an der falschen Stelle durchgeführt wurde, oder der "Parent" nicht richtig ist...

Ich habe hierzu dieses gefunden...

http://www.delphipraxis.net/72821-ab...gar-nicht.html

Aviator 11. Apr 2016 20:32

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1335335)
Jetzt gehe ich aktuell davon aus, dass die TAB Taste nicht erkannt wird, weil [...] der "Parent" nicht richtig ist...

Hast du denn den Parent in der Editorklasse auch gesetzt? Ich hatte den in meiner Basisklasse damals aus irgendeinem Grund weggelassen und in der Implementierung des eigentlichen Editors immer erst gesetzt. Aber du könntest das jetzt zum Testen mal machen und in der
Delphi-Quellcode:
TBaseDataEditLink.PrepareEdit()
Funktion den Parent des FEdit mal auf den Tree setzen. Dann nochmal probieren. Wenn es dann nicht funktioniert, dann müsstest du mal schauen, ob die Message evtl. im Tree schon abgefangen und eliminiert wird, sobald sich der Tree im Editmodus befindet. Kann jetzt leider gerade nicht nachschauen bzw. nachprüfen. Aber das wäre jetzt mal ein Ansatz den du austesten könntest.

[EDIT]Sorry nicht in der
Delphi-Quellcode:
TBaseDataEditLink.PrepareEdit()
sondern in einer der nachfolgenden Klassen, in der die Instanz der Komponente erzeugt wird.[/EDIT]

Jens Hartmann 11. Apr 2016 21:08

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Also ich mache das eigendlich in der Instanz in der ich die Komponente erzeuge...

Delphi-Quellcode:
function TKabelNrEditLink.PrepareEdit(Tree: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex): Boolean;
begin
  inherited;
  Self.FEdit.Style := csDropDownList;
  Self.FEdit.OnChange := Change;
  Self.FEdit.Parent := Self.FTree;
end;

Jens Hartmann 12. Apr 2016 05:25

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ergänzend muss ich vieleicht mal noch erklären, dass meine Form mit dem integrierten VST als Form auf einem Panel auf meiner MainForm liegt.

Ich habe meine Programmoberfläche wie folgt gestaltet:

1. MainForm
2. Linkes Seite MainForm ein VST als eine Art Datenexplorer
3. Rechte Seite MainForm ein Panel als Datenpanel auf dem ich je nach Auswahl im vst entsprechende Daten anzeige.
Auf dieses Panel werden dann entweder Frames oder in diesem Fall eine Form gelegt und dieser Form ist dann das Panel als Parent zugewiesen.

siehe Bild im Anhang..

jaenicke 12. Apr 2016 06:27

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Es ist dann vielleicht einfacher, wenn du mit dem Window Detective anschaust welche Messages eigentlich wo landen...
Vielleicht muss WM_GETDLGCODE auch in deinem obersten Formular beantwortet werden.

Kann ich das aktuelle Problem in dem Beispiel aus #14 nachvollziehen? Dann schaue ich mit das einmal heute Abend an.

Jens Hartmann 12. Apr 2016 08:06

AW: VirtualTreeView Editfelder, ComboBox und andere
 
Ich denke schon, aber Du meinst glaube ich #34


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:57 Uhr.
Seite 1 von 2  1 2      

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