UE 树形图 C++版
1.创建以下三个C++类
(1)UTreeViewItemBase(基类UObject)
UCLASS()
class UTreeViewItemBase : public UObject
{
GENERATED_BODY()
public:
UTreeViewItemBase();
virtual ~UTreeViewItemBase() override;
public:
FString Id;
//层级
int32 Level;
//标签
FText Label;
//与自身对应的Actor
AActor* SelfActor;
//SelfActor 的初始材质
TArray<UMaterialInterface*>OriginalMaterials;
//子级Item数组
TArray<UObject*> ChildrenData;
}
该类型为树形图三要素中的Item
①Level:决定Item对应Entry的在树形图中的层级结构,如下图

1-1
②Label:Entry显示的文本
③SelfActor: Actor->Item->Entry. Item是Actor和Entry之间的桥梁,如下图

1-2
④ChildrenData:存储本级Item的子级Item对象
(2)UEntryWidgetBase(基类UUserWidget,实现接口IUserObjectListEntry)
UCLASS()
class UEntryWidgetBase : public UUserWidget,public IUserObjectListEntry
{
GENERATED_BODY()
public:
//override begin
virtual void NativeConstruct() override;
virtual void NativePreConstruct() override;
virtual void NativeOnInitialized() override;
//override end
public:
//子级entry缩进像素长度
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="EntryWidgetBase")
float SubItemIndentWidth=20.f;
UCheckBox* CheckBox;
UTextBlock* TextBlock;
UHorizontalBox* HorBox;
UTreeView* TreeView;
protected:
//Entry对应的Item
UTreeViewItemBase* ListItemObject;
}
该类型为树形图三要素中的Entry
①CheckBox,TextBlock,HorBox控件,如下图

1-3
②ListItemObject:Entry对应的Item
(3)UTreeViewPanelBase(基类UUserWidget)
UCLASS()
class UTreeViewPanelBase : public UUserWidget
{
GENERATED_BODY()
public:
UTreeViewPanelBase(const FObjectInitializer& ObjectInitializer);
//override begin
virtual void NativeConstruct() override;
virtual void NativePreConstruct() override;
virtual bool Initialize() override;
//override end
UTreeView* TreeView;
}
该类型为树形图三要素中的TreeView
2.成员函数说明
(1)UTreeViewItemBase
类中定义以下成员函数
public:
//初始化子级
void InitChildren();
/**
* @brief 设置子级隐藏
* @param bHidden true:隐藏;false:显示
*/
void SetChildrenHiddenInGame(bool bHidden);
/**
* @brief 缓存对应的模型的初始材质
*/
void CacheOriginalMaterials();
/**
* @brief 被选中状态,材质更换
*/
void OnSelected();
/**
* @brief 取消选中状态,材质恢复
*/
void UnSelected();
/**
* @brief 根据选中的Actor找到其对应的Item
* @param SelectedActor 选中的Actor
* @param OutItem 对应的item
*/
void FindSubItemByGivenActor(AActor* SelectedActor,UObject*&OutItem);
解析:
①InitChildren
void UTreeViewItemBase::InitChildren()
{
UnSelected();
//缓存初始材质
CacheOriginalMaterials();
//获得所有子级actor
TArray<AActor*>AttachedActors;
SelfActor->GetAttachedActors(AttachedActors);
ChildrenData.Reset();
//为所有子级actor创建对应的子级item
for(int i=0;i<AttachedActors.Num();i++)
{
if (!AttachedActors[i])
continue;
UTreeViewItemBase* Item=NewObject<UTreeViewItemBase>(this);
Item->Id=FString::Printf(TEXT("%s_%d"),*Id,i);
Item->Level=Level+1;
Item->Label=FText::FromString(UKismetSystemLibrary::GetDisplayName(AttachedActors[i]));
Item->SelfActor=AttachedActors[i];
Item->InitChildren();
ChildrenData.Add(Item);
}
}
该成员函数用于查找SelfActor存在多少个子级Actor,然后为每个子级创建子级Item,并将子级Item与子级Actor建立对应关系.
ps:初始化子级时要注意,子级可能也有子级,所以需要通过递归逐级往下初始化
②SetChildrenHiddenInGame
void UTreeViewItemBase::SetChildrenHiddenInGame(bool bHidden)
{
//设置对应actor的显隐性
SelfActor->SetActorHiddenInGame(bHidden);
//递归设置子级显隐性
for (UObject*& ChildObject:ChildrenData)
{
UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(ChildObject);
if (ChildItem)
{
ChildItem->SetChildrenHiddenInGame(bHidden);
}
}
}
该成员函数用于控制子级Actor(包含自身)的显隐性,如

2-1
③CacheOriginalMaterials
void UTreeViewItemBase::CacheOriginalMaterials()
{
//获得static mesh 组件
UStaticMeshComponent* StaticMeshComp=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if (StaticMeshComp && StaticMeshComp->GetStaticMesh())
{
TArray<UMaterialInterface*>Materials;
Materials=StaticMeshComp->GetMaterials();
OriginalMaterials=Materials;
}
}
该函数用于缓存SelfActor的初始材质
④OnSelected
void UTreeViewItemBase::OnSelected()
{
//获得静态网格组件
UStaticMeshComponent* SMC=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if(!SMC) return;
//下面注释部分是从配置文件中读取高亮材质,本篇教程不对配置文件进行赘述,可自行加载一个材质用于实现选中高亮效果,如LoadObject
/******************************
//获得树形图工具配置文件
UTreeViewToolSettings* TreeViewToolSettings=UTreeViewToolSettings::GetTreeViewToolSettings();
//获得配置文件里配置的高亮材质
TSoftObjectPtr<UMaterialInterface> Material=TreeViewToolSettings->HightLightMaterial.LoadSynchronous();
if (!Material.IsValid()) return;
***********************************************/
//将所有材质设置为高亮
int MaterialNum=SMC->GetNumMaterials();
for (int i=0;i<MaterialNum;i++)
{
UMaterialInstanceDynamic* DMI=SMC->CreateDynamicMaterialInstance(i,Material.Get());
DMI->SetVectorParameterValue(FName("HightLightColor"),TreeViewToolSettings->Color);
}
}
该函数实现了点击Entry,使其对应的Actor高亮(被选中效果) ,效果如参考图2-1
⑤UnSelected
void UTreeViewItemBase::UnSelected()
{
//获得static mesh组件
UStaticMeshComponent* SMC=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
if(!SMC) return;
//重置材质为原始材质
for (int i=0;i<OriginalMaterials.Num();i++)
{
SMC->SetMaterial(i,OriginalMaterials[i]);
}
for(UObject* Child:ChildrenData)
{
if(UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(Child))
{
//递归取消选中状态
ChildItem->UnSelected();
}
}
}
该函数实现了取消选中效果,使其对应的Actor由选中高亮效果恢复到初始材质
⑥FindSubItemByGivenActor
void UTreeViewItemBase::FindSubItemByGivenActor(AActor* SelectedActor, UObject*& OutItem)
{
if(SelfActor==SelectedActor)
{
OutItem=this;
return;
}
for (UObject*&ChildObj:ChildrenData)
{
UTreeViewItemBase*ChildItem=Cast<UTreeViewItemBase>(ChildObj);
ChildItem->FindSubItemByGivenActor(SelectedActor,OutItem);
}
}
该函数用于查找与给定Actor对应的Item,用于点击Actor反向定位树形图中的Entry,如下图

