2016-08-05 20 views
0

Ich habe versucht einen Editor für einen VirtualStringTree auf example von Lazarius Basis zu implementierenZugriffsverletzung nach TStringEditLink erhalten zerstört (TVirtualStringTree) - Lazarus Beispiel

Können Sie mir sagen, warum habe ich eine Zugriffsverletzung erhalten, nachdem TStringEditLink zerstört wird ?

Es ist verrückt, dass Fehler nur erscheinen, wenn ich ESCAPE oder ENTER drücke. Wenn ich von einer Zelle auf eine andere klicke, gibt es keinen Fehler.

Wie eine Beobachtung säte ich, dass, wenn ich den FEdit.Free Code von destructor TStringEditLink.Destroy entferne der Fehler verschwinden.

Haben Sie eine Lösung dafür?

Bellow den vollständigen Code:

unit Unit2; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees, EditorLink, Vcl.StdCtrls, 
    Vcl.ExtCtrls, Vcl.Imaging.jpeg; 

type 
    TTreeData = record 
    Fields: array of String; 
    end; 
    PTreeData = ^TTreeData; 

const 
    SizeVirtualTree = SizeOf(TTreeData); 

type 
    TForm2 = class(TForm) 
    VirtualTree: TVirtualStringTree; 
    procedure FormCreate(Sender: TObject); 
    procedure VirtualTreeClick(Sender: TObject); 
    procedure VirtualTreeCreateEditor(Sender: TBaseVirtualTree; 
     Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); 
    procedure VirtualTreeEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; 
     Column: TColumnIndex; var Allowed: Boolean); 
    procedure VirtualTreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); 
    procedure VirtualTreeGetNodeDataSize(Sender: TBaseVirtualTree; 
     var NodeDataSize: Integer); 
    procedure VirtualTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
     Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); 
    procedure VirtualTreeNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
     Column: TColumnIndex; NewText: string); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form2: TForm2; 

implementation 

{$R *.dfm} 

procedure TForm2.FormCreate(Sender: TObject); 
var 
    Node: PVirtualNode; 
    LTreeData: PTreeData; 
begin 
    VirtualTree.Clear; 
    VirtualTree.BeginUpdate; 

    //node 1 
    Node:= VirtualTree.AddChild(nil,nil); 
    VirtualTree.ValidateNode(Node,False); 

    LTreeData:= VirtualTree.GetNodeData(Node); 
    SetLength(LTreeData^.Fields,3); 

    LTreeData^.Fields[0]:= 'John'; 
    LTreeData^.Fields[1]:= '2500'; 
    LTreeData^.Fields[2]:= 'Production'; 

    //node 2 
    Node:= VirtualTree.AddChild(nil,nil); 
    VirtualTree.ValidateNode(Node,False); 

    LTreeData:= VirtualTree.GetNodeData(Node); 
    SetLength(LTreeData^.Fields,3); 

    LTreeData^.Fields[0]:= 'Mary'; 
    LTreeData^.Fields[1]:= '2100'; 
    LTreeData^.Fields[2]:= 'HR'; 

    VirtualTree.EndUpdate; 
end; 

procedure TForm2.VirtualTreeClick(Sender: TObject); 
var 
    VT: TVirtualStringTree; 
    Click: THitInfo; 
begin 
    VT:= Sender as TVirtualStringTree; 
    VT.GetHitTestInfoAt(Mouse.CursorPos.X-VT.ClientOrigin.X, Mouse.CursorPos.Y-VT.ClientOrigin.Y, True, Click); 
    VT.EditNode(Click.HitNode,Click.HitColumn); 
end; 

procedure TForm2.VirtualTreeCreateEditor(Sender: TBaseVirtualTree; 
    Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); 
begin 
    EditLink := TStringEditLink.Create; 
end; 

procedure TForm2.VirtualTreeEditing(Sender: TBaseVirtualTree; 
    Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); 
begin 
    Allowed:= True; 
end; 

procedure TForm2.VirtualTreeFreeNode(Sender: TBaseVirtualTree; 
    Node: PVirtualNode); 
var 
    LTreeData: PTreeData; 
begin 
    LTreeData:= Sender.GetNodeData(Node); 
    Finalize(LTreeData^); 
end; 

procedure TForm2.VirtualTreeGetNodeDataSize(Sender: TBaseVirtualTree; 
    var NodeDataSize: Integer); 
