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);}
}
相关文章:
ue5 创建多列StreeView的方法与理解
创建StreeView的多列样式怎么就像是创建单行单列差不多?貌似就是在单行单列中加入了多列widget? 示例代码 DetailTabWidget #pragma once #include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget {SLATE_BEGIN_ARGS(SDetailT…...
C# OnnxRuntime部署DAMO-YOLO香烟检测
目录 说明 效果 模型信息 项目 代码 下载 参考 说明 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name:input tensor:Floa…...
陕西省地标-DB61/T 1121-2018 政务服务中心建设和运营规范
揭秘陕西省智慧政务服务中心新标准:打造高效便捷的服务新体验 随着信息化时代的深入发展,智慧政务已成为提升政府服务效率、优化营商环境的重要举措。陕西省作为全国政务改革的先行者,近期颁布了《陕西省地标-DB61_T 1121-2018 政务服务中心…...
UDP协议(20250303)
1. UDP UDP:用户数据报协议(User Datagram Protocol),传输层协议之一(UDP,TCP) 2. 特性 发送数据时不需要建立链接,节省资源开销不安全不可靠的协议 //一般用在实时性比较高…...
【四.RAG技术与应用】【12.阿里云百炼应用(下):RAG的云端优化与扩展】
在上一篇文章中,我们聊了如何通过阿里云百炼平台快速搭建一个RAG(检索增强生成)应用,实现文档智能问答、知识库管理等基础能力。今天咱们继续深入,聚焦两个核心问题:如何通过云端技术优化RAG的效果,以及如何扩展RAG的应用边界。文章会穿插实战案例,手把手带你踩坑避雷。…...
Docker新手入门(持续更新中)
一、定义 快速构建、运行、管理应用的工具。 Docker可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用。 所谓镜像,就是将应用所需的函数库、依赖、配置等应用一起打包得到的。 所谓容器,为每个镜像的应用进程创建…...
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架 1. 引言 本教程旨在帮助嵌入式开发小白从零开始,学习如何在STM32F407微控制器上实现一个基于串口的数据接收程序。该程序能够通过判断数据头来接收一串数据,并将其存储到缓冲区中…...
HSPF 水文模型建模方法与案例分析实践技术应用
在水文模拟领域,HSPF 模型(Hydrological Simulation Program Fortran)与 SWAT 模型一样,都是备受瞩目的水文模型软件。HSPF 模型因其强大的功能和简便的操作,在全球范围内得到了广泛应用。该模型不仅能够在缺乏测量数据…...
设置 CursorRules 规则
为什么要设置CursorRules? 设置 CursorRules 可以帮助优化代码生成和开发流程,提升工作效率。具体的好处包括: 1、自动化代码生成 :通过定义规则,Cursor 可以根据你的开发需求自动生成符合规定的代码模板,…...
人工智能AI在汽车设计领域的应用探索
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活…...
《当AI生成内容遭遇审核:需求与困境的深度剖析》:此文为AI自动生成
AI 内容审核:数字时代的守门人 在当今数字技术迅猛发展的浪潮中,AI 在内容生成领域取得了令人瞩目的成就,成为了推动创新与变革的核心力量。以 AI 绘画为例,从早期简单粗糙的图像生成,到如今能够创作出细节丰富、风格多…...
【无人机与无人车协同避障】
无人机与无人车协同避障的关键在于点云数据的采集、传输、解析及实时应用,以下是技术实现的分步解析: 1. 点云数据采集(无人机端) 传感器选择: LiDAR:通过激光雷达获取高精度3D点云(精度达厘米…...
ComfyUI AnimeDiff动画参数总结
ComfyUI AnimeDiff动画参数总结 一、动画生成核心参数 参数名称建议值/范围作用说明备注步数(Steps)15-25控制AI计算迭代次数,越高细节越精细,但耗时更长推荐20步,显存不足可降至15步CFG值7.0-8.5提示词对画面的控制…...
No manual entry for printf in section 3
问题描述 在尝试查看 printf 的 C 函数手册页(即 man 3 printf)时遇到了 “No manual entry for printf in section 3” 的错误信息。 解决方案 出现这问题,是由于系统上没有安装对应的部分的手册页,因此安装对应的部分的手册…...
React 之 Redux 第二十八节 学习目标与规划大纲及概要讲述
接下来 开始Redux 全面详细的文档输出,主要基于一下几个方面,欢迎大家补充指正 一、Redux 基础概念 为什么需要 Redux? 前端状态管理的挑战(组件间通信、状态共享) Redux 解决的问题:集中式、可预测的状态…...
OSPF路由ISIS路由与路由学习对比(OSPF vs ISIS Routing Learning Comparison)
OSPF路由ISIS路由与路由学习对比 1.OSPF 路由学习规律 OSPF使用链路状态数据库(Link State Database)来存储网络拓扑信息。每个OSPF路由器通过交换链路状态更新(Link State Updates)来了解整个网络的拓扑,并根据收到…...
PMP项目管理—资源管理篇—1.规划资源管理
文章目录 基本信息4W1HITTO输入工具与技术输出 三种组织结构图和职位描述组织分解结构 OBS职责分派矩阵 RAMRACI矩阵说明 文本格式 资源管理计划团队章程 基本信息 4W1H what: 规划资源管理是定义如何估算、获取、管理和利用端对以及实物资源的过程。why: 资源规划用于确定和…...
Kafka 消息 0 丢失的最佳实践
文章目录 Kafka 消息 0 丢失的最佳实践生产者端的最佳实践使用带有回调的 producer.send(msg, callback) 方法设置 acks all设置 retries 为一个较大的值启用幂等性与事务(Kafka 0.11)正确关闭生产者与 flush() 方法 Broker 端的最佳实践设置 unclean.l…...
机器学习算法——回归任务
回归分析是估计因变量和自变量之间关系的过程。 目录 1、多元线性回归 2、岭回归 3、Lasso回归 4、弹性网络回归 5、多项式回归 6、指数回归 7、自然对数回归 8、广义线性模型 GLM 9、Cox比例风险模型 10、决策树回归 11、随机森林回归 12、梯度提升回归 13、XGBoost回归 14、…...
【数据库】数据库基础
第一章 数据库基础 一、数据库基础1.1 数据库系统的体系结构 (三层模式两级映像)1.1.1 逻辑模式1.1.2 外模式1.1.3 内模式1.1.4 外模式/模式映象1.1.5 逻辑模式/内模式映象1.1.6 逻辑独立性1.1.7 物理独立性 1.2 数据模型 一、数据库基础 1.1 数据库系统…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