(2)UEntryWidgetBase
NativeOnInitialized(初始化函数)实现
void UEntryWidgetBase::NativeOnInitialized()
{
Super::NativeOnInitialized();
//获得umg 控件
TextBlock=Cast<UTextBlock>(GetWidgetFromName(FName("Textblock")));
CheckBox=Cast<UCheckBox>(GetWidgetFromName(FName("CheckBox")));
HorBox=Cast<UHorizontalBox>(GetWidgetFromName(FName("HorBox")));
//绑定checkbox勾选状态改变回调
if(CheckBox)
{
CheckBox->OnCheckStateChanged.AddDynamic(this,&UEntryWidgetBase::OnCheckBoxStateChanged);
}
//默认勾选状态
CheckBox->SetCheckedState(ECheckBoxState::Checked);
}
类中定义以下成员函数
public:
/**
* @brief 推测Entry的勾选状态
* @param Item entry对应的Item
* @return 勾选状态
*/
ECheckBoxState PredictEntryState(UTreeViewItemBase* Item);
/**
* @brief 递归刷新entry的上一级entry状态
*/
void RefreshParentEntries();
/**
* @brief 递归刷新子级entry状态
* @param State 子级状态受父级entry checkbox控件选中状态影响
*/
void RefreshChildrenEntries(ECheckBoxState State);
/**
* @brief 刷新各级Entry状态
* @param State checkbox 控件选中状态
*/
void RefreshHierarchyState(ECheckBoxState State);
/**
* @brief checkbox控件选中状态更改回调函数
* @param bIsChecked 是否勾选
*/
UFUNCTION()
void OnCheckBoxStateChanged(bool bIsChecked);
/**
* @brief 根据entry选中状态切换字体
* @param bIsSelected 选中:粗体;未选中:正常字体
*/
void SwitchFontBySelectState(bool bIsSelected);
/**
* @brief 根据entry选中状态更新对应模型的高亮效果
* @param bIsSelected 选中高亮,未选中恢复正常
*/
void SwitchMaterialBySelectState(bool bIsSelected);
protected:
//IUserObjectListEntry begin
void NativeOnListItemObjectSet(UObject* ListItemObject) override;
void NativeOnItemSelectionChanged(bool bIsSelected) override;
void NativeOnEntryReleased() override;
//IUserObjectListEntry end
解析:
①PredictEntryState(推测Entry状态)
ECheckBoxState UEntryWidgetBase::PredictEntryState(UTreeViewItemBase* Item)
{
ECheckBoxState State=ECheckBoxState::Checked;
//注1:
int Count=0;
if (!Item) return State;
//统计子级Entry勾选checkbox的数量
if (Item->ChildrenData.Num()>0)
{
for (UObject*& ChildData:Item->ChildrenData)
{
UEntryWidgetBase* ChildEntry=Cast<UEntryWidgetBase>(TreeView->GetEntryWidgetFromItem(ChildData));:
if (ChildEntry)
{
ECheckBoxState ChildState=ChildEntry->CheckBox->GetCheckedState();
switch (ChildState)
{
case ECheckBoxState::Checked:
Count++;
break;
case ECheckBoxState::Undetermined:
Count+=10000;
break;
default:
break;
}
}
//entry无效(不显示时)以对应item的actor显隐性为准
else
{
UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(ChildData);
if (ChildItem)
Count+=ChildItem->SelfActor->IsHidden()?0:1;
}
}
//注2
State=Item->ChildrenData.Num()==Count?ECheckBoxState::Checked:ECheckBoxState::Undetermined;
}
//没有子级时,以对应item的actor的显隐性为准,显示=checked;隐藏=unchecked
else
{
State=Item->SelfActor->IsHidden()?ECheckBoxState::Unchecked:ECheckBoxState::Checked;
}
return State;
}
注1:父级的勾选状态(checkbox)受子级影响.通过对比子级已勾选数量对比子级数量来决定父级的勾选状态.
注2:
子级Entry全部Checked(子级数量==Count):父级Checked
子级Entry部分Checked 或者 全部UnChecked(子级数量!=Count):父级Undetermined
参考如下图2-2

2-2
②RefreshParentEntries(刷新父级Entry状态)
void UEntryWidgetBase::RefreshParentEntries()
{
if(!TreeView) return;
//获得父级item和entry
UTreeViewItemBase* ParentItem=Cast<UTreeViewItemBase>(ListItemObject->GetOuter());
UEntryWidgetBase* ParentEntry=Cast<UEntryWidgetBase>(TreeView->GetEntryWidgetFromItem(ParentItem));
if (ParentEntry)
{
//推测父级checkbox状态
ECheckBoxState ParentState=PredictEntryState(ParentItem);
ParentEntry->CheckBox->SetCheckedState(ParentState);
//递归刷新更上一级父级
ParentEntry->RefreshParentEntries();
}
}
向上递归刷新父级Entry的状态,效果参考2-2
③RefreshChildrenEntries(刷新子级Entry状态)
void UEntryWidgetBase::RefreshChildrenEntries(ECheckBoxState State)
{
if(!TreeView) return;
//刷新子级checkbox和启用状态
for(UObject*& ChildData:ListItemObject->ChildrenData)
{
UEntryWidgetBase* Entry=TreeView->GetEntryWidgetFromItem<UEntryWidgetBase>(ChildData);
//entry有效时设置勾选状态
if (Entry)
{
Entry->CheckBox->SetCheckedState(State);
Entry->RefreshChildrenEntries(State);
}
}
}
遍历+递归更新本级Entry的子级Entry(以及子级的子级)的状态.效果参考2-2
④RefreshHierarchyState(刷新层级结构状态,即子父级一块刷新)
void UEntryWidgetBase::RefreshHierarchyState(ECheckBoxState State)
{
if(State==ECheckBoxState::Undetermined) return;
//根据勾选状态更新对应的Actor的显隐性
ListItemObject->SetChildrenHiddenInGame(CheckBox->GetCheckedState()==ECheckBoxState::Unchecked);
RefreshChildrenEntries(State);
RefreshParentEntries();
}
效果参考2-2
④OnCheckBoxStateChanged(Checkbox勾选状态改变回调)
void UEntryWidgetBase::OnCheckBoxStateChanged(bool bIsChecked)
{
RefreshHierarchyState(bIsChecked?ECheckBoxState::Checked:ECheckBoxState::Unchecked);
}
该回调函数在本篇开头NativeOnInitialized中绑定.

2-3
⑤SwitchFontBySelectState(选中切换字体)
void UEntryWidgetBase::SwitchFontBySelectState(bool bIsSelected)
{
//此处字体选择逻辑仅为示例,实际使用建议将字体公开到蓝图,可在蓝图中进行手动选择
UFont* FontObject=nullptr;
if(bIsSelected)
{
FontObject=LoadObject<UFont>(nullptr,TEXT("/Engine/EngineFonts/BoldFont"));
}
else
{
FontObject=LoadObject<UFont>(nullptr,TEXT("/Engine/EngineFonts/Roboto"));
}
//设置字体
if(FontObject)
{
FSlateFontInfo FontInfo=TextBlock->GetFont();
FontInfo.FontObject=FontObject;
TextBlock->SetFont(FontInfo);
}
}
效果参考2-3
⑥SwitchMaterialBySelectState(选中高亮)
void UEntryWidgetBase::SwitchMaterialBySelectState(bool bIsSelected)
{
//根据选中状态更换材质
if (bIsSelected)
{
ListItemObject->OnSelected();
}
else
{
ListItemObject->UnSelected();
}
}
效果参考2-3
⑦NativeOnListItemObjectSet
void UEntryWidgetBase::NativeOnListItemObjectSet(UObject* ItemObject)
{
//获取TreeView
TreeView=Cast<UTreeView>(GetOwningListView());
//获得对应的Item
ListItemObject=Cast<UTreeViewItemBase>(ItemObject);
//设置标签字体,文本内容和字体大小,字体大小与level挂钩,级别越高字体越大
TextBlock->SetText(ListItemObject->Label);
FSlateFontInfo FontInfo=TextBlock->GetFont();
FontInfo.Size=UKismetMathLibrary::MapRangeClamped(ListItemObject->Level,1,6,15,8);
TextBlock->SetFont(FontInfo);
//根据Entry的level调整文本缩进长度
UBorderSlot* BorderSlot=Cast<UBorderSlot>(HorBox->Slot);
if (BorderSlot)
{
BorderSlot->SetPadding(FMargin(ListItemObject->Level*SubItemIndentWidth,0,0,0));
}
CheckBox->SetCheckedState(PredictEntryState(ListItemObject));
RefreshParentEntries();
}
该接口函数触发时会返回Entry对应的Item,根据Item的成员变量Level(层级结构的级别,级树形图中的第n级)来决定Entry的字体大小以及文本缩进长度,如下图2-4