begin 
    NodeDataSize:= SizeVirtualTree; 
end; 

procedure TForm2.VirtualTreeGetText(Sender: TBaseVirtualTree; 
    Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; 
    var CellText: string); 
var 
    LTreeData: PTreeData; 
begin 
    if Assigned(Node) and (Column > NoColumn) then 
    begin 
     LTreeData:= Sender.GetNodeData(Node); 
     CellText:= LTreeData^.Fields[Column]; 
    end; 
end; 

procedure TForm2.VirtualTreeNewText(Sender: TBaseVirtualTree; 
    Node: PVirtualNode; Column: TColumnIndex; NewText: string); 
var 
    LTreeData: PTreeData; 
begin 
    LTreeData:= Sender.GetNodeData(Node); 
    LTreeData^.Fields[Column]:= NewText; 
end; 

end. 

und die EditorLink Einheit

unit EditorLink; 

interface 

uses 
    Classes, SysUtils, Forms, Controls, Graphics, Dialogs, 
    VirtualTrees, Messages, Windows, StdCtrls, Vcl.ExtCtrls; 

type 

    TStringEditLink = class(TInterfacedObject, IVTEditLink) 
    private 
    FEdit: TWinControl; 
    FTree: TVirtualStringTree; 
    FNode: PVirtualNode; 
    FColumn: Integer; 
    FStopping: Boolean; 
    protected 
    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(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; 
    procedure ProcessMessage(var Message: TMessage); stdcall; 
    procedure SetBounds(R: TRect); stdcall; 
    end; 

implementation 

uses unit2; 

destructor TStringEditLink.Destroy; 
begin 
    FEdit.Free; //--> seems that due to this I get the access violation 
    inherited; 
end; 

procedure TStringEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 
begin 
    case Key of 
    VK_ESCAPE: 
     begin 
     FTree.CancelEditNode; 
     Key := 0; 
     FTree.setfocus; 
     end; 
    VK_RETURN: 
     begin 
     PostMessage(FTree.Handle, WM_KEYDOWN, VK_DOWN, 0); 
     Key := 0; 
     FTree.EndEditNode; 
     FTree.setfocus; 
     end; 
    end; //case 
end; 

function TStringEditLink.BeginEdit: Boolean; 
begin 
    Result := not FStopping; 
    if Result then 
    begin 
     FEdit.Show; 
     FEdit.SetFocus; 
    end; 
end; 

function TStringEditLink.CancelEdit: Boolean; 
begin 
    Result := True; 
    FEdit.Hide; 
end; 

function TStringEditLink.EndEdit: Boolean; 
var 
    s: String; 
begin 
    Result := True; 
    s := TComboBox(FEdit).Text; 
    FTree.Text[FNode, FColumn] := s; 

    FTree.InvalidateNode(FNode); 
    FEdit.Hide; 
    FTree.SetFocus; 
end; 

function TStringEditLink.GetBounds: TRect; 
begin 
    Result := FEdit.BoundsRect; 
end; 

function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; 
var 
    FCellText: String; 
    FCellTextBounds: TRect; 
    FCellFont: TFont; 
begin 
    Result := True; 
    FTree := Tree as TVirtualStringTree; 

    FNode := Node; 
    FColumn := Column; 

    FCellFont:= TFont.Create; 
    FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText); 

    FEdit := TComboBox.Create(nil); 
    with FEdit as TComboBox do 
    begin 
     Visible := False; 
     Parent := Tree; 
     Items.Add('Google'); 
     Items.Add('Yahoo'); 
     Items.Add('Altavista'); 
     OnKeyDown := EditKeyDown; 
     Text:= FCellText; 
    end; 
end; 

procedure TStringEditLink.ProcessMessage(var Message: TMessage); 
begin 
    FEdit.WindowProc(Message); 
end; 

procedure TStringEditLink.SetBounds(R: TRect); 
var 
    Dummy: Integer; 
begin 
    FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right); 
    FEdit.BoundsRect := R; 
end; 

end. 

Antwort

0

Die Lösung, die ich am Ende verwendet wird unten aufgeführt:

TBasePanel = class(TPanel) 
    private 
    procedure CMRelease(var Message: TMessage); message CM_RELEASE; 
    protected 
    public 
    procedure Release; virtual; 
    end; 

TStringEditLink = class(TInterfacedObject, IVTEditLink) 
    private 
    FBasePanel: TBasePanel; 
    ... 
    protected 
    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(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; 
    procedure ProcessMessage(var Message: TMessage); stdcall; 
    procedure SetBounds(R: TRect); stdcall; 
    end; 

implementation 

procedure TBasePanel.CMRelease(var Message: TMessage); 
begin 
    Free; 
end; 

procedure TBasePanel.Release; 
begin 
    if HandleAllocated then 
    PostMessage(Handle, CM_RELEASE, 0, 0); 
end; 

destructor TStringEditLink.Destroy; 
begin 
    if Assigned(FBasePanel) then FBasePanel.Release; 
    inherited; 
end; 

FBasePanel sollte als owner und als parent für so viele Komponenten-Editoren wie in der gleichen Zeit angezeigt werden sollte verwendet werden.

0

Ich habe keine Lazarus aber es scheint das gleiche auf XE4 zu verhalten.

In meiner VST-Installation, in ./VirtualTreeviewV5.3.0/Demos/Advanced befindet sich eine Editors.pas Datei, in der ich den Destruktor unten gefunden habe. Beachten Sie den Kommentar casues issue #357:

destructor TPropertyEditLink.Destroy; 
begin 
    //FEdit.Free; casues issue #357. Fix: 
    if FEdit.HandleAllocated then 
    PostMessage(FEdit.Handle, CM_RELEASE, 0, 0); 
    inherited; 
end; 

Außerdem FEdit.Free im PrepareEdit Verfahren vor seiner Neuschöpfung durchgeführt wird:

function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; 
var 
    FCellText: String; 
    FCellTextBounds: TRect; 
    FCellFont: TFont; 
begin 
    Result := True; 
    FTree := Tree as TVirtualStringTree; 

    FNode := Node; 
    FColumn := Column; 

    FEdit.Free; 
    FEdit := nil; 

    FCellFont:= TFont.Create; 
    FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText); 

    FEdit := TComboBox.Create(nil); 
    with FEdit as TComboBox do 
    . . . 

Dies löst die VK_ESC und die VK_RETURN Probleme auf meinem XE4 und XE7 Installation.


Die Frage #357 scheint noch nicht festgelegt worden: - Used fix proposed in issue #361 to fix issue #357 (AV in advanced demo - PropertiesDemo form in XE3+) sehen. Ich habe keine Beweise für die #361 fix gefunden.


Ein weiteres Problem passiert mir beim Klicken auf einen nicht zugewiesenen Knoten nach einem Bearbeitungsvorgang.
Überprüfen, ob die Click.HitNode nicht nil vor Beginn der Bearbeitung löst das obige.

procedure TForm2.VirtualTreeClick(Sender: TObject); 
var 
    VT: TVirtualStringTree; 
    Click: THitInfo; 
begin 
    VT:= Sender as TVirtualStringTree; 
    VT.GetHitTestInfoAt(Mouse.CursorPos.X-VT.ClientOrigin.X, Mouse.CursorPos.Y-VT.ClientOrigin.Y, True, Click); 

    if Assigned(Click.HitNode) then 
    VT.EditNode(Click.HitNode,Click.HitColumn); 
end; 

Beachten Sie auch, haben Sie einen zirkulären Verweis in der EditorLink Einheit:

uses Unit2; 
+0

Jetzt, seit ich deine Antwort säen, erinnere ich mich, dass ich diesen Code vor einigen Monaten säen. Ich werde es testen und ich werde mit einem Feedback zurückkommen. – REALSOFO

+0

löst das Problem nicht. Das Steuerelement wird zerstört, nachdem 'FTree' und Formular zerstört wurden. Es ist dasselbe, wenn ich 'FEdit.Free' nicht lege. Um zu sehen, wann das 'FEdit' zerstört wird, benutzte ich einen Wrapper' TAltComboBox = class (TComboBox); ... procedure WMDestroy (var Msg: TWMDestroy); Nachricht WM_DESTROY; ' – REALSOFO

+0

Warum benutzen Sie die ziemlich alte V5.3.0? Ist das Problem in der aktuellen V6.3.0 noch vorhanden? –

0

Diese Pseudo-Stack-Trace des Codes das Problem veranschaulicht:

FEdit.EditKeyDown() 
    -- calls -- 
FTree.EndEditNode() { or FTree.CancelEditNode } 
    -- which calls -- 
