ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/DBTreeView.pas
Revision: 330
Committed: Fri Feb 26 16:16:06 2021 UTC (3 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 20278 byte(s)
Log Message:
Add missing DBTreeview.pas

File Contents

# User Rev Content
1 tony 330 (*
2     * IBX For Lazarus (Firebird Express)
3     *
4     * The contents of this file are subject to the Initial Developer's
5     * Public License Version 1.0 (the "License"); you may not use this
6     * file except in compliance with the License. You may obtain a copy
7     * of the License here:
8     *
9     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10     *
11     * Software distributed under the License is distributed on an "AS
12     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13     * implied. See the License for the specific language governing rights
14     * and limitations under the License.
15     *
16     * The Initial Developer of the Original Code is Tony Whyman.
17     *
18     * The Original Code is (C) 2015 Tony Whyman, MWA Software
19     * (http://www.mwasoftware.co.uk).
20     *
21     * All Rights Reserved.
22     *
23     * Contributor(s): ______________________________________.
24     *
25     *)
26     unit DBTreeView;
27    
28     {$mode objfpc}{$H+}
29    
30     interface
31    
32     uses
33     Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls,
34     DB;
35    
36     type
37     {
38     TDBTreeView is intended to be a data aware descendent of TCustomTreeView and used to display
39     hierarchically structured data in a natural manner. Nodes can be deleted, moved
40     and added to the tree and each change is reflected in the underlying dataset. The
41     Node text can similarly be edited.
42     }
43    
44     TVariantArray = array of variant;
45    
46     TDBTreeView = class;
47    
48     { TDBTreeViewDatalink }
49    
50     TDBTreeViewDatalink = class(TDataLink)
51     private
52     FOwner: TDBTreeView;
53     protected
54     procedure ActiveChanged; override;
55     procedure DataSetChanged; override;
56     procedure RecordChanged(Field: TField); override;
57     procedure UpdateData; override;
58     public
59     constructor Create(AOwner: TDBTreeView);
60     end;
61    
62     { TDBTreeNode }
63    
64     TDBTreeNode = class(TTreeNode)
65     private
66     FKeyValue: variant;
67     public
68     constructor Create(AnOwner: TTreeNodes); override;
69     procedure DeleteAll;
70     property KeyValue: variant read FKeyValue;
71     end;
72    
73     TDBTreeView = class(TCustomTreeView)
74     private
75     { Private declarations }
76     FDataLink: TDBTreeViewDatalink;
77     FHasChildField: string;
78     FImageIndexField: string;
79     FKeyField: string;
80     FSelectedIndexField: string;
81     FTextField: string;
82     FParentField: string;
83     FNoAddNodeToDataset: boolean;
84     FRelationName: string;
85     FModifiedNode: TDBTreeNode;
86     FLastSelected: TVariantArray;
87     procedure ActiveChanged(Sender: TObject);
88     procedure AddNodes;
89     procedure DataSetChanged(Sender: TObject);
90     function GetDataSet: TDataSet;
91     function GetDataSource: TDataSource;
92     function GetSelectedKeyValue: variant;
93     procedure NodeMoved(Node: TTreeNode);
94     procedure RecordChanged(Sender: TObject; Field: TField);
95     procedure SetHasChildField(AValue: string);
96     procedure SetImageIndexField(AValue: string);
97     procedure SetKeyField(AValue: string);
98     procedure SetSelectedIndexField(AValue: string);
99     procedure SetTextField(AValue: string);
100     procedure SetDataSource(AValue: TDataSource);
101     procedure SetParentField(AValue: string);
102     procedure UpdateData(Sender: TObject);
103     protected
104     { Protected declarations }
105     FExpandNode: TTreeNode;
106     FUpdateNode: TDBTreeNode;
107     FUpdating: boolean;
108     FLocatingNode: boolean;
109     procedure Added(Node: TTreeNode); override;
110     procedure DataSourceChanged; virtual; abstract;
111     procedure DoOnDataInit; virtual;
112     procedure Delete(Node: TTreeNode); override;
113     procedure Change(Node: TTreeNode); override;
114     function CreateNode: TTreeNode; override;
115     function CanEdit(Node: TTreeNode): Boolean; override;
116     procedure Expand(Node: TTreeNode); override;
117     function GetRelationNameQualifier: string;
118     procedure Loaded; override;
119     procedure NodeChanged(Node: TTreeNode; ChangeEvent: TTreeNodeChangeReason); override;
120     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
121     procedure NodeUpdated(Node: TTreeNode); virtual;
122     procedure Reinitialise;
123     procedure RefreshDataset; virtual; abstract;
124     function ScrollToNode(Node: TDBTreeNode): boolean; virtual;
125     public
126     { Public declarations }
127     constructor Create(TheComponent: TComponent); override;
128     destructor Destroy; override;
129     function FindNode(KeyValuePath: TVariantArray; SelectNode: boolean): TDBTreeNode; overload;
130     function FindNode(KeyValue: variant): TDBTreeNode; overload;
131     function GetNodePath(Node: TTreeNode): TVariantArray;
132     property DataSet: TDataSet read GetDataSet;
133     property SelectedKeyValue: variant read GetSelectedKeyValue;
134     published
135     { Published declarations }
136     property Align;
137     property Anchors;
138     property AutoExpand;
139     property BorderSpacing;
140     //property BiDiMode;
141     property BackgroundColor;
142     property BorderStyle;
143     property BorderWidth;
144     property Color;
145     property Constraints;
146     property TextField: string read FTextField write SetTextField;
147     property DataSource: TDataSource read GetDataSource write SetDataSource;
148     property DefaultItemHeight;
149     property DragKind;
150     property DragCursor;
151     property DragMode;
152     property Enabled;
153     property ExpandSignColor;
154     property ExpandSignType;
155     property Font;
156     property HideSelection;
157     property HotTrack;
158     property Images;
159     property Indent;
160     property HasChildField: string read FHasChildField write SetHasChildField;
161     property ImageIndexField: string read FImageIndexField write SetImageIndexField;
162     property SelectedIndexField: string read FSelectedIndexField write SetSelectedIndexField;
163     property KeyField: string read FKeyField write SetKeyField;
164     property MultiSelect;
165     property MultiSelectStyle;
166     //property ParentBiDiMode;
167     property ParentColor default False;
168     property ParentField: string read FParentField write SetParentField;
169     property ParentFont;
170     property ParentShowHint;
171     property PopupMenu;
172     property ReadOnly;
173     property RelationName: string read FRelationName write FRelationName;
174     property RightClickSelect;
175     property RowSelect;
176     property ScrollBars;
177     property SelectionColor;
178     property ShowButtons;
179     property ShowHint;
180     property ShowLines;
181     property ShowRoot;
182     property StateImages;
183     property TabOrder;
184     property TabStop default True;
185     property Tag;
186     property ToolTips;
187     property Visible;
188     property OnAddition;
189     property OnAdvancedCustomDraw;
190     property OnAdvancedCustomDrawItem;
191     property OnChange;
192     property OnChanging;
193     property OnClick;
194     property OnCollapsed;
195     property OnCollapsing;
196     property OnCompare;
197     property OnContextPopup;
198     property OnCreateNodeClass;
199     property OnCustomCreateItem;
200     property OnCustomDraw;
201     property OnCustomDrawItem;
202     property OnDblClick;
203     property OnDeletion;
204     property OnDragDrop;
205     property OnDragOver;
206     property OnEdited;
207     property OnEditing;
208     property OnEditingEnd;
209     //property OnEndDock;
210     property OnEndDrag;
211     property OnEnter;
212     property OnExit;
213     property OnExpanded;
214     property OnExpanding;
215     property OnGetImageIndex;
216     property OnGetSelectedIndex;
217     property OnKeyDown;
218     property OnKeyPress;
219     property OnKeyUp;
220     property OnMouseDown;
221     property OnMouseEnter;
222     property OnMouseLeave;
223     property OnMouseMove;
224     property OnMouseUp;
225     property OnNodeChanged;
226     property OnSelectionChanged;
227     property OnShowHint;
228     //property OnStartDock;
229     property OnStartDrag;
230     property OnUTF8KeyPress;
231     property Options;
232     property Items;
233     property TreeLineColor;
234     property TreeLinePenStyle;
235     end;
236    
237     function StrIntListToVar(s: string): TVariantArray;
238     function VarToStrIntList(a: TVariantArray): string;
239    
240     implementation
241    
242     uses Variants;
243    
244     function StrIntListToVar(s: string): TVariantArray;
245     var i, idx: integer;
246     List: TStringList;
247     begin
248     List := TStringList.Create;
249     try
250     idx := 1;
251     List.Clear;
252     while idx <= Length(s) do
253     List.Add(ExtractFieldName(s,idx));
254    
255     Setlength(Result,List.Count);
256     for i := 0 to List.Count - 1 do
257     Result[i] := StrToInt(List[i])
258     finally
259     List.Free
260     end;
261     end;
262    
263     function VarToStrIntList(a: TVariantArray): string;
264     var i: integer;
265     begin
266     for i := 0 to Length(a) - 1 do
267     if VarIsOrdinal(a[i]) then
268     begin
269     if i = 0 then
270     Result := IntToStr(a[i])
271     else
272     Result := Result + ';' + IntToStr(a[i])
273     end
274     else
275     raise Exception.Create('Ordinal Type Expected when converting to integer string');
276     end;
277    
278     { TDBTreeNode }
279    
280     constructor TDBTreeNode.Create(AnOwner: TTreeNodes);
281     begin
282     inherited Create(AnOwner);
283     FKeyValue := NULL;
284     end;
285    
286     procedure TDBTreeNode.DeleteAll;
287     var Node, NextNode: TTreeNode;
288     begin
289     Expand(true);
290     Node := GetFirstChild;
291     while Node <> nil do
292     begin
293     NextNode := Node.GetNextSibling;
294     TDBTreeNode(Node).DeleteAll;
295     Node := NextNode;
296     end;
297     Delete
298     end;
299    
300     { TDBTreeView }
301    
302     procedure TDBTreeView.ActiveChanged(Sender: TObject);
303     begin
304     if (csDesigning in ComponentState) then Exit;
305     DataSourceChanged;
306     if assigned(DataSet) and not DataSet.Active then
307     begin
308     if not assigned(FExpandNode) and not assigned(FUpdateNode) then {must really be closing}
309     Reinitialise
310     end
311     else
312     begin
313     AddNodes;
314     if not FLocatingNode and (Selected = nil) and (Items.TopLvlCount > 0) then
315     begin
316     if Length(FLastSelected) > 0 then
317     Selected := FindNode(FLastSelected,true)
318     else
319     Selected := Items.TopLvlItems[0];
320     end
321     end
322     end;
323    
324     procedure TDBTreeView.AddNodes;
325     var Node: TTreeNode;
326     ChildCount: integer;
327     begin
328     if assigned(FExpandNode) or (Items.Count = 0) then
329     begin
330     ChildCount := 0;
331     FNoAddNodeToDataset := true;
332     try
333     DataSet.First;
334     while not DataSet.EOF do
335     begin
336     if (FExpandNode = nil) or (TDBTreeNode(FExpandNode).KeyValue <> DataSet.FieldByName(KeyField).AsVariant) then
337     begin
338     Node := Items.AddChild(FExpandNode,DataSet.FieldByName(TextField).AsString);
339     Node.HasChildren := (HasChildField = '') or (DataSet.FieldByName(HasChildField).AsInteger <> 0);
340     Inc(ChildCount);
341     end;
342     DataSet.Next
343     end;
344     finally
345     FNoAddNodeToDataset := false
346     end;
347     if assigned(FExpandNode) then
348     FExpandNode.HasChildren := ChildCount > 0;
349     FExpandNode := nil
350     end
351     end;
352    
353     procedure TDBTreeView.DataSetChanged(Sender: TObject);
354     begin
355     // Do nothing;
356     end;
357    
358     function TDBTreeView.GetDataSet: TDataSet;
359     begin
360     Result := FDataLink.DataSet
361     end;
362    
363     function TDBTreeView.GetDataSource: TDataSource;
364     begin
365     Result := FDataLink.DataSource
366     end;
367    
368     function TDBTreeView.GetRelationNameQualifier: string;
369     begin
370     if FRelationName <> '' then
371     Result := FRelationName + '.'
372     else
373     Result := ''
374     end;
375    
376     function TDBTreeView.GetSelectedKeyValue: variant;
377     begin
378     Result := NULL;
379     if assigned(Selected) and (Selected is TDBTreeNode) then
380     Result := TDBTreeNode(Selected).KeyValue
381     end;
382    
383     procedure TDBTreeView.NodeMoved(Node: TTreeNode);
384     begin
385     {Need to update Parent}
386     if ScrollToNode(TDBTreeNode(Node)) then
387     begin
388     FDataLink.Edit;
389     FModifiedNode := TDBTreeNode(Node)
390     end;
391     end;
392    
393     procedure TDBTreeView.NodeUpdated(Node: TTreeNode);
394     begin
395     {Need to Update List Field}
396     if ScrollToNode(TDBTreeNode(Node)) then
397     begin
398     FDataLink.Edit;
399     FModifiedNode := TDBTreeNode(Node);
400     FDataLink.UpdateRecord
401     end;
402     end;
403    
404     procedure TDBTreeView.RecordChanged(Sender: TObject; Field: TField);
405     var Node: TDBTreeNode;
406     Destination: TDBTreeNode;
407     begin
408     if DataSet.State = dsInsert then Exit;
409    
410     if assigned(Field) and (Field.FieldName = TextField) then
411     begin
412     Node := FindNode(DataSet.FieldByName(KeyField).AsVariant);
413     if assigned(Node) then
414     begin
415     FUpdating := true;
416     try
417     Node.Text := Field.Text
418     finally
419     FUpdating := false
420     end;
421     end;
422     end
423     else
424     if assigned(Field) and (Field.FieldName = ImageIndexField) then
425     begin
426     Node := FindNode(DataSet.FieldByName(KeyField).AsVariant);
427     if assigned(Node) then
428     begin
429     FUpdating := true;
430     try
431     Node.ImageIndex := Field.AsInteger
432     finally
433     FUpdating := false
434     end;
435     end;
436     end
437     else
438     if assigned(Field) and (Field.FieldName = ParentField) then
439     begin
440     Node := FindNode(DataSet.FieldByName(KeyField).AsVariant);
441     if assigned(Node) then
442     begin
443     if DataSet.FieldByName(ParentField).IsNull then
444     Destination := nil
445     else
446     Destination := FindNode(DataSet.FieldByName(ParentField).AsVariant);
447    
448     if (Destination = nil) or (Destination = Node.Parent) then Exit;
449    
450     FUpdating := true;
451     try
452     Node.MoveTo(Destination,naAddChild);
453     finally
454     FUpdating := false
455     end;
456     end;
457     end
458     end;
459    
460     procedure TDBTreeView.SetHasChildField(AValue: string);
461     begin
462     if FHasChildField = AValue then Exit;
463     FHasChildField := AValue;
464     Reinitialise
465     end;
466    
467     procedure TDBTreeView.SetImageIndexField(AValue: string);
468     begin
469     if FImageIndexField = AValue then Exit;
470     FImageIndexField := AValue;
471     Reinitialise
472     end;
473    
474     procedure TDBTreeView.SetKeyField(AValue: string);
475     begin
476     if FKeyField = AValue then Exit;
477     FKeyField := AValue;
478     Reinitialise
479     end;
480    
481     procedure TDBTreeView.SetSelectedIndexField(AValue: string);
482     begin
483     if FSelectedIndexField = AValue then Exit;
484     FSelectedIndexField := AValue;
485     Reinitialise;
486     end;
487    
488     procedure TDBTreeView.SetTextField(AValue: string);
489     begin
490     if FTextField = AValue then Exit;
491     FTextField := AValue;
492     Reinitialise
493     end;
494    
495     procedure TDBTreeView.SetDataSource(AValue: TDataSource);
496     begin
497     FDataLink.DataSource := AValue;
498     DataSourceChanged;
499     end;
500    
501     procedure TDBTreeView.SetParentField(AValue: string);
502     begin
503     if FParentField = AValue then Exit;
504     FParentField := AValue;
505     Reinitialise
506     end;
507    
508     function TDBTreeView.ScrollToNode(Node: TDBTreeNode): boolean;
509     begin
510     Result := assigned(DataSet) and DataSet.Active and assigned(Node) and not varIsNull(Node.KeyValue);
511     if Result then
512     begin
513     if DataSet.Active and (DataSet.RecordCount > 0)
514     and DataSet.Locate(KeyField,Node.KeyValue,[]) then Exit;
515    
516     FUpdateNode := Node;
517     try
518     RefreshDataset;
519     finally
520     FUpdateNode := nil
521     end;
522     Result := DataSet.FieldByName(KeyField).AsVariant = Node.KeyValue
523     end;
524     end;
525    
526     procedure TDBTreeView.UpdateData(Sender: TObject);
527     begin
528     if assigned(FModifiedNode) then
529     begin
530     DataSet.FieldByName(TextField).AsString := FModifiedNode.Text;
531     if FModifiedNode.Parent = nil then
532     DataSet.FieldByName(ParentField).Clear
533     else
534     DataSet.FieldByName(ParentField).AsVariant := TDBTreeNode(FModifiedNode.Parent).KeyValue;
535     FModifiedNode := nil
536     end
537     end;
538    
539     procedure TDBTreeView.Added(Node: TTreeNode);
540     begin
541     if assigned(DataSet) and DataSet.Active then
542     begin
543     if not FNoAddNodeToDataset then
544     begin
545     DataSet.Append;
546     if (Node.Text = '') and not DataSet.FieldByName(TextField).IsNull then
547     Node.Text := DataSet.FieldByName(TextField).AsString;
548     FModifiedNode := TDBTreeNode(Node);
549     FDataLink.UpdateRecord;
550     end;
551     DoOnDataInit;
552     TDBTreeNode(Node).FKeyValue := DataSet.FieldByName(KeyField).AsVariant;
553     if ImageIndexField <> '' then
554     Node.ImageIndex := DataSet.FieldByName(ImageIndexField).AsInteger;
555     if SelectedIndexField <> '' then
556     Node.SelectedIndex := DataSet.FieldByName(SelectedIndexField).AsInteger;
557     end;
558     inherited Added(Node);
559     end;
560    
561     procedure TDBTreeView.DoOnDataInit;
562     begin
563     //Do nothing
564     end;
565    
566     procedure TDBTreeView.Delete(Node: TTreeNode);
567     begin
568     if not (tvsUpdating in States) {TreeNodes being cleared}
569     and not (tvsManualNotify in States) {Tree Collapse with node delete}
570     and ScrollToNode(TDBTreeNode(Node)) then
571     DataSet.Delete;
572     inherited Delete(Node);
573     end;
574    
575     procedure TDBTreeView.Change(Node: TTreeNode);
576     begin
577     inherited Change(Node);
578     ScrollToNode(TDBTreeNode(Node));
579     end;
580    
581     function TDBTreeView.CreateNode: TTreeNode;
582     var
583     NewNodeClass: TTreeNodeClass;
584     begin
585     Result := nil;
586     if Assigned(OnCustomCreateItem) then
587     OnCustomCreateItem(Self, Result);
588     if Result = nil then
589     begin
590     NewNodeClass:=TDBTreeNode;
591     if Assigned(OnCreateNodeClass) then
592     OnCreateNodeClass(Self,NewNodeClass);
593     Result := NewNodeClass.Create(Items);
594     end;
595     end;
596    
597     function TDBTreeView.CanEdit(Node: TTreeNode): Boolean;
598     begin
599     Result := inherited CanEdit(Node)
600     and assigned(DataSet) and not DataSet.FieldByName(TextField).ReadOnly
601     end;
602    
603     procedure TDBTreeView.Expand(Node: TTreeNode);
604     begin
605     inherited Expand(Node);
606     if Node.HasChildren and assigned(DataSet) and (Node.GetFirstChild = nil) then
607     begin
608     FExpandNode := Node;
609     RefreshDataset;
610     Selected := Node;
611     end;
612     end;
613    
614     procedure TDBTreeView.Loaded;
615     begin
616     inherited Loaded;
617     DataSourceChanged;
618     Reinitialise
619     end;
620    
621     procedure TDBTreeView.NodeChanged(Node: TTreeNode;
622     ChangeEvent: TTreeNodeChangeReason);
623     begin
624     inherited NodeChanged(Node, ChangeEvent);
625     if not FNoAddNodeToDataset and not FUpdating then
626     case ChangeEvent of
627     ncTextChanged:
628     NodeUpdated(Node);
629     ncParentChanged:
630     NodeMoved(Node);
631     end;
632     end;
633    
634     procedure TDBTreeView.Notification(AComponent: TComponent; Operation: TOperation
635     );
636     begin
637     inherited Notification(AComponent, Operation);
638     if (Operation = opRemove) and
639     (FDataLink <> nil) and (AComponent = DataSource) then
640     DataSource := nil;
641     end;
642    
643     procedure TDBTreeView.Reinitialise;
644     begin
645     if [csDesigning,csLoading] * ComponentState <> [] then Exit;
646     FLastSelected := GetNodePath(Selected);
647     Items.Clear;
648     end;
649    
650     constructor TDBTreeView.Create(TheComponent: TComponent);
651     begin
652     inherited Create(TheComponent);
653     FDataLink := TDBTreeViewDatalink.Create(self);
654     end;
655    
656     destructor TDBTreeView.Destroy;
657     begin
658     if assigned(FDataLink) then FDataLink.Free;
659     inherited Destroy;
660     end;
661    
662     function TDBTreeView.FindNode(KeyValuePath: TVariantArray; SelectNode: boolean
663     ): TDBTreeNode;
664     var Node: TTreeNode;
665     i,j: integer;
666     begin
667     Result := nil;
668     if Length(KeyValuePath) = 0 then Exit;
669    
670     FLocatingNode := true;
671     try
672     for j := 0 to Items.TopLvlCount - 1 do
673     begin
674     Node := Items.TopLvlItems[j];
675     i := 0;
676     Node.Expand(false);
677     while assigned(Node) do
678     begin
679     if not VarIsNull(TDBTreeNode(Node).KeyValue) and
680     (TDBTreeNode(Node).KeyValue = KeyValuePath[i]) then
681     begin
682     Inc(i);
683     if i = Length(KeyValuePath) then
684     begin
685     Result := TDBTreeNode(Node);
686     if SelectNode then
687     Selected := Node;
688     Exit
689     end
690     else
691     begin
692     Node.Expand(false);
693     Node := Node.GetFirstChild;
694     end
695     end
696     else
697     Node := Node.GetNextSibling
698     end
699     end
700     finally
701     FLocatingNode := false
702     end
703     end;
704    
705     function TDBTreeView.FindNode(KeyValue: variant): TDBTreeNode;
706     var i: integer;
707     begin
708     Result := nil;
709     if (Selected <> nil) and (TDBTreeNode(Selected).KeyValue = KeyValue) then
710     Result := TDBTreeNode(Selected)
711     else
712     {Find it the hard way}
713     begin
714     FullExpand;
715     for i := 0 to Items.Count -1 do
716     if TDBTreeNode(Items[i]).KeyValue = KeyValue then
717     begin
718     Result := TDBTreeNode(Items[i])
719     end;
720     end;
721     end;
722    
723     function TDBTreeView.GetNodePath(Node: TTreeNode): TVariantArray;
724     var aParent: TTreeNode;
725     i: integer;
726     begin
727     if not assigned(Node) or not (Node is TDBTreeNode) then
728     SetLength(Result,0)
729     else
730     begin
731     {Count length of Path}
732     i := 1;
733     aParent := Node.Parent;
734     while (aParent <> nil) do
735     begin
736     Inc(i);
737     aParent := aParent.Parent
738     end;
739    
740     {Save Path}
741     Setlength(Result,i);
742     while i > 0 do
743     begin
744     Dec(i);
745     Result[i] := TDBTreeNode(Node).KeyValue;
746     Node := Node.Parent
747     end;
748     end;
749     end;
750    
751     { TDBTreeViewDatalink }
752    
753     procedure TDBTreeViewDatalink.ActiveChanged;
754     begin
755     FOwner.ActiveChanged(self)
756     end;
757    
758     procedure TDBTreeViewDatalink.DataSetChanged;
759     begin
760     FOwner.DataSetChanged(self)
761     end;
762    
763     procedure TDBTreeViewDatalink.RecordChanged(Field: TField);
764     begin
765     FOwner.RecordChanged(self,Field);
766     end;
767    
768     procedure TDBTreeViewDatalink.UpdateData;
769     begin
770     FOwner.UpdateData(self)
771     end;
772    
773     constructor TDBTreeViewDatalink.Create(AOwner: TDBTreeView);
774     begin
775     inherited Create;
776     FOwner := AOwner
777     end;
778    
779     end.