2-4
⑧NativeOnItemSelectionChanged
void UEntryWidgetBase::NativeOnItemSelectionChanged(bool bIsSelected)
{
//根据选中状态更换字体和材质
SwitchFontBySelectState(bIsSelected);
if(bIsSelected)
{
ListItemObject->OnSelected();
}
else
{
ListItemObject->UnSelected();
}
}
该接口函数在Entry的选中状态改变时触发,效果参考2-3
⑨NativeOnEntryReleased
void UEntryWidgetBase::NativeOnEntryReleased()
{
SwitchFontBySelectState(false);
SwitchMaterialBySelectState(false);
}
该接口在Entry被释放的时候触发,将字体和Actor材质还原.
Entry在以下情况会被释放:
1.子级Entry被折叠收起时会触发Release,此外,被展开时会触发NativeOnListItemObjectSet
2.Entry不被绘制时,即在视口中不可见,如随着scrollbox滑动导致看不见,同理,当Entry重新被绘制时,触发NativeOnListItemObjectSet
两种情况效果如下图2-5

2-5
(3)UTreeViewPanelBase
NativeConstruct函数实现
void UTreeViewPanelBase::NativeConstruct()
{
Super::NativeConstruct();
TreeView=Cast<UTreeView>(GetWidgetFromName(FName("TreeView")));
//绑定回调函数
TreeView->SetOnGetItemChildren<UTreeViewPanelBase>(this,&UTreeViewPanelBase::OnGetItemChildren);
}
类中定义以下成员函数
/**
* @brief 查找actor的父级
* @param Actor 要被查找父级的actor
* @param bFindTopLevelParent 是否查找最上级父级
*/
void FindParentActor(AActor*& GivenActor,bool bFindTopLevelParent=true);
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void StartSelect();
/**
* @brief 选中一个actor用于显示其层级结构
* @param Level 层级级别,
* @param Actor 选中的Actor
* @param bUseTopLevelParent 是否使用选中actor的最上级父类作为层级结构起点
*/
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void OnSelectActor(const int32 Level,AActor* Actor,bool bUseTopLevelParent=true);
/**
* @brief 展开所有Item
*/
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void ExpandAllItems();
/**
* @brief
* @param Item 展开单个Item
*/
void ExpandItem(UObject* Item);
/**
* @brief 收起所有Item
*/
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void CollapseAllItem();
/**
* @brief 刷新树形图
*/
void RefreshTreeView();
/**
* @brief 获得item children回调函数
* @param Item
* @param Children
*/
UFUNCTION()
void OnGetItemChildren(UObject* Item,TArray<UObject*>& Children);
解析:
①FindParentActor

3-1
void UTreeViewPanelBase::FindParentActor(AActor*& GivenActor, bool bFindTopLevelParent)
{
if(!Acotr) return;
//获得父级actor
AActor* ParentActor=Acotr->GetAttachParentActor();
if (ParentActor)
{
Acotr=ParentActor;
//递归继续查找更上层父级
if (bFindTopLevelParent)
{
FindParentActor(Acotr,bFindTopLevelParent);
}
}
}
GivenActor:选中的Actor(例如被鼠标点击到的)
bFindTopLevelParent:
true表示查找层级结构中的最上级,如传入参考图3-1中的Cone,返回Cube
false表示查找结构中的上级,如传入Cone返回Sphere
②StartSelect(选取场景中Actor)
void UTreeViewPanelBase::StartSelect()
{
APlayerController* PlayerController=GetWorld()->GetFirstPlayerController();
if(PlayerController->IsInputKeyDown(EKeys::RightMouseButton))
return;
FVector Location,Direction;
if(PlayerController->DeprojectMousePositionToWorld(Location,Direction))
{
FHitResult HitResult;
bool bHit=UKismetSystemLibrary::LineTraceSingle(
this,
Location,
Direction*100000.f+Location,
ETraceTypeQuery::TraceTypeQuery1,
true,TArray<AActor*>(),
EDrawDebugTrace::ForOneFrame,
HitResult,
true);
if (bHit)
{
OnSelectActor(1,HitResult.GetActor(),true);
}
}
}
③OnSelectActor(选中Actor)
void UTreeViewPanelBase::OnSelectActor(const int32 Level, AActor* Actor, bool bUseTopLevelParent)
{
//查找模型最上级的层级
AActor*ParentActor=Actor;
FindParentActor(ParentActor,bUseTopLevelParent);
if (!ParentActor) return;
bool bNeedRebuildTreeView=true;
if(TreeView->GetListItems().IsValidIndex(0))
{
UTreeViewItemBase* RootItem=Cast<UTreeViewItemBase>(TreeView->GetListItems()[0]);
//注1
if(RootItem->SelfActor==ParentActor)
{
bNeedRebuildTreeView=false;
UObject* OutItem=nullptr;
RootItem->FindSubItemByGivenActor(Actor,OutItem);
if (OutItem)
{
TArray<UUserWidget*> EntryWidgets=TreeView->GetDisplayedEntryWidgets();
for (UUserWidget*& EntryWidget:EntryWidgets)
{
UEntryWidgetBase* Entry=Cast<UEntryWidgetBase>(EntryWidget);
UObject* ItemObj=Entry->GetListItem<UObject>();
//注2
TreeView->SetItemSelection(ItemObj,false);
}
//注3
TreeView->RequestNavigateToItem(OutItem);
TreeView->SetItemSelection(OutItem,true);
}
}
}
//重新构建树形图
if (bNeedRebuildTreeView)
{
UE_LOG(LogTemp,Warning,TEXT("Select a new object :%s"),*ParentActor->GetName());
UTreeViewItemBase* Item=NewObject<UTreeViewItemBase>(this);
Item->Id="0";
Item->Level=Level;
Item->Label=FText::FromString(UKismetSystemLibrary::GetDisplayName(ParentActor));
Item->SelfActor=ParentActor;
Item->InitChildren();
TreeView->ClearListItems();
TreeView->AddItem(Item);
}
//展开所有item
ExpandAllItems();
}
注1:判断选中的Actor的父级Actor是否是上次选中的(即是否切换目标),如下图3-2
3-2
注2:在未切换目标的情况下,将未被选中的子Entry设为未选择状态(未高亮)
注3:将选中的Entry设为已选中(高亮)
效果如下图3-3
3-3
④ExpandItem(展开单个) ExpandAllItems(展开所有Item)
引擎提供的ExpandAll并不能真正的展开所有的层级结构