TStringEditLink.Destroy() 
    -- which calls -- 
FEdit.Free() 

Der Code in Der Event-Handler für FEdit.EditKeyDown() gibt FEdit vor dem Schlüssel frei Der Ereignishandlercode wird beendet. Also der Zugriffsverletzungsfehler.

Wir behandelt dies durch einen Signalmechanismus einrichten, so dass die TStringEditLink das Hauptformular signalisieren könnte, wenn es fertig war, und die Hauptform den Code ausführen könnte die TStringEditLink zu zerstören (da ist es derjenige, der die TStringEditLink in der erstellt erster Platz). Wir fügten dem Hauptformular eine TTimer hinzu und eine Eigenschaft, um das Signal zu empfangen. Die TTimer überwacht die Eigenschaft. Die TStringEditLink Komponente hat einen Zeiger auf das Formular, so dass es die Eigenschaft festlegen kann.

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, VirtualTrees; 

type 
    TEditorAction = (eaCancel, eaAccept, eaNotSet); 

    TForm1 = class(TForm) 
    vstTree: TVirtualStringTree; 
    procedure vstTreeCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); 
    procedure DoWatchTreeEditorTimer(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    FEndEditTimer: TTimer; 
    FEditorAction: TEditorAction; 
    procedure SetEditorAction(const Value: TEditorAction); 
    public 
    property EditorAction: TEditorAction read FEditorAction write SetEditorAction; 
    end; 

    TPropertyEdit = class(TInterfacedObject, IVTEditLink) 
    procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 
    private 
    FEdit: TWinControl; 
    FTree: TVirtualStringTree; 
    FNode: PVirtualNode; 
    FColumn: Integer; 
    public 
    FForm: TForm1; 
    destructor Destroy; override; 
    function BeginEdit: Boolean; stdcall; 
    function CancelEdit: Boolean; stdcall; 
    function EndEdit: Boolean; stdcall; 
    function GetBounds: TRect; stdcall; 
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; 
    procedure ProcessMessage(var Message: TMessage); stdcall; 
    procedure SetBounds(R: TRect); stdcall; 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

{ TForm1 } 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    FEndEditTimer := TTimer.Create(nil); 
    FEndEditTimer.Enabled := False; 
    FEndEditTimer.Interval := 100; 
    FEndEditTimer.OnTimer := DoWatchTreeEditorTimer; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    FreeAndNil(FEndEditTimer); 
end; 

procedure TForm1.vstTreeCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); 
begin 
    EditLink := TPropertyEdit.Create; 
    TPropertyEdit(EditLink).FForm := Self; { lets us signal the form when the editor needs to be destroyed } 
    FEditorAction := eaNotSet; 
end; 

procedure TForm1.SetEditorAction(const Value: TEditorAction); 
begin 
    if FEditorAction <> Value then 
    begin 
    FEditorAction := Value; 
    FEndEditTimer.Enabled := True; 
    end; 
end; 

procedure TForm1.DoWatchTreeEditorTimer(Sender: TObject); 
begin 
    FEndEditTimer.Enabled := False; 
    Application.ProcessMessages; 
    case FEditorAction of 
    eaCancel: 
     begin 
     vstTree.CancelEditNode; 
     vstTree.SetFocus; 
     end; 
    eaAccept: 
     begin 
     vstTree.EndEditNode; 
     vstTree.SetFocus; 
     end; 
    end; 
end; 

{ TPropertyEdit } 

function TPropertyEdit.BeginEdit: Boolean; 
begin 
    Result := True; 
    FEdit.Show; 
end; 

function TPropertyEdit.CancelEdit: Boolean; 
begin 
    Result := True; 
    FEdit.Hide; 
    FForm.FEditorAction := eaCancel; 
end; 

destructor TPropertyEdit.Destroy; 
begin 
    if FEdit <> nil then 
    FreeAndNil(FEdit); 
    inherited; 
end; 

procedure TPropertyEdit.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 
begin 
    case Key of 
    VK_ESCAPE: 
     begin 
     Key := Word(0); { some versions of Delphi throw random A/V errors if '0' is not cast as a Word() } 
     FForm.EditorAction := eaCancel; 
     end; 
    VK_RETURN: 
     begin 
     Key := Word(0); { some versions of Delphi throw random A/V errors if '0' is not cast as a Word() } 
     FForm.EditorAction := eaAccept 
     end; 
    end; 
end; 

function TPropertyEdit.EndEdit: Boolean; 
begin 
    Result := True; 
    { Do something with the value provided by the user } 
    FEdit.Hide; 
    FForm.EditorAction := eaAccept; 
end; 

function TPropertyEdit.GetBounds: TRect; 
begin 
    Result := FEdit.BoundsRect; 
end; 

function TPropertyEdit.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; 
begin 
    Result := True; 
    FTree := Tree as TVirtualStringTree; 
    FNode := Node; 
    FColumn := Column; 
    { Setup the editor for user } 
    FEdit := TSomeWinControl.Create(nil); 
    FEdit.Properties := Values; 
    { Capture keystrokes } 
    FEdit.OnKeyDown := EditKeyDown; 
end; 

procedure TPropertyEdit.ProcessMessage(var Message: TMessage); 
begin 
    FEdit.WindowProc(Message); 
end; 

procedure TPropertyEdit.SetBounds(R: TRect); 
var 
    Dummy: Integer; 
begin 
    FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right); 
    FEdit.BoundsRect := R; 
end; 

end. 

Unser Code macht eine Menge zusätzlicher Dinge, so dass der oben genannte Code ein Kopieren/Einfügen der wesentlichen Teile ist zu zeigen, wie die Race-Bedingung zu überwinden. Es ist nicht getestet, aber sollte Sie in die richtige Richtung zeigen.

+0

Es funktioniert nicht! Ich hatte auch schon vorher mit 'wenn zugewiesen (FEdit) dann FEdit.Free;' versucht. Es ist auch interessant, dass, wenn ich eine 'showmessage ('...')' nach 'geerbt' hinzufügen, der Fehler verschwindet. Vielleicht ist es etwas mit dem Fokus des Knotens, nachdem der Editor zerstört wurde. – REALSOFO

+0

Vielleicht würde es auch helfen, 'FEdit' auf' nil' zu setzen? Ansonsten klingt es wie eine Race Condition. –

+0

es muss etwas über die Taste gedrückt werden, aber ich kann nicht herausgefunden ... – REALSOFO

0

In HeidiSql Quellcode gibt es ein gutes Beispiel, um diesen Fehler zu vermeiden. Der Code ein wenig verändert ist:

procedure TBaseEditorLink.TempWindowProc(var Message: TMessage); 
begin 
    case Message.Msg of 
    WM_CHAR: //Catch hotkeys 
     if not (TWMChar(Message).CharCode = VK_TAB) then FOldWindowProc(Message); 
    WM_GETDLGCODE: //"WantTabs" mode for main control 
     Message.Result := Message.Result or DLGC_WANTARROWS or DLGC_WANTALLKEYS or DLGC_WANTTAB; 
    else 
     begin 
     try 
      FOldWindowProc(Message); 
     except 
      on E : EAccessViolation do; //EAccessViolation occurring in some cases 
      on E : Exception do raise; 
     end; 
     end; 
    end; 
end; 
+2

Dies behebt den Fehler nicht, sondern versteckt ihn nur vor dem Benutzer. Es wäre besser, das Problem zu beheben, anstatt den Fehler zu verbergen. –

0

Eine Lösung auch die zuvor erstellten Kontrollen zu befreien ist.

function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; 
var 
    FCellText: String; 
    FCellTextBounds: TRect; 
    FCellFont: TFont; 
    i: Integer; 
    Item: TControl; 
begin 
    Result := True; 
    FTree := Tree as TVirtualStringTree; 

    FNode := Node; 
    FColumn := Column; 

    FCellFont:= TFont.Create; 
    FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText); 

    //----->> free previuous created control <<---------- 
    for i := (FTree.ControlCount - 1) downto 0 do 
    begin 
     Item := FTree.controls[i]; 
     if assigned(item) then 
     begin 
      if item is TComboBox then FreeAndNil(item); 
     end; 
    end; 
    //--------------------------------------------------- 

    FEdit := TComboBox.Create(nil); 
    with FEdit as TComboBox do 
    begin 
     Visible := False; 
     Parent := Tree; 
     Items.Add('Google'); 
     Items.Add('Yahoo'); 
     Items.Add('Altavista'); 
     OnKeyDown := EditKeyDown; 
     Text:= FCellText; 
    end; 
end;