ue5 创建多列StreeView的方法与理解
创建StreeView的多列样式怎么就像是创建单行单列差不多?貌似就是在单行单列中加入了多列widget?
示例代码
DetailTabWidget
#pragma once
#include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget
{SLATE_BEGIN_ARGS(SDetailTabWidget){}SLATE_END_ARGS()void Construct(const FArguments& InArgs);private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;TSharedPtr<STreeView<TSharedRef<FTreeViewItemBase>>> TreeViewItem;TArray<TSharedRef<FTreeViewItemBase>> TreeViewSource;public:TSharedRef<class ITableRow> GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase, const TSharedRef< class STableViewBase >& TableViewBase);void OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase, TArray<TSharedRef<FTreeViewItemBase>>& OutChildren);void SetItemData();
};
#include "DetailTabWidget.h"#include "TreeViewItemBaseBool.h"
#include "Widgets/Layout/SWidgetSwitcher.h"void SDetailTabWidget::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("This DetailTab Panel"))]+SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SAssignNew(TreeViewItem,STreeView<TSharedRef<FTreeViewItemBase>>).TreeItemsSource(&TreeViewSource)//委托,生成每一行,绑定自定义方法(事件),触发时生成.OnGenerateRow(this, &SDetailTabWidget::GenerateTreeViewRow).OnGetChildren(this,&SDetailTabWidget::OnGetChildren).HeaderRow(SNew(SHeaderRow)+SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10,5,5,5))[SNew(STextBlock).Text(FText::FromString(TEXT("名 称")))]]+SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10,5,5,5))[SNew(STextBlock).Text(FText::FromString(TEXT("属 性")))]])]];SetItemData();
}TSharedRef<class ITableRow> SDetailTabWidget::GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase,const TSharedRef<class STableViewBase>& TableViewBase)
{return SNew(SMultiTableRow, TableViewBase,ItemBase);
}void SDetailTabWidget::OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase,TArray<TSharedRef<FTreeViewItemBase>>& OutChildren)
{ItemBase->Getchildren(OutChildren);
}void SDetailTabWidget::SetItemData()
{TSharedRef<FTreeViewItemBaseBool> BoolItem = MakeShareable(new FTreeViewItemBaseBool());TreeViewSource.Add(BoolItem);TreeViewItem->RequestTreeRefresh();WidgetSwitcher->SetActiveWidgetIndex(1);}
TreeViewItemBase
#pragma once// Item
class FTreeViewItemBase : public TSharedFromThis<FTreeViewItemBase>
{
public:virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;void Getchildren(TArray<TSharedRef<FTreeViewItemBase>>& OutChildren) const{OutChildren = Children;}
private:TArray<TSharedRef<FTreeViewItemBase>> Children;
};// Row单行多列
class SMultiTableRow : public SMultiColumnTableRow<TSharedRef<FTreeViewItemBase>>
{SLATE_BEGIN_ARGS(SMultiTableRow){}SLATE_END_ARGS()virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase);
private:TWeakPtr<FTreeViewItemBase> TreeViewItemBasePtr;
};
#include "TreeViewItemBase.h"TSharedRef<SWidget> SMultiTableRow::GenerateWidgetForColumn(const FName& ColumnName)
{if (ColumnName.IsEqual((TEXT("Name")))){return TreeViewItemBasePtr.Pin()->MakeNameWidget();}if (ColumnName.IsEqual(TEXT("Value"))){return TreeViewItemBasePtr.Pin()->MakeValueWidget(); }return SNullWidget::NullWidget;
}void SMultiTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView,const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase)
{TreeViewItemBasePtr = InTreeViewItemBase;STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,0,0,0));SMultiColumnTableRow::Construct(ParentArgs, InOwnerTableView);
}
TreeViewItemBaseBool
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseBool : public FTreeViewItemBase
{
public:virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool Value;};
#include "TreeViewItemBaseBool.h"TSharedRef<SWidget> FTreeViewItemBaseBool::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FTreeViewItemBaseBool::MakeValueWidget()
{return SNew(SCheckBox).OnCheckStateChanged(this, &FTreeViewItemBaseBool::OnCheckStateChanged).IsChecked(this, &FTreeViewItemBaseBool::GetCheckBoxState);}void FTreeViewItemBaseBool::OnCheckStateChanged(ECheckBoxState CheckState)
{Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FTreeViewItemBaseBool::GetCheckBoxState() const
{return Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
目录结构:
必备条件
StreeView的多列创建需要的必备条件:
数据基类
CustomItemBase
#pragma once
/*
----------------------------------
| Name | Value |
----------------------------------
| 香蕉 | 真正的香蕉 |
----------------------------------
*/// 构建一个基类,相当于创建一个空的价签,其中包括(名字:价格:),具体怎么填由子类决定
class FCustomItemBase : public TSharedFromThis<FCustomItemBase>
{
public:virtual ~FCustomItemBase() {}// 比如要卖香蕉:名字:香蕉 ,Value :价格virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;//当展示时需要先获取到这个价签后才能知道这是为哪个商品准备的价签(即:名字,价格)void GetChildrens(TArray<TSharedRef<FCustomItemBase>>& OutChildren) const{ OutChildren = Childrens;};private://用于保存传入的参数并通过OutChildren传出去TArray<TSharedRef<FCustomItemBase>> Childrens;
};/*
----------------------------------
| Name | Value |
----------------------------------
FCustomItemBase中有两个属性(name、Value),所以制作价签时就需要预留两个空位,使用SMultiColumnTableRow多列样式
创建继承至class SMultiColumnTableRow的类,查看基类样式为SMultiColumnTableRow : public STableRow<ItemType>
解释为: S类名 :public 基类<价签的引用>,有几个属性就安排几个位置(即:有两个属性,就安排两列),这是S类,所以需要S类的构造方法
如果是单列:STableRow,每行就只能放一个属性,明白了吗?
----------
| Name |
----------
*/class SMultiDetailTableRow : public SMultiColumnTableRow<TSharedRef<FCustomItemBase>>
{SLATE_BEGIN_ARGS(SMultiDetailTableRow){}SLATE_END_ARGS()// 父类SMultiColumnTableRow中的方法,动态生成不同列的显示内容,必须实现的方法virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& InColumnName ) override;// 父类方法中默认两个参数,第三个参数是因为创建时必须有FCustomItemBase才能正确显示void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView,TSharedRef<FCustomItemBase> InCustomItemBase);
private:// 这个弱指针是为了初始化时将传进来的FCustomItemBase赋值给CustomItemBase用于保存TWeakPtr<FCustomItemBase> CustomItemBase;
};
#include "CustomItemBase.h"//一行多列
TSharedRef<SWidget> SMultiDetailTableRow::GenerateWidgetForColumn(const FName& InColumnName)
{// 判断InColumnName,如果传入的是名字,就在价签的name中添加名字if (InColumnName.IsEqual(TEXT("Name"))){return CustomItemBase.Pin()->MakeNameWidget();}// 判断InColumnName,如果传入的是Value,就在价签的Value中添加价格if ((InColumnName.IsEqual(TEXT("Value")))){return CustomItemBase.Pin()->MakeValueWidget();}// 空widget用于占位return SNullWidget::NullWidget;
}//还是创建了一行两列,streeview中的多个行还是需要在生成树的类中创建
void SMultiDetailTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView,TSharedRef<FCustomItemBase> InCustomItemBase)
{//赋值保存CustomItemBase = InCustomItemBase;//STableRow::FArguments()是这个是原生的风格样式,生成时的样式布局(可替换)STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,8,0,0));//调用基类的构造函数,查看基类中的Construct方法SMultiColumnTableRow::Construct(ParentArgs, OwnerTableView);}
数据子类
CustomItemBool
#pragma once
#include "CustomItemBase.h"//价签的子类,父类中的价签已经印刷好了
class FCustomItemBool : public FCustomItemBase
{
public://创建后默认就是 falseFCustomItemBool():Value(false){}// 实现父类 CustomItemBase中的方法,做具体的事(即:具体的名字,具体的价格)virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool Value;
};
#include "CustomItemBool.h"TSharedRef<SWidget> FCustomItemBool::MakeNameWidget()
{//具体的名字Test Bool,显示中文必须用Text包裹:(TEXT("选中测试Bool")return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FCustomItemBool::MakeValueWidget()
{//这里创建的是选择框,可以是选择框、下拉框...Bool类的控件return SNew(SCheckBox)//绑定点击状态,是否点击了,这是一个事件SLATE_EVENT( FOnCheckStateChanged, OnCheckStateChanged )//相当于点击了就触发点击事件,将这个事件与回调函数OnCheckStateChanged绑定,点击就触发.OnCheckStateChanged(this, &FCustomItemBool::OnCheckStateChanged)//点击后的状态,是选中了还是取消了,查看SLATE_ATTRIBUTE( ECheckBoxState, IsChecked )后不是知道是啥//继续查看enum class ECheckBoxState : uint8,里面有三种状态,那这个IsChecked()的参数是不是就是要这个枚举?//因为这里是绑定,需要回调函数,所以创建一个ECheckBoxState类型的函数试试,结果懵对了.IsChecked(this, &FCustomItemBool::GetCheckBoxState);
}void FCustomItemBool::OnCheckStateChanged(ECheckBoxState CheckState)
{//既然是回调函数,那么当被触发后要做什么?这个类是bool类型,非真即假//使用三目运算符 :给Value 赋值为 CheckState//CheckState的状态如果与ECheckBoxState::Checked 的状态相同,就是真,否则是假Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FCustomItemBool::GetCheckBoxState() const
{//上面已经给Value赋值了,这里就问Value是什么?如果是真就是 ECheckBoxState::Checked了,否则。。。return Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
CustomItemInt
#pragma once
#include "CustomItemBase.h"class FCustomItemInt : public FCustomItemBase
{
public:FCustomItemInt(): Value(0){}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;protected:void OnValueChanged(int32 InValue);int32 GetValue() const;
private:int32 Value;
};
#include "CustomItemInt.h"#include "Widgets/Input/SSpinBox.h"TSharedRef<SWidget> FCustomItemInt::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("测试Int32")));
}TSharedRef<SWidget> FCustomItemInt::MakeValueWidget()
{return SNew(SBox).WidthOverride(200).MaxDesiredWidth(400).HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SNew(SSpinBox<int32>).OnValueChanged(this, &FCustomItemInt::OnValueChanged).Value(this, &FCustomItemInt::GetValue)];
}void FCustomItemInt::OnValueChanged(int32 InValue)
{Value = InValue;
}int32 FCustomItemInt::GetValue() const
{return Value;
}
streetview组装类
CustomDetailPlane
#pragma once
#include "CustomItemBase.h"//这里是使用typedef创建别名的方式,用FCustomItemBase中的数据,创建了一个树,
typedef STreeView<TSharedRef<FCustomItemBase>> CustomDetailTreeView;class SCustomDetailPlane : public SCompoundWidget
{SLATE_BEGIN_ARGS(SCustomDetailPlane){}SLATE_END_ARGS()SCustomDetailPlane();virtual ~SCustomDetailPlane();void Construct(const FArguments& InArgs);//树形结构中的行,这里就是一行一列,这里可以再装入1行2列或是其他。。。TSharedRef<ITableRow> CustomOnGenerateRow(TSharedRef<FCustomItemBase> Item,const TSharedRef< STableViewBase >& TableView);//SLATE_EVENT( FOnGetChildren, OnGetChildren ),//事件类型,进一步查看API DECLARE_DELEGATE_TwoParams (FOnGetChildren,ArgumentType, TArray<ArgumentType>& );//TwoParams表示需要两个参数,没有返回值,因为是GetChildren,ArgumentType应该是Children父类,然后从父类获取包含的子类void OnGetChildren(TSharedRef<FCustomItemBase> ParentItem, TArray<TSharedRef<FCustomItemBase>>& OutChildrens);//当CustomItemBool类创建好后,这时已经在类中确定了要做的事,这里创建一个函数用于给TreeItemSources赋值测试void SetItemData();private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;//这里用的是上面创建的别名,生成指向STreeView<TSharedRef<FCustomItemBase>>的指针//因为STreeView<TSharedRef<FCustomItemBase>>太长,所以使用别名,也方便修改TSharedPtr<CustomDetailTreeView> OtherNameDetailTreeView;//树结构需要的数据源,(即已经准备好的价签,展台已经准备好了,需要几层就放入几个价签)TArray<TSharedRef<FCustomItemBase>> TreeItemSources;
};
#include "CustomDetailPlane.h"#include "CustomItemBool.h"
#include "Widgets/Layout/SWidgetSwitcher.h"SCustomDetailPlane::SCustomDetailPlane()
{
}SCustomDetailPlane::~SCustomDetailPlane()
{
}void SCustomDetailPlane::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+ SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("Custom Detail Plane"))]+ SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[//生成树控件,只有树干SAssignNew(OtherNameDetailTreeView, CustomDetailTreeView)//设置数据源(即:准备好树枝).TreeItemsSource(&TreeItemSources)//绑定代理(将生成事件与实际生成方法绑定到一起).OnGenerateRow(this, &SCustomDetailPlane::CustomOnGenerateRow)//绑定代理(将获取事件与实际获取方法绑定到一起).OnGetChildren(this, &SCustomDetailPlane::OnGetChildren)//生成列.HeaderRow(SNew(SHeaderRow)+ SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(20, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString("Name"))]]+ SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0)).FillWidth(0.6f)[SNew(SBorder).Padding(FMargin(20, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString("Value"))]])]];SetItemData();
}TSharedRef<ITableRow> SCustomDetailPlane::CustomOnGenerateRow(TSharedRef<FCustomItemBase> Item,const TSharedRef<STableViewBase>& TableView)
{return SNew(SMultiDetailTableRow, TableView, Item);
}void SCustomDetailPlane::OnGetChildren(TSharedRef<FCustomItemBase> ParentItem,TArray<TSharedRef<FCustomItemBase>>& OutChildrens)
{ParentItem->GetChildrens(OutChildrens);
}void SCustomDetailPlane::SetItemData()
{//相当于实例化了一个FCustomItemBool类型的小控件BoolItem,等待显示TSharedRef<FCustomItemBool> BoolItem = MakeShareable(new FCustomItemBool());//在FCustomItemBool中已经将水果放到了果篮中并将果篮摆在了层板上,这里将层板放到树上TreeItemSources.Add(BoolItem);//这里如果你又创建了新的控件,就还用上面的MakeShareable(new FCustomItemBool());方法//然后用TreeItemSources.Add(BoolItem);添加//刷新OtherNameDetailTreeView->RequestTreeRefresh();//显示WidgetSwitcher的那一页WidgetSwitcher->SetActiveWidgetIndex(1);
}
使用FProperty反射
使用FProperty反射,用widget控件实时控制类中变量
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ActorBase.generated.h"UCLASS()
class CUSTOMWINDOW_API AActorBase : public AActor
{GENERATED_BODY()public:// Sets default values for this actor's propertiesAActorBase();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:// Called every framevirtual void Tick(float DeltaTime) override;protected:UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")bool bIsSelect;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FVector _Location;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FVector _Scale;UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")FRotator _Rotation;
};
// Fill out your copyright notice in the Description page of Project Settings.#include "ActorBase.h"// Sets default values
AActorBase::AActorBase()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void AActorBase::BeginPlay()
{Super::BeginPlay();}// Called every frame
void AActorBase::Tick(float DeltaTime)
{Super::Tick(DeltaTime);
}
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "ActorBase.h"
#include "TestActor.generated.h"UCLASS()
class CUSTOMWINDOW_API ATestActor : public AActorBase
{GENERATED_BODY()public:// Sets default values for this actor's propertiesATestActor();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:// Called every framevirtual void Tick(float DeltaTime) override;UFUNCTION(BlueprintCallable, Category="TestActor")void SetSelect(bool bInValue);UFUNCTION(BlueprintCallable, Category="TestActor")void SetTestPropertChange(const FString& InValue);UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, meta=(AllowedClasses), Category="ProActorBase")int32 TestInt32 = 16;
};
// Fill out your copyright notice in the Description page of Project Settings.#include "TestActor.h"// Sets default values
ATestActor::ATestActor()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void ATestActor::BeginPlay()
{Super::BeginPlay();}// Called every frame
void ATestActor::Tick(float DeltaTime)
{Super::Tick(DeltaTime);GEngine->AddOnScreenDebugMessage(1,2.f,FColor::Orange,FString::Printf(TEXT("bIsSelect = %d TestInt32 = %d"),bIsSelect,TestInt32));
}void ATestActor::SetSelect(bool bInValue)
{
}void ATestActor::SetTestPropertChange(const FString& InValue)
{
}
************
#pragma once
#include "ActorBase.h"// Item
class FTreeViewItemBase : public TSharedFromThis<FTreeViewItemBase>
{
protected:using Super = FTreeViewItemBase;
public:FTreeViewItemBase(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName);virtual TSharedRef<SWidget> MakeNameWidget() = 0;virtual TSharedRef<SWidget> MakeValueWidget() = 0;virtual ~FTreeViewItemBase(){}void Getchildren(TArray<TSharedRef<FTreeViewItemBase>>& OutChildren) const{OutChildren = Children;}
protected:TArray<TSharedRef<FTreeViewItemBase>> Children;TWeakObjectPtr<AActorBase> ActorBasePtr;FProperty* PropertyPtr = nullptr;
};// Row单行多列
class SMultiTableRow : public SMultiColumnTableRow<TSharedRef<FTreeViewItemBase>>
{SLATE_BEGIN_ARGS(SMultiTableRow){}SLATE_END_ARGS()virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase);
private:TWeakPtr<FTreeViewItemBase> TreeViewItemBasePtr;
};
#include "TreeViewItemBase.h"FTreeViewItemBase::FTreeViewItemBase(TWeakObjectPtr<AActorBase> InActorPtr, const FString& PropertyName): ActorBasePtr(InActorPtr)
{if (ActorBasePtr.IsValid()){PropertyPtr = FindFProperty<FProperty>(ActorBasePtr->GetClass(),*PropertyName);}
}TSharedRef<SWidget> SMultiTableRow::GenerateWidgetForColumn(const FName& ColumnName)
{if (ColumnName.IsEqual((TEXT("Name")))){return TreeViewItemBasePtr.Pin()->MakeNameWidget();}if (ColumnName.IsEqual(TEXT("Value"))){return TreeViewItemBasePtr.Pin()->MakeValueWidget(); }return SNullWidget::NullWidget;
}void SMultiTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView,const TSharedRef<FTreeViewItemBase>& InTreeViewItemBase)
{TreeViewItemBasePtr = InTreeViewItemBase;STableRow::FArguments ParentArgs;ParentArgs.Padding(FMargin(0,2,0,2));SMultiColumnTableRow::Construct(ParentArgs, InOwnerTableView);
}
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseBool : public FTreeViewItemBase
{
public:FTreeViewItemBaseBool(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName): Super(InActorPtr,PropertyName){if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(PropertyPtr)){Value = BoolProperty->ContainerPtrToValuePtr<bool>(InActorPtr.Get());}}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;private:void OnCheckStateChanged(ECheckBoxState CheckState);ECheckBoxState GetCheckBoxState() const;bool* Value = nullptr;};
#include "TreeViewItemBaseBool.h"TSharedRef<SWidget> FTreeViewItemBaseBool::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("选中测试Bool")));
}TSharedRef<SWidget> FTreeViewItemBaseBool::MakeValueWidget()
{return SNew(SCheckBox).OnCheckStateChanged(this, &FTreeViewItemBaseBool::OnCheckStateChanged).IsChecked(this, &FTreeViewItemBaseBool::GetCheckBoxState);}void FTreeViewItemBaseBool::OnCheckStateChanged(ECheckBoxState CheckState)
{*Value = CheckState == ECheckBoxState::Checked ? true : false;
}ECheckBoxState FTreeViewItemBaseBool::GetCheckBoxState() const
{return *Value? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
#pragma once
#include "TreeViewItemBase.h"class FTreeViewItemBaseNumber : public FTreeViewItemBase
{
public:FTreeViewItemBaseNumber(TWeakObjectPtr<AActorBase> InActorPtr,const FString& PropertyName): Super(InActorPtr,PropertyName){if (PropertyPtr){Value = PropertyPtr->ContainerPtrToValuePtr<int32>(InActorPtr.Get());}}virtual TSharedRef<SWidget> MakeNameWidget() override;virtual TSharedRef<SWidget> MakeValueWidget() override;protected:void OnValueChanged(int32 InValue);int32 GetValue() const;
private:int32* Value = nullptr;
};
#include "TreeViewItemBaseNumber.h"#include "Widgets/Input/SSpinBox.h"TSharedRef<SWidget> FTreeViewItemBaseNumber::MakeNameWidget()
{return SNew(STextBlock).Text(FText::FromString(TEXT("测试Int32")));
}TSharedRef<SWidget> FTreeViewItemBaseNumber::MakeValueWidget()
{return SNew(SBox).WidthOverride(200).MaxDesiredWidth(400).HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SNew(SSpinBox<int32>).OnValueChanged(this, &FTreeViewItemBaseNumber::OnValueChanged).Value(this, &FTreeViewItemBaseNumber::GetValue)];
}void FTreeViewItemBaseNumber::OnValueChanged(int32 InValue)
{*Value = InValue;
}int32 FTreeViewItemBaseNumber::GetValue() const
{return *Value;
}
*****************************
#pragma once
#include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget
{SLATE_BEGIN_ARGS(SDetailTabWidget){}SLATE_END_ARGS()void Construct(const FArguments& InArgs);private:TSharedPtr<SWidgetSwitcher> WidgetSwitcher;TSharedPtr<STreeView<TSharedRef<FTreeViewItemBase>>> TreeViewItem;TArray<TSharedRef<FTreeViewItemBase>> TreeViewSource;public:TSharedRef<class ITableRow> GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase, const TSharedRef< class STableViewBase >& TableViewBase);void OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase, TArray<TSharedRef<FTreeViewItemBase>>& OutChildren);void SetItemData();
};
#include "DetailTabWidget.h"#include "TestActor.h"
#include "TreeViewItemBaseBool.h"
#include "TreeViewItemBaseNumber.h"
#include "Kismet/GameplayStatics.h"
#include "Widgets/Layout/SWidgetSwitcher.h"void SDetailTabWidget::Construct(const FArguments& InArgs)
{ChildSlot[SAssignNew(WidgetSwitcher, SWidgetSwitcher)+ SWidgetSwitcher::Slot().HAlign(HAlign_Center).VAlign(VAlign_Center)[SNew(STextBlock).Text(FText::FromString("This DetailTab Panel"))]+ SWidgetSwitcher::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)[SAssignNew(TreeViewItem, STreeView<TSharedRef<FTreeViewItemBase>>).TreeItemsSource(&TreeViewSource)//委托,生成每一行,绑定自定义方法(事件),触发时生成.OnGenerateRow(this,&SDetailTabWidget::GenerateTreeViewRow).OnGetChildren(this,&SDetailTabWidget::OnGetChildren).HeaderRow(SNew(SHeaderRow)+ SHeaderRow::Column("Name").HeaderContentPadding(FMargin(0, 0, 0, 0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString(TEXT("名 称")))]]+ SHeaderRow::Column("Value").HeaderContentPadding(FMargin(0, 0, 0, 0)).FillWidth(0.4f)[SNew(SBorder).Padding(FMargin(10, 5, 5, 5))[SNew(STextBlock).Text(FText::FromString(TEXT("属 性")))]])]];SetItemData();
}TSharedRef<class ITableRow> SDetailTabWidget::GenerateTreeViewRow(TSharedRef<FTreeViewItemBase> ItemBase,const TSharedRef<class STableViewBase>& TableViewBase)
{return SNew(SMultiTableRow, TableViewBase, ItemBase);
}void SDetailTabWidget::OnGetChildren(TSharedRef<FTreeViewItemBase> ItemBase,TArray<TSharedRef<FTreeViewItemBase>>& OutChildren)
{ItemBase->Getchildren(OutChildren);
}void SDetailTabWidget::SetItemData()
{UWorld* World = GWorld->GetWorld();AActor* FindAcr = UGameplayStatics::GetActorOfClass(World, ATestActor::StaticClass());//这里需要注意TestActor必须放置在场景中,否则运行崩溃 if (AActorBase* ActorBase = Cast<AActorBase>(FindAcr)){TSharedRef<FTreeViewItemBaseBool> BoolItem = MakeShareable(new FTreeViewItemBaseBool(ActorBase,"bIsSelect" ));TreeViewSource.Add(BoolItem);TSharedRef<FTreeViewItemBaseNumber> NumberItem = MakeShareable(new FTreeViewItemBaseNumber(ActorBase,"TestInt32"));TreeViewSource.Add(NumberItem);TreeViewItem->RequestTreeRefresh();WidgetSwitcher->SetActiveWidgetIndex(1);}
}