3-4
调用引擎提供的ExpandAll仅能展开了一层结构,效果如3-5
3-5
为此我们需要自行实现ExpandAll功能,代码如下
void UTreeViewPanelBase::ExpandItem(UObject* Item)
{
TreeView->SetItemExpansion(Item,true);
UTreeViewItemBase* TreeViewItem=Cast<UTreeViewItemBase>(Item);
for (UObject*& ChildItem:TreeViewItem->ChildrenData)
{
if(ChildItem)
{
//递归展开当前Item的Children
ExpandItem(ChildItem);
}
}
}
void UTreeViewPanelBase::ExpandAllItems()
{
//官方提供的expandall节点智能展开一级entry,再深层子级无法展开
TreeView->ExpandAll();
//手动展开item
TArray<UObject*> Items=TreeView->GetListItems();
if (Items.IsValidIndex(0))
{
ExpandItem(Items[0]);
}
}
⑤CollapseAllItem(折叠收起所有Item)
void UTreeViewPanelBase::CollapseAllItem()
{
TreeView->CollapseAll();
}
⑥RefreshTreeView(刷新层级结构)
void UTreeViewPanelBase::RefreshTreeView()
{
if(TreeView->GetListItems().IsValidIndex(0))
{
UTreeViewItemBase* RootItem=Cast<UTreeViewItemBase>(TreeView->GetListItems()[0]);
RootItem->InitChildren();
TreeView->ClearListItems();
TreeView->AddItem(RootItem);
ExpandAllItems();
}
}
注1:Treeview通过GetListItems获得的Item数组中,第0个元素表示根Item
注2:刷新时清空原有的Item
注3:默认刷新时展开所有层级,可根据自身需求选择,不想展开就是执行ExpandAllItems
⑦OnGetItemChildren
void UTreeViewPanelBase::OnGetItemChildren(UObject* Item,TArray<UObject*>& Children)
{
UTreeViewItemBase* TreeViewItem=Cast<UTreeViewItemBase>(Item);
if(!TreeViewItem) return;
Children=TreeViewItem->ChildrenData;
}
该函数在Treeview执行AddItem时自动触发.
一.创建UMG
1.WBP_EntryWidget

1-1
2.WBP_TreeViewPanel

1-2

代码如下

1-3
解析:
(1)使用右键鼠标进行选择目标
(2)EventOnMouseEnter和EventIOnMouseLeave 事件为了防止鼠标穿透UI选中目标
二.演示

至此树形图功能讲解完毕!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)