|
Antwort |
Registriert seit: 31. Dez 2002 178 Beiträge Delphi 5 Enterprise |
#1
Hallo,
ich setze mich gerade mit dem VirtualStringTree auseinander und komme auch ganz gut voran. Allerdings habe ich ein Problem beim bearbeiten von Einträgen. Das Ändern von Werten habe ich mir aus der PropertyForm aus der Advanced-Demo abgeguckt. (allerdings eine älter Version da ich auf der Homepage des Autors keine aktuelle Demo gefunden habe) Ich habe in einer Beispielanwendung ein VirtualStringGrid mit zwei Spalten. Die erste Spalte wird mit Hilfe eines TEdit bearbeitet, die zweite Spalte mit einer TComboBox. Wenn ich nun die Spalte mit dem Edit bearbeite und mit Enter bestätige so bekomme ich eine Zugriffsverletzung. Wenn ich jedoch das Edit nicht mit Enter bestätige und stattdessen einfach auf einen anderen Eintrag klicke gibt es die Zugriffsverletzung nicht und der Wert wird richtig übernommen. Beim debuggen hat sich herausgestellt das der Fehler nicht in dem KeyDown-Event des EditLinks ausgelöst wird. Wenn ich den Inhalt aus der Event-Methode auskommentiere kommt es jedoch auch nicht mehr zu der Zugriffsverletzung. Direkt nach dem die Eventmethode abgearbeitet wurde lande ich mit dem Debugger in "TWinControl.KeyDown" => "TWinControl.DoKeyDown" => ... bis ich schließlich in der Unit Forms in der Methode "GetParentForm" lande. Dort wird dann die Exception ausgelöst als versucht wird auf den übergebenen Parameter zuzugreifen. Als Parameter wird jedoch "Self" aus der letzte TWinControl-Methode übergeben. Allerdings weiß ich nicht mal in welcher TWinControl-Instanz ich mich gerade befinde. (es läßt sich nicht mal Classname abrufen) Vielleicht hat jemand eine Idee. Ich weiß nicht mehr weiter. Ich verwende Delphi7 und VirtualTreeview sollte in der Version 4.8.6 vorliegen. (ich habe auf die Schnelle nichts gefunden wo ich das kontrollieren kann) Anbei mal mein Quellcode des Hauptformulars wo alles drin ist was ich mache. Im Anhang füge ich noch ein Archiv mit einer ausführbaren Beispielanwendung und dem komplette Quellcode an. Wahrscheinlich ist es mal wieder ein dumme Kleinigkeit. Vielen Dank schon mal.
Delphi-Quellcode:
unit Main_F;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, VirtualTrees; type TMainForm = class; TTreeData = record Id : integer; Name : string; Quantity : integer; end; PTreeData = ^TTreeData; TMainForm = class(TForm) VST: TVirtualStringTree; procedure VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); procedure VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); procedure VSTEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); procedure VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: WideString); procedure VSTCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); procedure FormShow(Sender: TObject); protected function AddNodeToTree(Tree: TCustomVirtualStringTree; Parent: PVirtualNode; Node: TTreeData): PVirtualNode; procedure LoadData; public end; // Our own edit link to implement several different node editors. TPropertyEditLink = class(TInterfacedObject, IVTEditLink) private FColumn : integer; // The column of the node being edited. FEdit : TWinControl; // One of the property editor classes. FNode : PVirtualNode; // The node being edited. FOldEditProc : TWndMethod; // Used to capture some important messages // regardless of the type of edit control we use. FTree : TVirtualStringTree; // A back reference to the tree calling. protected procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure EditWindowProc(var Message: TMessage); public destructor Destroy; override; procedure ProcessMessage(var Message: TMessage); stdcall; procedure SetBounds(R: TRect); stdcall; 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; end; var MainForm: TMainForm; implementation uses StdCtrls; {$R *.dfm} { TMainForm } function TMainForm.AddNodeToTree(Tree: TCustomVirtualStringTree; Parent: PVirtualNode; Node: TTreeData): PVirtualNode; var pData : PTreeData; begin Result := Tree.AddChild(Parent); pData := Tree.GetNodeData(Result); Tree.ValidateNode(Result, false); pData^.Id := Node.Id; pData^.Name := Node.Name; pData^.Quantity := Node.Quantity; end; procedure TMainForm.FormShow(Sender: TObject); begin //VST.Header.Columns[0].Options := VST.Header.Columns[0].Options + [coVisible]; //VST.Header.Columns[1].Options := VST.Header.Columns[1].Options + [coVisible]; LoadData; end; procedure TMainForm.LoadData; var pNode : PVirtualNode; rData : TTreeData; begin VST.NodeDataSize := SizeOf(TTreeData); VST.BeginUpdate; rData.Id := 1; rData.Name := 'Test 1'; rData.Quantity := 1; pNode := AddNodeToTree(VST, nil, rData); rData.Id := 2; rData.Name := 'Test 2'; rData.Quantity := 7; AddNodeToTree(VST, pNode, rData); VST.EndUpdate; end; procedure TMainForm.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); var pData: PTreeData; begin inherited; pData := Sender.GetNodeData(Node); if Assigned(pData) then pData^.Name := ''; end; procedure TMainForm.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); var iUnitIdx : integer; pData : PTreeData; begin pData := Sender.GetNodeData(Node); if Assigned(pData) then case Column of 0: CellText := IntToStr(pData^.Id) + ' - ' + pData^.Name; 1: CellText := FormatFloat('0.##', pData^.Quantity); end; inherited; end; procedure TMainForm.VSTEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); begin Allowed := true; end; procedure TMainForm.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: WideString); var iUnitIdx : integer; pData : PTreeData; begin inherited; pData := Sender.GetNodeData(Node); if not Assigned(pData) then Exit; case Column of 0 : pData^.Name := NewText; 1 : if StrToIntDef(NewText, -1) < 0 then MessageDlg('Bitte geben Sie eine gültige Menge ein.', mtInformation, [mbOK], 0) else pData^.Quantity := StrToInt(NewText); end; end; procedure TMainForm.VSTCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); begin EditLink := TPropertyEditLink.Create; end; { TPropertyEditLink } function TPropertyEditLink.BeginEdit: boolean; begin Result := true; FEdit.Show; FEdit.SetFocus; // Set a window procedure hook (aka subclassing) to get notified about important messages. FOldEditProc := FEdit.WindowProc; FEdit.WindowProc := EditWindowProc; end; function TPropertyEditLink.CancelEdit: boolean; begin Result := true; // Restore the edit's window proc. FEdit.WindowProc := FOldEditProc; FEdit.Hide; end; destructor TPropertyEditLink.Destroy; begin FreeAndNil(FEdit); inherited; end; procedure TPropertyEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var bCanAdvance: Boolean; begin case Key of VK_RETURN, VK_UP, VK_DOWN: begin // Consider special cases before finishing edit mode. bCanAdvance := Shift = []; if FEdit is TComboBox then bCanAdvance := bCanAdvance and not TComboBox(FEdit).DroppedDown; if bCanAdvance then begin FTree.EndEditNode; if Key = VK_UP then FTree.FocusedNode := FTree.GetPreviousVisible(FTree.FocusedNode) else FTree.FocusedNode := FTree.GetNextVisible(FTree.FocusedNode); FTree.Selected[FTree.FocusedNode] := true; Key := 0; end; end; end; end; procedure TPropertyEditLink.EditWindowProc(var Message: TMessage); begin case Message.Msg of WM_KILLFOCUS: FTree.EndEditNode; else FOldEditProc(Message); end; end; function TPropertyEditLink.EndEdit: boolean; var aBuffer : array[0..1024] of Char; iDummy : integer; pData : PTreeData; rPoint : TPoint; sString : WideString; begin // Check if the place the user click on yields another node as the one we // are currently editing. If not then do not stop editing. GetCursorPos(rPoint); rPoint := FTree.ScreenToClient(rPoint); Result := FTree.GetNodeAt(rPoint.X, rPoint.Y, True, iDummy) <> FNode; if Result then begin // restore the edit's window proc FEdit.WindowProc := FOldEditProc; pData := FTree.GetNodeData(FNode); if FEdit is TComboBox then sString := TComboBox(FEdit).Text else begin GetWindowText(FEdit.Handle, aBuffer, 1024); sString := aBuffer; end; if Assigned(pData) then case FColumn of 0 : begin pData^.Name := sString; FTree.InvalidateNode(FNode); end; 1 : if StrToIntDef(sString, -1) >= 0 then begin pData^.Quantity := StrToInt(sString); FTree.InvalidateNode(FNode); end else MessageDlg('Geben Sie eine gültige Mengenangabe ein.', mtWarning, [mbOK], 0); end; FEdit.Hide; end; end; function TPropertyEditLink.GetBounds: TRect; begin Result := FEdit.BoundsRect; end; function TPropertyEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): boolean; var i : integer; pData : PTreeData; oComboBox : TComboBox; oEdit : TEdit; sItem : string; begin Result := true; FTree := Tree as TVirtualStringTree; FNode := Node; FColumn := Column; // determine what edit type actually is needed FreeAndNil(FEdit); pData := FTree.GetNodeData(Node); case Column of 0 : begin oEdit := TEdit.Create(Tree); oEdit.Visible := false; oEdit.Parent := Tree; oEdit.OnKeyDown := EditKeyDown; if Assigned(pData) then oEdit.Text := pData^.Name else oEdit.Text := ''; FEdit := oEdit; oEdit := nil; end; 1 : if Assigned(pData) then begin oComboBox := TComboBox.Create(Tree); oComboBox.Visible := false; oComboBox.Parent := Tree; oComboBox.Style := csDropDownList; oComboBox.Items.Clear; for i := 1 to 5 do begin oComboBox.Items.Add(IntToStr(i)); if pData^.Quantity = i then oComboBox.ItemIndex := Pred(oComboBox.Items.Count); end; oComboBox.OnKeyDown := EditKeyDown; FEdit := oComboBox; oComboBox := nil; //Referenz wurde in FEdit gespeichert! end else Result := false; else Result := false; end; end; procedure TPropertyEditLink.ProcessMessage(var Message: TMessage); begin if FEdit <> nil then FEdit.WindowProc(Message); end; procedure TPropertyEditLink.SetBounds(R: TRect); var iDummy: integer; begin // Since we don't want to activate grid extensions in the tree (this would influence how the selection is drawn) // we have to set the edit's width explicitly to the width of the column. FTree.Header.Columns.GetColumnBounds(FColumn, iDummy, R.Right); FEdit.BoundsRect := R; end; end. |
Zitat |
Registriert seit: 31. Dez 2002 178 Beiträge Delphi 5 Enterprise |
#2
Inzwischen habe ich paar neue Informationen herausgefunden.
In der Methode TPropertyEditLink.EditKeyDown (also wenn Enter gedrückt wird) wird FTree.EndEditNode aufgerufen. Die wiederrum ruft TBaseVirtualTree.DoEndEdit auf. Dort wird FEditLink := nil gesetzt. Dadurch wird dieser Freigegeben da es sich um eine Instanz von TInterfacesObject handelt. Nun wird in TPropertyEditLink.Destroy das Edit freigegeben. Danach wird jedoch noch die MainWndProc vom Edit aufgerufen. Hier knallt es dann da das Edit ja schon freigegeben wurde. Jetzt weiß ich warum es passiert. Aber wie ich das am besten umgehen ist mir noch nicht ganz klar. Hat jemand einen guten Vorschlag? Geändert von VizeTE (13. Okt 2010 um 20:02 Uhr) |
Zitat |
Registriert seit: 31. Dez 2002 178 Beiträge Delphi 5 Enterprise |
#3
So ich glaube jetzt habe ich die Lösung.
Ich habe einfach alles etwas weniger dynamisch gestaltet und es scheint zu funktionieren. Im Detail habe ich folgendes geändert:
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |