📄 dbtree.pas
字号:
procedure DoRefreshText;
var
i: Integer;
begin
for i := 0 to NewTVRecordList.Count - 1 do
begin
with TTvRecordInfo(NewTVRecordList[i]) do
if (TTvRecordInfo(FTVRecordList[i]).Text <> Text) then
begin
Node := GetIDNode(ID);
if (Node <> nil) and (Node.Text <> Text) then
Node.Text := Text;
end;
end;
end; { DoRefreshText }
procedure DoTheBuild;
begin
if (TopItem = nil) then
TopItemID := FRootID
else
TopItemID := IDOfNode(TopItem);
SelectedItemID := SelectedID;
StoreExpanded;
Items.Clear;
if Assigned(FSaveIDList) then
begin
FSaveIDList.Free;
FSaveIDList := nil;
end;
if (PreviousSortType <> stNone) then
{ adding nodes goes much faster without sorting: }
SortType := stNone;
DoCreateTree; { find root and call CreateTree }
if (PreviousSortType <> stNone) then
SortType := PreviousSortType;
Exclude(FState, dtvsBuilding);
if (Items.Count = 0) then
exit;
ReStoreExpanded; { Expand previous expanded items }
{ Find previous TopItem: }
Node := GetIDNode(TopItemID);
if (Node <> nil) then
TopItem := Node;
{ Find previous Selected: }
if (dtSynchronizeDataSet in Options) then
begin
{ Select current record: }
Node := GetDataSetIDNode;
if (Node = nil) then
begin
{ Current record not in Items: }
if (Selected = nil) and (TopItem <> nil) then
Selected := TopItem;
if (Selected <> nil) then
SynchronizeCurrentRecordToSelectedNode;
end
else
Selected := Node;
end
else
begin
if (SelectedItemID <> '') then
begin
Node := GetIDNode(SelectedItemID);
if (Node = nil) then
begin
if (TopItem <> nil) then
Selected := TopItem;
end
else
Selected := Node;
end
else
if (TopItem <> nil) then
Selected := TopItem;
end;
end; { DoTheBuild }
begin { BuildTree }
FRSelected := nil;
KillAllTimer;
FIDOfDeleted := '';
if not NeedRebuild then exit;
Exclude(FState, dtvsNeedReBuild);
PreviousSortType := SortType;
Items.BeginUpdate; { changes are not visible until EndUpdate }
try
DataSet.DisableControls;
Include(FState, dtvsBuilding);
if (dtvsDataSetNeedsRefresh in FState) then
begin
{$IFDEF Ver90}
DataSet.UpdateCursorPos;
if (DbiForceReread(DataSet.Handle) = 0) then
DataSet.Resync([]);
{$ELSE DEF Ver90} { Delphi >= 3.0: }
if (Dataset is TBDEDataSet) then
begin
TBDEDataSet(DataSet).UpdateCursorPos;
if (DbiForceReread(TBDEDataSet(DataSet).Handle) = 0) then
DataSet.Resync([]);
end;
{$ENDIF DEF Ver90}
Exclude(FState, dtvsDataSetNeedsRefresh);
end;
NewTVRecordList := CreateTVRecordList;
if (NewTVRecordList <> nil) then
begin
try
case NewTVRecordList.GetDifference(FTVRecordList) of
tvrldNone:
if (Items.GetFirstNode <> nil) and
(IDOfNode(Items.GetFirstNode) <> FRootID) then
{ RootID has changed: }
DoTheBuild;
tvrldText:
DoRefreshText;
else
DoTheBuild;
end; { case }
finally
if Assigned(FTVRecordList) then
FTVRecordList.Free;
FTVRecordList := NewTVRecordList;
end;
end;
finally
Include(FState, dtvsBuilding);
DataSet.EnableControls;
Exclude(FState, dtvsBuilding);
Items.EndUpdate; { make changes visible }
Node := Selected;
if (Node <> nil) then
begin
Node.MakeVisible;
if (dtvsEditAfterReBuild in FState) then
Node.EditText;
end;
Exclude(FState, dtvsEditAfterReBuild);
end;
end;
function TCustomDBTreeView.NeedRebuild: Boolean;
begin
{ Function BuildTree produce some side effects by changing
following properties, fields:
1. DataSet's current record;
2. Table.IndexFieldNames;
3. Table.State -> dsBrowse;
4. Table's buffer for searching;
There are no ways retain and later restore State
and search buffer(without using private methods).
Therefore Building method is called only in Browse state,
and when not used GoToKey method. }
Result := false;
if (FTableIDField = '') or (FTableTextField = '') or
(csLoading in ComponentState) or (DataSet = nil) then
exit;
with DataSet do
begin
FPrevState := State;
if (dtvsBuilding in FState) or (FDisableCount > 0) then
exit;
if State = dsBrowse then
Result := true;
if (FPrevState = dsSetKey) and (State = dsBrowse) then
Result := false; { Catch calls of GoToKey method }
end;
end;
procedure TCustomDBTreeView.CreateTree(ParentNode: TTreeNode;
const AParent: string; TempRecordList: TTVRecordList);
var
Node: TTreeNode;
Index: Integer;
begin
if TempRecordList.FindParent(AParent, Index) then
begin
with TempRecordList.Parent[Index] do
begin
if (ID = FRootID) or (ID = AParent) then
begin
ClosedLoop;
exit;
end;
Node := Items.AddChild(ParentNode, Text);
TTreeIDNode(Node).ID := ID;
CreateTree(Node, ID, TempRecordList);
end;
Inc(Index);
while (Index < TempRecordList.Count) do
begin
with TempRecordList.Parent[Index] do
begin
if (Parent <> AParent) then
break;
if (ID = FRootID) or (ID = AParent) then
ClosedLoop
else
begin
Node := Items.Add(Node, Text);
TTreeIDNode(Node).ID := ID;
CreateTree(Node, ID, TempRecordList);
end;
end;
Inc(Index);
end;
end;
end;
procedure TCustomDBTreeView.BuildTreeIfNeeded;
begin
if (dtvsNeedReBuild in FState) and (FDisableCount = 0) and
(not (dtvsBuilding in FState)) and
(DataSet.State = dsBrowse) and
(FIDOfDeleted = '') and
((not (dtRebuildFocusedOnly in Options)) or Focused) then
BuildTree;
end;
procedure TCustomDBTreeView.ClosedLoop;
begin
if Assigned(FOnClosedLoop) then
FOnClosedLoop(Self);
end;
procedure TCustomDBTreeView.RootNotFound;
begin
{ if (csDesigning in ComponentState) then
ShowMessage('No record with root-ID (' + FRootID + ') found.'); {}
if Assigned(FOnRootNotFound) then
FOnRootNotFound(Self);
end;
function TCustomDBTreeView.AddNewNodeFromDataset(
Node: TTreeNode; AsChild: Boolean): TTreeNode;
begin
with DataSet do
begin
if not AsChild or (Node = nil) then
Result := Items.Add(Node, FieldByName(FTableTextField).AsString)
else
Result := Items.AddChild(Node, FieldByName(FTableTextField).AsString);
if (Result <> nil) then
TTreeIDNode(Result).ID := FieldByName(FTableIDField).AsString;
end;
end;
function TCustomDBTreeView.GetIDNode(const aID: string): TTreeNode;
var
i: Integer;
begin
for i := 0 to Items.Count -1 do
begin
Result := TTreeNode(Items[i]);
if (IDOfNode(Result) = aID) then
exit;
end;
Result := nil
end;
function TCustomDBTreeView.GetDataSetIDNode: TTreeNode;
begin
Result := GetIDNode(DataSet.FieldByName(FTableIDField).AsString);
end;
procedure TCustomDBTreeView.DataChanged;
begin
if (dtvsChangingDataset in FState) or (dtvsBuilding in FState) then
{ TCustomDBTreeView has changed Dataset, do nothing: }
Exclude(FState, dtvsChangingDataset)
else
RecordNumberChanged;
end;
procedure TCustomDBTreeView.DatasetRefreshed;
begin
Include(FState, dtvsNeedReBuild);
Include(FState, dtvsDataSetNeedsRefresh);
if (FReBuildTimer = 0) and
(Focused or not (dtRebuildFocusedOnly in Options)) then
FReBuildTimer := SetTimer(Handle, TimerIDRebuild, RebuildTickCount, nil);
end;
procedure TCustomDBTreeView.RecordNumberChanged;
begin
if (FDisableCount = 0) and (not (dtvsBuilding in FState)) and
(not IsEditing) and (FIDOfDeleted = '') then
if (dtvsNeedReBuild in FState) and
((not (dtRebuildFocusedOnly in Options)) or Focused) then
BuildTree
else
if (dtSynchronizeDataSet in Options) then
SynchronizeSelectedNodeToCurrentRecord
end;
procedure TCustomDBTreeView.RecordChanged(Field: TField);
var
Node: TTreeNode;
FieldName: string;
begin
if (FDisableCount = 0) and not (dtvsBuilding in FState) and
(Field <> nil) then
begin
FieldName := UpperCase(Field.FieldName);
if (FieldName = UpperCase(FTableTextField)) then
begin
Node := GetDataSetIDNode;
if Node <> nil then
begin
Include(FState, dtvsChangingDataset);
Node.Text := DataSet.FieldByName(FTableTextField).AsString;
end;
end
else
if (FieldName = UpperCase(FTableParentField)) or
(FieldName = UpperCase(FTableIDField)) or
((DataSet is TTable) and
(Pos(FieldName, UpperCase(TTable(DataSet).MasterFields)) > 0)) or
(DataSet.Filtered and
(Pos(FieldName, UpperCase(DataSet.Filter)) > 0)) then
begin
{ maybe structure of tree changed: }
if (DataSet.State = dsEdit) or (DataSet.State = dsInsert) then
Include(FState, dtvsNeedReBuildAfterPost)
else
if (not (dtRebuildFocusedOnly in Options)) or Focused then
BuildTree
else
Include(FState, dtvsNeedReBuild);
end;
end;
end;
procedure TCustomDBTreeView.EditingChanged;
var
WasFocused: Boolean;
begin
if (DataSet.State = dsEdit) then
begin
if (FDisableCount = 0) and not ReadOnly and not IsEditing then
begin
{ DataSet.State has changed from dsEdit to dsBrowse, but that was not
done by TCustomDBTreeView: }
if (dtFocusOnEdit in Options) then
SetFocus;
if Focused then
begin
{ TCustomDBTreeView is Focused: Set selected node to edit-mode: }
if not (dtSynchronizeDataSet in Options) then
SynchronizeSelectedNodeToCurrentRecord;
if (Selected <> nil) then
Selected.EditText;
end;
end;
Include(FState, dtvsDatasetInEditMode);
end
else
if (DataSet.State = dsInsert) then
begin
if (FDisableCount = 0) and not ReadOnly then
begin
{ DataSet was set by someone (not by us) to insert-mode: }
WasFocused := Focused;
Inc(FDisableCount); { avoid BuildTree at next action: }
DataSet.Cancel; { Cancel insert-mode, because... }
Insert(dtInsertAsChild in Options); { ... we will do it our way! }
if (not WasFocused) then
begin
if (dtFocusOnEdit in Options) then
SetFocus
else
begin
{ We don't have the focus, do not edit node: }
if (Selected <> nil) then
Selected.EndEdit(false);
{ If we get the focus later, please edit node: }
Include(FState, dtvsLostFocusWhileDatasetInEditModes);
end;
end;
Dec(FDisableCount);
end;
Include(FState, dtvsDatasetInInsertMode);
end;
end;
procedure TCustomDBTreeView.DataSetBeforePost;
begin
if (dtvsDatasetInEditMode in FState) then
begin
{ DataSet.State has changed from dsEdit to dsBrowse, but that was not
done by TCustomDBTreeView: }
Exclude(FState, dtvsDatasetInEditMode);
if (FDisableCount = 0) and IsEditing then
begin
{ TCustomDBTreeView is still in edit-mode but someone posted e.g.
by pressing post on a DBNavigator. End edit: }
Inc(FDisableCount);
with Selected do
begin
EndEdit(false);
if (DataSet.FieldByName(FTableTextField).AsString <> Text) then
DataSet.FieldByName(FTableTextField).AsString := Text;
end;
Dec(FDisableCount);
end;
end
else
begin
if (dtvsDatasetInInsertMode in FState) then
begin
{ DataSet.State has changed from dsInsert to dsBrowse, but that was
not done by TCustomDBTreeView: }
Exclude(FState, dtvsDatasetInInsertMode);
if (FDisableCount = 0) then
begin
if IsEditing then
begin
{ TCustomDBTreeView is still in insert-mode but someone posted
e.g. by pressing post on a DBNavigator. End insert: }
Inc(FDisableCount);
with Selected do
begin
EndEdit(false);
if (DataSet.FieldByName(FTableTextField).AsString <> Text) then
DataSet.FieldByName(FTableTextField).AsString := Text;
end;
Dec(FDisableCount);
end
else
begin
{ insert-mode was not done by us.
Structure of tree has changed: }
Include(FState, dtvsNeedReBuildAfterPost);
end;
end;
end;
end;
Exclude(FState, dtvsLostFocusWhileDatasetInEditModes);
end;
procedure TCustomDBTreeView.DataSetAfterPost;
begin
if (dtvsNeedReBuildAfterPost in FState) then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -