Weil mich das Thema interessiert, habe ich mir die VirtualTreeView-Editoren näher angesehen. So gut die Komponente ansonsten ist, hier hatte der Autor wohl nicht seinen besten Tag...
Der oben erwähnte "Dreizeiler" war etwas zu gut geschätzt: statt drei sind es fast 300 Zeilen geworden. Ich habe mir aus "VirtualTrees.pas" und der Demo-Unit "editors.pas" einen einfachen Zellen-Editor zusammengebastelt, der mit TAB so umgeht, so wie du dir das wahrscheinlich vorstellst. Im Edit-Modus (also nach Drücken von F2) wird nach Drücken von TAB der Eingabemodus beendet, der Text in den Datensatz geschrieben und die nächste Spalte in derselben Zeile fokussiert. Der Tree wechselt auch gleich wieder in den Eingabemodus. Am Ende einer Zeile wird das erste Feld der nächsten Zeile ausgewählt und zum Bearbeiten vorbereitet. Zusätzlich gibt es auch die Möglichkeit mit SHIFT+TAB zum vorigen Feld zu wechseln; falls man das erste Feld einer Zeile bearbeitet hat, geht es beim letzten der vorigen Zeile weiter.
Das Verhalten bei ENTER im Edit-Modus habe ich so realisiert, dass hier zum nächsten Feld entlang der Spalte gewechselt wird (bei SHIFT+ENTER zur vorigen Zeile). Allerdings bleibt hier die Eingabe bei der letzten (ersten) Zeile stehen.
Den Eingabemodus verlässt man mit ESC.
Zur Verwendung musst du die beigefügte Unit in die Uses-Zeile aufnehmen. Dann dem VirtualStringTree folgenden Event-Handler für OnCreateEditor zuweisen, damit der neue Editor verwendet wird (hier am Beispiel des GridForm im Advanced-Demo des VT-Package):
Code: Alles auswählen
procedure TGridForm.VST5CreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
out EditLink: IVTEditLink);
begin
EditLink := TVTStringEditLink.Create;
end;
Nicht vergessen, dass die eingegebenen Strings noch mit dem Ereignis OnNewText in die Daten geschrieben werden müssen, die im VirtualTree angezeigt werden. In dem AdvancedDemo (GridForm) geht das so:
Code: Alles auswählen
procedure TGridForm.VST5NewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; const NewText: String);
var
Data: PGridData;
begin
Data := Sender.GetNodeData(Node);
if Column > 0 then
Data.Value[Column-1] := NewText;
end;
Ich habe das alles mit der Trunk-Version von VirtualTreeView gemacht und hoffe, dass keine Änderungen zur Stable-Version betroffen sind. Ansonsten melde dich hier doch nochmals.
P.S.
Leider lässt mich die Forum-Software gerade nicht die Unit als Anhang hochladen, daher hier direkt im Posting:
Code: Alles auswählen
unit VTEditor;
{$mode objfpc}{$H+}
interface
uses
LCLType, LCLIntf, Messages, Classes, SysUtils, Graphics, Controls, Forms, StdCtrls, VirtualTrees;
type
{ TVTStringEditLink }
TVTStringEditLink = class(TInterfacedObject, IVTEditLink)
private
FEdit: TEdit; // Edit control as cell editor
FTree: TVirtualStringTree; // A back reference to the tree calling.
FNode: PVirtualNode; // The node being edited.
FColumn: Integer; // The column of the node being edited.
protected
procedure EditExit(Sender: TObject);
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
public
destructor Destroy; override;
function BeginEdit: Boolean; stdcall;
function CancelEdit: Boolean; stdcall;
function EndEdit: Boolean; stdcall;
function GetBounds: TRect; stdcall;
function PrepareEdit(ATree: TBaseVirtualTree; ANode: PVirtualNode;
AColumn: TColumnIndex): Boolean; stdcall;
procedure ProcessMessage(var Message: TMessage); stdcall;
procedure SetBounds(R: TRect); stdcall;
end;
implementation
//----------------- TVTStringEditLink ------------------------------------------
destructor TVTStringEditLink.Destroy;
begin
Application.ReleaseComponent(FEdit);
inherited;
end;
//------------------------------------------------------------------------------
procedure TVTStringEditLink.EditExit(Sender: TObject);
begin
FTree.EndEditNode;
end;
//------------------------------------------------------------------------------
type
TVirtualStringTreeHack = class(TVirtualStringTree);
procedure TVTStringEditLink.EditKeyDown(Sender: TObject;
var Key: Word; Shift: TShiftState);
var
node: PVirtualNode;
col: TColumnIndex;
GetStartColumn: function(ConsiderAllowFocus: Boolean = False): TColumnIndex of object;
GetNextColumn: function(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex of object;
GetNextNode: TGetNextNodeProc;
begin
case Key of
VK_ESCAPE:
begin
FTree.CancelEditNode;
Key := 0;
end;
VK_RETURN:
begin
FTree.InvalidateNode(FNode);
if (ssShift in Shift) then
node := FTree.GetPreviousVisible(FNode, True)
else
node := FTree.GetNextVisible(FNode, True);
FTree.EndEditNode;
if node <> nil then FTree.FocusedNode := node;
Key := 0;
if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
TVirtualStringTreeHack(FTree).DoEdit;
end;
VK_TAB:
begin
FTree.InvalidateNode(FNode);
if ssShift in Shift then
begin
GetStartColumn := @FTree.Header.Columns.GetLastVisibleColumn;
GetNextColumn := @FTree.Header.Columns.GetPreviousVisibleColumn;
GetNextNode := @FTree.GetPreviousVisible;
end
else
begin
GetStartColumn := @FTree.Header.Columns.GetFirstVisibleColumn;
GetNextColumn := @FTree.Header.Columns.GetNextVisibleColumn;
GetNextNode := @FTree.GetNextVisible;
end;
// Advance to next/previous visible column/node.
Node := FNode;
col := GetNextColumn(FColumn, True);
repeat
// Find a column for the current node which can be focused.
while (col > NoColumn) and
not TVirtualStringTreeHack(FTree).DoFocusChanging(FNode, node, FColumn, col)
do
col := GetNextColumn(col, True);
if col > NoColumn then
begin
// Set new node and column in one go.
TVirtualStringTreeHack(FTree).SetFocusedNodeAndColumn(node, col);
Break;
end;
// No next column was accepted for the current node. So advance to next node and try again.
node := GetNextNode(node);
col := GetStartColumn();
until node = nil;
FTree.EndEditNode;
Key := 0;
if node <> nil then
begin
FTree.FocusedNode := node;
FTree.FocusedColumn := col;
end;
if FTree.CanEdit(FTree.FocusedNode, FTree.FocusedColumn) then
with TVirtualStringTreeHack(FTree) do
begin
EditColumn := FocusedColumn;
DoEdit;
end;
end;
VK_UP,
VK_DOWN:
begin
// Forward the keypress to the tree. It will asynchronously change the focused node.
PostMessage(FTree.Handle, WM_KEYDOWN, Key, 0);
Key := 0;
end;
end;
end;
//------------------------------------------------------------------------------
function TVTStringEditLink.BeginEdit: Boolean; stdcall;
begin
Result := True;
FEdit.Show;
FEdit.SetFocus;
end;
//------------------------------------------------------------------------------
function TVTStringEditLink.CancelEdit: Boolean; stdcall;
begin
Result := True;
FEdit.Hide;
end;
//------------------------------------------------------------------------------
function TVTStringEditLink.EndEdit: Boolean; stdcall;
var
Buffer: array[0..1024] of Char;
S: String;
begin
Result := True;
S := FEdit.Text;
if FEdit.Modified then
FTree.Text[FNode, FColumn] := FEdit.Text;
FEdit.Hide;
FTree.SetFocus;
end;
//------------------------------------------------------------------------------
function TVTStringEditLink.GetBounds: TRect; stdcall;
begin
Result := FEdit.BoundsRect;
end;
//------------------------------------------------------------------------------
function TVTStringEditLink.PrepareEdit(ATree: TBaseVirtualTree; ANode: PVirtualNode;
AColumn: TColumnIndex): Boolean; stdcall;
begin
Result := True;
FTree := ATree as TVirtualStringTree;
FNode := ANode;
FColumn := AColumn;
FEdit.Free;
FEdit := TEdit.Create(nil);
FEdit.Visible := False;
FEdit.Parent := ATree;
FEdit.Font.Color := clWindowText;
FEdit.HandleNeeded;
FEdit.Text := FTree.Text[FNode, FColumn];;
FEdit.OnKeyDown := @EditKeyDown;
FEdit.OnExit := @EditExit;
end;
//------------------------------------------------------------------------------
procedure TVTStringEditLink.ProcessMessage(var Message: TMessage); stdcall;
begin
FEdit.WindowProc(Message);
end;
//------------------------------------------------------------------------------
procedure TVTStringEditLink.SetBounds(R: TRect); stdcall;
var
Dummy: Integer;
begin
FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
FEdit.BoundsRect := R;
end;
end.