TTreeView完整的枚举和递归算法
一、TTreeView
(一)、重要属性
Memo_Test.Lines.Add(
‘TTreeView重要属性:’
+',1、可见数:'+IntToStr((LAddObject as TTreeViewItem).VisibleCount) //:该节点所有展开的下级总数
+',2、Item个数:'+IntToStr((LAddObject as TTreeViewItem).Count) //:该节点所有下级总数>=0,末级Count=0
+',3、几级:'+IntToStr((LAddObject as TTreeViewItem).Level) //:该节点的级数:级数>=1
+',4、全局索引:'+IntToStr((LAddObject as TTreeViewItem).GlobalIndex) //:该节点所有展开的下级的重新的自动索引(作用不大)
);
5、该节点的父节点:
(LAddObject as TTreeViewItem).ParentItem
6、该节点被勾选IsChecked:
if (LAddObject as TTreeViewItem).IsChecked=True then ;
7、该节点是否展开其下级、展开或收拢其下级:
if FTreeViewItemCurrt.IsExpanded=false then
FTreeViewItemCurrt.IsExpanded:=true
else FTreeViewItemCurrt.IsExpanded:=false;
8、TreeView展开或收拢所有下级:
FIFCollapse:=not FIFCollapse;
if FIFCollapse=false then
TreeView.CollapseAll //收拢
else TreeView.ExpandAll; //展开
9、TreeView改变了勾选状态:
procedure TreeViewChangeCheck(Sender: TObject);
begin
if (LAddObject as TTreeViewItem).IsChecked=True then ; //:该节点改变了勾选状态
if (LAddObject as TTreeViewItem).ParentItem.IsChecked=True then ; //:该节点的父节点改变了勾选状态
end;
10、TreeView的某节点被点击:
LTreeViewItem.OnClick:=LAddObjectOnClick; //:定义某节点的点击事件:LAddObjectOnClick:写代码
11、TreeView的动态增加节点:
LTreeViewItem:=TTreeViewItem.Create( AOwnerObject ); //:所有者:应为窗体或TFrame
LTreeViewItem.Index:=AIndex; //:11.1、索引会被TTreeView的节点自动索引所覆盖:应使用自定义索引LTreeViewItem.Tag
LTreeViewItem.Name:=AName; //:11.2、节点命名:不能重复
LTreeViewItem.Text:=AText; //:11.3、节点的显示文本
LTreeViewItem.Parent:=AParent; //:11.4、父组件:关键:父组件可以是TreeView,也可以是某个TreeViewItem节点
//LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AParent.Name;
LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AText; //:11.5、节点的个性化标签文字
LTreeViewItem.ImageIndex:=ImageIndex; //:11.6、节点的个性化图片索引
LTreeViewItem.Tag:=AIndex; //:11.7、自定义索引LTreeViewItem.Tag
SetLength(FTreeViewItemCheckedCount,LTreeViewItem.Tag+1); //:11.8:TreeViewItem被勾中的数组长度
FTreeViewItemCheckedCount[LTreeViewItem.Tag]:=0; //:初始化TreeViewItem被勾中的数组
//:初始化TreeViewItem被勾中的数组TreeView.OnChangeCheck
///<summary>动态新增TreeView的TreeViewItem节点:</summary>
function TForm2.CreateATreeViewItems(
const AIndex:Integer;
AName:string;
AText:string;
ATreeFiledName:string;
AParent:TFmxObject;
//:非常关键的参数:指明该节点:
//:其父节点是TTreeView还是它的上级递归TTreeViewItem
ImageIndex:Integer;
ASizeHeight,ASizeWidth:Single;
MarginsLeft:Single;
AOwnerObject:TFmxObject
):TTreeViewItem;
var LTreeViewItem:TTreeViewItem;
begin
InitATreeViewItems(TreeView_WorkSpace_Menus,FIFInited);
LTreeViewItem:=TTreeViewItem.Create( AOwnerObject ); //:所有者:应为窗体或TFrame
LTreeViewItem.Index:=AIndex;//:索引会被TTreeView的节点自动索引所覆盖:应使用自定义索引LTreeViewItem.Tag
LTreeViewItem.Name:=AName; //:节点命名:不能重复
LTreeViewItem.Text:=AText; //:节点的显示文本
LTreeViewItem.Parent:=AParent;//:父组件:关键:父组件可以是TreeView,也可以是某个TreeViewItem节点
//LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AParent.Name;
LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AText; //:节点的个性化标签文字
LTreeViewItem.ImageIndex:=ImageIndex; //:节点的个性化图片索引
LTreeViewItem.Tag:=AIndex; //:自定义索引LTreeViewItem.Tag
SetLength(FTreeViewItemCheckedCount,LTreeViewItem.Tag+1);
FTreeViewItemCheckedCount[LTreeViewItem.Tag]:=0;
//:初始化TreeViewItem勾中数组TreeView.OnChangeCheck
LTreeViewItem.Align:=TAlignLayout.Left;
LTreeViewItem.IsChecked:=false;
LTreeViewItem.IsExpanded:=false;
LTreeViewItem.IsSelected:=false;
LTreeViewItem.StyleLookup:='treeviewitemstyle';
LTreeViewItem.StyledSettings:=
LTreeViewItem.StyledSettings-
[TStyledSetting.Family,
TStyledSetting.Size,
TStyledSetting.FontColor,
TStyledSetting.Other
];
LTreeViewItem.TextSettings.FontColor
:=TAlphaColor($FF1373A9);
LTreeViewItem.TextSettings.Font.Family:='微软雅黑';
LTreeViewItem.TextSettings.Font.Size:=16;
LTreeViewItem.Size.Height:=ASizeHeight;
LTreeViewItem.Size.Width:=ASizeWidth;
LTreeViewItem.Margins.Left:=MarginsLeft;
LTreeViewItem.Position.Y:=AIndex*40;
LTreeViewItem.IsExpanded:=false;
LTreeViewItem.OnClick:=LAddObjectOnClick;
Result:=LTreeViewItem;
end;
(二)、关键处理好
12、if (LAddObject as TTreeViewItem).Level=1 then ; //:处理好顶级
13、if ( (LAddObject as TTreeViewItem).Level=1) and ((LAddObject as TTreeViewItem).Count>0) then ; //:处理好顶级但非末级的节点
14、if ( (LAddObject as TTreeViewItem).Level=1) and ((LAddObject as TTreeViewItem).Count=0) then ; //:处理好顶级且为末级的节点
15、if ( (LAddObject as TTreeViewItem).Level>1) and ((LAddObject as TTreeViewItem).Count>0) then ; //:处理好中间级的节点
16、if ( (LAddObject as TTreeViewItem).Level>1) and ((LAddObject as TTreeViewItem).Count=0) then ; //:处理好非顶级且为末级的节点
二、TTreeView枚举其下被勾选的节点
procedure TForm2.ControlAction_btnEnumCheckedExecute(Sender: TObject);
begin
//遍历TreeView选中的节点:
FTreeViewSelectAllOrNot:=false;
EnumChecked(false,FTreeViewSelectAllOrNot);
end;
function TForm2.EnumChecked(
const AIFAncester:Boolean=true;
AIFUpdateCheckedStatus:Boolean=true):TStringList;
var LAddObject:TFmxObject;
LStringList:TStringList;
LCircle:Integer;
LSeparator: array of char;
LLineOfStringList :TArray<string>;
begin
//枚举TreeView选中的节点并决定是否变更选中状态:
LStringList:=TStringList.Create;
LTreeViewItemCount:=0;
try
TreeView_WorkSpace_Menus.EnumControls(
procedure (const AControl: TControl; var Done: boolean)
begin
if (AControl.ClassName = 'TTreeViewItem') then
begin
LAddObject:=TTreeViewItem(AControl);
if (AIFUpdateCheckedStatus=true) then
begin
if (LAddObject as TTreeViewItem).IsChecked=True then
(LAddObject as TTreeViewItem).IsChecked:=false
else (LAddObject as TTreeViewItem).IsChecked:=True;
end;
if (LAddObject as TTreeViewItem).IsChecked=True then
begin
if (AIFAncester=true) then
begin
if (LAddObject.TagString).Substring(
(LAddObject.TagString).IndexOf('=')+1
,length(LAddObject.TagString) )=(LAddObject as TTreeViewItem).Text
then
LStringList.Add(LAddObject.TagString);
//:格式:LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AParent.Name;
end else
if (AIFAncester=false) then
begin
if ( (LAddObject as TTreeViewItem).Count=0 )
then //:找到末级反推上级
begin
if (LAddObject as TTreeViewItem).Level>1 then
begin
if ((LAddObject as TTreeViewItem).ParentItem.Count
=FTreeViewItemCheckedCount[(LAddObject as TTreeViewItem).ParentItem.Tag]) then
begin //:找到了上级打勾:即下级全打勾
if LStringList.Text.Contains((LAddObject as TTreeViewItem).ParentItem.TagString)
=false then
begin
//Memo_Test.Lines.Add('找到了上级打勾');
LStringList.Add((LAddObject as TTreeViewItem).ParentItem.TagString);
end;
Done:=false;
end else
begin //:else:找到了只有个别下级打勾
if LStringList.Text.Contains((LAddObject as TTreeViewItem).TagString)
=false then
begin
//Memo_Test.Lines.Add('找到了只有个别下级打勾');
LStringList.Add((LAddObject as TTreeViewItem).TagString);
end;
Done:=false;
end;
end;
if (LAddObject as TTreeViewItem).Level=1 then
begin //:末级是顶级
if LStringList.Text.Contains((LAddObject as TTreeViewItem).TagString)
=false then
begin
//Memo_Test.Lines.Add('找到了末级是顶级的节点');
LStringList.Add((LAddObject as TTreeViewItem).TagString);
end;
Done:=false;
end;
end;
//:格式:LTreeViewItem.TagString:=ATreeFiledName.Trim+'='+AText;
end;
end;
end;
Done:=false;//:不要停继续找下一个
end
,true //:只枚举可见的组件
);
//LStringList.Sort; //:枚举类型只能按照索引号index排序
//LStringList的调用方法如下(可传递给sql条件拼接):测试枚举结果:
//{
//Memo_Test.Lines.Clear;
Memo_Test.BringToFront; Memo_Test.Visible:=true;
Memo_Test.Position.X:=361;
Memo_Test.BeginUpdate;
SetLength(LSeparator,1);
LSeparator[0]:='=';
for LCircle := 0 to LStringList.Count-1 do
begin
SetLength(LLineOfStringList,length(LStringList[LCircle]));
LLineOfStringList[0]:=LStringList[LCircle].Split(LSeparator[0])[0];
LLineOfStringList[1]:=LStringList[LCircle].Split(LSeparator[0])[1];
Memo_Test.Lines.Add(LLineOfStringList[0]+'='+LLineOfStringList[1]);
//Memo_Test.Lines.Add(LStringList[LCircle]);
end;
Memo_Test.EndUpdate; //}
Result:=LStringList;
finally
FreeAndNil(LStringList);
end;
end;
三、TTreeView与枚举和递归算法:TTreeView枚举并递归加载外部数据源
///<summary>某节点TTreeViewItem被勾中或取消勾中处理其节点及其上下级节点:</summary>
procedure TForm2.TreeView_WorkSpace_MenusChangeCheck(Sender: TObject);
var LLevel:Integer;
procedure LEnumControls(LFmxObject:TFmxObject;EnumControlClassName:string);
begin
//1、处理ChangeCheck的上级:
if (LFmxObject as TTreeViewItem).IsChecked=true then
begin
if (LLevel>1) then//:基线条件
begin
try
if FTreeViewItemCheckedCount[FTreeViewItemTag]
< (LFmxObject as TTreeViewItem).ParentItem.Count then
begin
if LErgodicParent<>'父已枚举' then
INC(FTreeViewItemCheckedCount[FTreeViewItemTag]);
//:上级+1
LErgodicParent:='';
Memo_Test.Lines.Add('枚举对象:'+(Sender as TTreeViewItem).Text+':上级:'+(Sender as TTreeViewItem).ParentItem.Text+':被勾中数:'+IntToStr(FTreeViewItemCheckedCount[FTreeViewItemTag]) );
end;
finally
if FTreeViewItemCheckedCount[FTreeViewItemTag]
= (Sender as TTreeViewItem).ParentItem.Count then
begin
LErgodicParent:='父已枚举';
if (Sender as TTreeViewItem).ParentItem.IsChecked=false then
(Sender as TTreeViewItem).ParentItem.IsChecked:=true;
//:开始递归:上级枚举
end;
end;
end else
if (LLevel=1) then//:基线条件
begin
if FTreeViewItemCheckedCount[FTreeViewItemTag]
< (LFmxObject as TTreeViewItem).Count then
begin
if LErgodicParent<>'父已枚举' then
INC(FTreeViewItemCheckedCount[FTreeViewItemTag]);
//:上级+1
LErgodicParent:='';
Memo_Test.Lines.Add('枚举对象:'+(Sender as TTreeViewItem).Text+':上级:'+(Sender as TTreeViewItem).Text+':被勾中数:'+IntToStr(FTreeViewItemCheckedCount[FTreeViewItemTag]) );
end;
end;
end else
if (LFmxObject as TTreeViewItem).IsChecked=false then
begin
if (LLevel>1) then//:基线条件
begin
try
if FTreeViewItemCheckedCount[FTreeViewItemTag]
>0 then
begin
if LErgodicParent<>'父已枚举' then
DEC(FTreeViewItemCheckedCount[FTreeViewItemTag]);
//:上级-1
LErgodicParent:='';
Memo_Test.Lines.Add('枚举对象:'+(Sender as TTreeViewItem).Text+':上级:'+(Sender as TTreeViewItem).ParentItem.Text+':被勾中数:'+IntToStr(FTreeViewItemCheckedCount[FTreeViewItemTag]) );
end;
finally
if FTreeViewItemCheckedCount[FTreeViewItemTag]
= 0 then
begin
LErgodicParent:='父已枚举';
if (Sender as TTreeViewItem).ParentItem.IsChecked=true then
(Sender as TTreeViewItem).ParentItem.IsChecked:=false;
//:开始递归:上级枚举
end;
end;
end else
if (LLevel=1) then//:基线条件
begin
if FTreeViewItemCheckedCount[FTreeViewItemTag]
>0 then
begin
if LErgodicParent<>'父已枚举' then
DEC(FTreeViewItemCheckedCount[FTreeViewItemTag]);
//:上级-1
LErgodicParent:='';
Memo_Test.Lines.Add('枚举对象:'+(Sender as TTreeViewItem).Text+':上级:'+(Sender as TTreeViewItem).Text+':被勾中数:'+IntToStr(FTreeViewItemCheckedCount[FTreeViewItemTag]) );
end;
end;
end;
//2、处理ChangeCheck的本级枚举:
try
LErgodicParent:='';
LFmxObject.EnumObjects(//const Proc: TFunc<TFmxObject, TEnumProcResult>
function (LControl:TFmxObject):TEnumProcResult
var LEnumProcResult:TEnumProcResult;
begin
//2.1、处理ChangeCheck的本级枚举到其下级:
if LControl.ClassName=EnumControlClassName then
begin
if EnumControlClassName='TTreeViewItem' then
begin
if (LFmxObject as TTreeViewItem).IsChecked=true then
begin
//Memo_Test.Lines.Add('枚举到了下级');
if (LControl as TTreeViewItem).IsChecked=false then
begin
(LControl as TTreeViewItem).IsChecked:=true;
end;
//:开始递归:(本级有下级)中间级枚举
end;
if (LFmxObject as TTreeViewItem).IsChecked=false then
begin
if (LControl as TTreeViewItem).IsChecked=true then
begin
(LControl as TTreeViewItem).IsChecked:=false;
end;
//:开始递归:(本级有下级)中间级枚举
end;
end;
end;
//2.1、处理ChangeCheck的本级枚举为末级:
if LControl.ClassName<>EnumControlClassName then
begin
//Memo_Test.Lines.Add('末级被勾中:没有枚举到下级');
//Memo_Test.Lines.Add('枚举对象:'+(LFmxObject as TTreeViewItem).Text+':上级:'+(LFmxObject as TTreeViewItem).ParentItem.Text+':被勾中数:'+IntToStr(FTreeViewItemCheckedCount[FTreeViewItemTag]) );
end;
Result:=TEnumProcResult.Continue;//:直到再无可枚举的下级
end
);
finally
end;
end;
begin
if FIFInited=false then exit//:必须先完成初始化: 初始化在Checked:=false
else
begin
LLevel:=(Sender as TTreeViewItem).Level;
if (LLevel=1) then
begin
FTreeViewItemTag:=(Sender as TTreeViewItem).Tag;
//1、枚举子节点及其下级节点,自动勾或去勾:
//:同时子节点会开始递归本单元:
LEnumControls((Sender as TTreeViewItem),'TTreeViewItem');
end else
if (LLevel>1) then
begin
FTreeViewItemTag:=(Sender as TTreeViewItem).ParentItem.Tag;
//1、枚举子节点及其下级节点,自动勾或去勾:
//:同时子节点会开始递归本单元:
LEnumControls((Sender as TTreeViewItem),'TTreeViewItem');
end;
end;
end;
四、TreeView父Item和子Item勾选互动:
TTreeView = class(TCustomTreeView)
published
property OnChangeCheck; //.....(还有很多发布属性)
//:该属性的父类为受保护属性: protected TCustomTreeView.OnChangeCheck
end;
TMyTreeView =Class(TTreeView)
private
protected
public
published
// 我要改写并将其发布出来供调用者和RttiType使用:
procedure OnChangeCheck( AItem :TObject );//:protected TCustomTreeView.OnChangeCheck
end;
// 实现代码如下:
implementation
{$R *.fmx}
{TMyTreeView}
procedure TMyTreeView.OnChangeCheck( AItem :TObject );
var LLoop,
LCountAllCheckedOfAParentItem,
LCountAllUnCheckedOfAParentItem :Integer;
LItem,
LParentItem: TTreeViewItem;
begin
inherited DoChangeCheck( AItem as TTreeViewItem );
{
procedure TCustomTreeView.DoChangeCheck( const Item: TTreeViewItem );
begin
if Assigned(FOnChangeCheck) then
FOnChangeCheck(Item);
end;
}
LItem := TTreeViewItem(AItem as TTreeViewItem);
for LLoop:=0 to LItem.Count - 1 do
LItem.Items[LLoop].IsChecked := LItem.IsChecked;
if TTreeViewItem(LItem).ParentItem<>nil then
begin
LParentItem :=TTreeViewItem(LItem).ParentItem;
LCountAllCheckedOfAParentItem:=0;
for LLoop:=0 to LParentItem.Count - 1 do
if LParentItem.Items[LLoop].IsChecked=true then
LCountAllCheckedOfAParentItem
:=LCountAllCheckedOfAParentItem+1;
if LCountAllCheckedOfAParentItem=LParentItem.Count then
begin
LParentItem.IsChecked:=true;
end;
LCountAllUnCheckedOfAParentItem:=LParentItem.Count;
for LLoop:=0 to LParentItem.Count - 1 do
if LParentItem.Items[LLoop].IsChecked=false then
LCountAllUnCheckedOfAParentItem
:=LCountAllUnCheckedOfAParentItem-1;
if LCountAllUnCheckedOfAParentItem=0 then
begin
LParentItem.IsChecked:=false;
end;
end;
end;
// 调用代码:
procedure TForm2.FormCreate(Sender: TObject);
var LTreeView :TMyTreeView;
begin
LTreeView :=TMyTreeView.Create(TreeView1); // :TreeView1 :UI中的TreeView组件
TreeView1.OnChangeCheck :=LTreeView.OnChangeCheck;
end;
五、TTreeView枚举与递归效果