Unreal UMG MVVM

news2024/11/19 4:51:40

Unreal UMG MVVM

文章目录

  • Unreal UMG MVVM
    • 背景
    • M - VM - V
    • 扩展点
    • === Editortime ===
    • Viewmodels 编辑器界面
    • View Bindings 编辑器界面
    • 蓝图编译相关
    • === Runtime ===
    • 创建 ViewModel
    • ViewModel 更新

背景

  1. 先阅读文档和 quabqi 的 UOD 视频分享,目前网上唯一的资料
  2. 看眼成熟的巨硬方案 WPF MVVM
  3. 剩下的就硬扒代码

M - VM - V

  1. Model:原始数据
  2. ViewModel:视图所需数据
  3. View:控件蓝图 WBP

扩展点

三个 Extension,注意 MVVM 是一个插件,通过这种方式扩展 WBP

// editortime
class MODELVIEWVIEWMODELBLUEPRINT_API UMVVMWidgetBlueprintExtension_View : public UWidgetBlueprintExtension
// runtime
class MODELVIEWVIEWMODEL_API UMVVMView : public UUserWidgetExtension
class MODELVIEWVIEWMODEL_API UMVVMViewClass : public UWidgetBlueprintGeneratedClassExtension

新增一个 BP Compiler, MVVM 自动生成了一部分 BP 代码

FKismetCompilerContext::RegisterCompilerForBP(UMVVMViewModelBlueprint::StaticClass(), &UMVVMViewModelBlueprint::GetCompilerForViewModelBlueprint);

=== Editortime ===

有两个编辑器界面

在这里插入图片描述

WidgetBlueprint.UMVVMBlueprintView,这也是通过 WBP Extension UMVVMWidgetBlueprintExtension_View 挂上的

两个编辑器,一个添加 VM Class,一个添加 Binding,最终存储在 UMVVMBlueprintView

// UMVVMBlueprintView
UPROPERTY(EditAnywhere, Category = "MVVM")
TArray<FMVVMBlueprintViewBinding> Bindings;

UPROPERTY(EditAnywhere, Category = "MVVM")
TArray<FMVVMBlueprintViewModelContext> AvailableViewModels;

Viewmodels 编辑器界面

在这里插入图片描述

// Editor AddViewModel
if (UWidgetBlueprint* WidgetBlueprint = WidgetBlueprintEditor->GetWidgetBlueprintObj())
{
	UMVVMEditorSubsystem* EditorSubsystem = GEditor->GetEditorSubsystem<UMVVMEditorSubsystem>();
	check(EditorSubsystem);

	UMVVMBlueprintView* CurrentBlueprintView = WeakBlueprintView.Get();
	if (!CurrentBlueprintView)
	{
		CurrentBlueprintView = EditorSubsystem->RequestView(WidgetBlueprint);
		WeakBlueprintView = CurrentBlueprintView;
		ViewModelsUpdatedHandle = CurrentBlueprintView->OnViewModelsUpdated.AddSP(this, &SMVVMViewModelPanel::HandleViewModelsUpdated);
	}

	EditorSubsystem->AddViewModel(WidgetBlueprint, SelectedClass);
}

View Bindings 编辑器界面

在这里插入图片描述

蓝图编译相关

UMVVMBlueprintView 编辑时数据编译到 UMVVMViewClass 给运行时用

// FMVVMViewBlueprintCompiler::PreCompileBindingSources
UMVVMViewClass* ViewExtension = NewObject<UMVVMViewClass>(Class);
CurrentCompilerContext->Compile(Class, BlueprintView, ViewExtension);
CurrentCompilerContext->AddExtension(Class, ViewExtension);

// FMVVMViewBlueprintCompiler::Compile
CompileSourceCreators(CompileResult.GetValue(), Class, BlueprintView, ViewExtension);
CompileBindings(CompileResult.GetValue(), Class, BlueprintView, ViewExtension);
ViewExtension->BindingLibrary = MoveTemp(CompileResult.GetValue().Library);

=== Runtime ===

创建 ViewModel

四种模式,134 都是自动创建并关联,以默认的 Create Instance 为例

视图模型创建类型描述
自动创建小部件会自动创建自己的 Viewmodel 实例。
手动创建小部件初始化时 Viewmodel 为 null,您需要手动创建一个实例并分配它。
全局视图模型集合指的是一个全局可用的视图模型,可以被项目中的任何小部件使用。需要全局视图模型标识符。
属性路径在初始化时,执行一个函数来查找 Viewmodel。Viewmodel 属性路径使用由句点分隔的成员名称。

Widget::Init -> Init Extensions -> MVVMViewClass -> MVVMView

在这里插入图片描述

UMVVMViewClass 每个 WBP Class 一份,是编辑时存下的绑定数据
MVVMView 每个 WBP Instance 一份
UMVVMViewClass 拉起每个 WBP Instance 的 MVVMView

void UMVVMViewClass::Initialize(UUserWidget* UserWidget)
{
	ensure(UserWidget->GetExtension<UMVVMView>() == nullptr);
	UMVVMView* View = UserWidget->AddExtension<UMVVMView>();
	if (ensure(View))
	{
		if (!bLoaded)
		{
			BindingLibrary.Load();
			bLoaded = true;
		}

		View->ConstructView(this);
	}
}

CreateInstance,如前所说,会自动创建并关联 ViewModel 实例和 WBP 实例

/**
* Instance UMVVMClassExtension_View for the UUserWdiget
*/
UCLASS(Transient, DisplayName="MVVM View")
class MODELVIEWVIEWMODEL_API UMVVMView : public UUserWidgetExtension

void UMVVMView::Construct()
{
   // Init ViewModel instances
   for (const FMVVMViewClass_SourceCreator& Item : ClassExtension->GetViewModelCreators())
   {
      Item.CreateInstance(ClassExtension, this, GetUserWidget());
   }
   ...
}

UObject* FMVVMViewClass_SourceCreator::CreateInstance(const UMVVMViewClass* InViewClass, UMVVMView* InView, UUserWidget* InUserWidget) const
{
	UObject* Result = nullptr;

	FObjectPropertyBase* FoundObjectProperty = FindFProperty<FObjectPropertyBase>(InUserWidget->GetClass(), PropertyName);
	if (ensureAlwaysMsgf(FoundObjectProperty, TEXT("The compiler should have added the property")))
	{
		auto AssignProperty = [FoundObjectProperty, InUserWidget](UObject* NewObject)
		{
			check(NewObject);
			if (ensure(NewObject->GetClass()->IsChildOf(FoundObjectProperty->PropertyClass)))
			{
				FoundObjectProperty->SetObjectPropertyValue_InContainer(InUserWidget, NewObject);
			}
		};

		if (bCreateInstance)
		{
			//...
		}
		else if (GlobalViewModelInstance.IsValid())
		{
			//...
		}
		else if (FieldPath.IsValid())
		{
			//...
		}
	}

	return Result;
}

ViewModel 更新

C++ 封装 SetXXX 函数来调用 UE_MVVM_SET_PROPERTY_VALUE 宏

/** After a field value changed. Broadcast the event. */
#define UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(MemberName) \
	BroadcastFieldValueChanged(ThisClass::FFieldNotificationClassDescriptor::MemberName)

/** If the property value changed then set the new value and notify. */
#define UE_MVVM_SET_PROPERTY_VALUE(MemberName, NewValue) \
	SetPropertyValue(MemberName, NewValue, ThisClass::FFieldNotificationClassDescriptor::MemberName)

/** Set the new value and notify if the property value changed. */
template<typename T, typename U>
bool SetPropertyValue(T& Value, const U& NewValue, UE::FieldNotification::FFieldId FieldId)
{
	if (Value == NewValue)
	{
		return false;
	}

	Value = NewValue;
	BroadcastFieldValueChanged(FieldId);
	return true;
}

蓝图换掉 SetProperty 指令

DEFINE_FUNCTION(UMVVMViewModelBase::execK2_SetPropertyValue)
{
	UMVVMViewModelBase* ViewModelContext = Cast<UMVVMViewModelBase>(Context);

	bResult = TargetProperty->Identical(TargetValuePtr, SourceValuePtr);
	if (!bResult)
	{
		// Set the value then notify that the value changed.
		TargetProperty->SetValue_InContainer(ViewModelContext, SourceValuePtr);
		ViewModelContext->BroadcastFieldValueChanged(FieldId);
	}
}

总之都是调用 BroadcastFieldValueChanged,ViewModel 派生了 INotifyFieldValueChanged 来监听属性变化

但是其实就是封装了 Setter,每次变了就调用下 Delegate 派发事件

class MODELVIEWVIEWMODEL_API UMVVMViewModelBase : public UObject, public INotifyFieldValueChanged

virtual FDelegateHandle AddFieldValueChangedDelegate(UE::FieldNotification::FFieldId InFieldId, FFieldValueChangedDelegate InNewDelegate) override final;

void UMVVMViewModelBase::BroadcastFieldValueChanged(UE::FieldNotification::FFieldId InFieldId)
{
	NotificationDelegates.BroadcastFieldValueChanged(this, InFieldId);
}

蓝图编辑的 Binding 数据,会在 WBP::SetXXXViewModel 时解绑旧的,再绑新的

// 贴下入口函数,代码不贴了
UMVVMView::SetViewModel
UMVVMView::EnableLibraryBinding

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1155012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回溯法(2)--图着色问题和旅行商问题

目录 一、图着色问题 1、算法设计 2、代码 二、旅行商问题 1、概述问题 2、穷举法 3、回溯法 一、图着色问题 1、算法设计 图着色问题&#xff0c;给定图中各个区域的相邻关系&#xff0c;抽象成一个无向图G&#xff08;V,E&#xff09;&#xff0c;给定m种颜色&…

【HarmonyOS】服务卡片 API6 JSUI跳转不同页面并携带参数

【关键字】 服务卡片、卡片跳转不同页面、卡片跳转页面携带参数 【写在前面】 本篇文章主要介绍开发服务卡片时&#xff0c;如何实现卡片点击跳转不同页面&#xff0c;并携带动态参数到js页面。在此篇文章“服务卡片 API6 JSUI跳转不同页面”中说明了如果跳转不同页面&#xf…

十、W5100S/W5500+RP2040树莓派Pico<PING(ICMP)检测网络连通性>

文章目录 1 前言2 协议简介2.1 什么是PING2.2 PING的优点2.3 PING的原理2.4 应用场景 3 WIZnet以太网芯片4 PING网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 烧录验证 5 注意事项6 相关链接 1 前言 随着网络应用的日益丰富和普及&…

代码随想录打卡第五十六天|1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和

1143.最长公共子序列 题目&#xff1a; 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的…

iPhone听筒声音小怎么办?分享5种修复方法!

最近有小伙伴反映&#xff1a;苹果手机使用了一段时间后&#xff0c;听筒声音突然变小了&#xff0c;这是什么情况&#xff1f;这确实是一个让人感到困扰的问题&#xff0c;特别是在进行重要通话的时候&#xff0c;如果听不到对方说话&#xff0c;那场面将会十分尴尬。那么&…

C语言--顺序查找、折半查找

顺序查找 实现逻辑 顺序查找&#xff08;sequential search&#xff09;就是按照数组的顺序一 一比较数组中的元素的值和所查找的值。如下图表所示&#xff0c;遍历数组进行比较。若找到&#xff0c;则break跳出循环。 a[0]a[1]a[2]a[3]a[4]912221334229?2212?2222? 实现…

出海数字化,国产CRM如何支撑?纷享销客这样思考

2023年&#xff0c;疫情阴霾逐渐消散&#xff0c;企业全球化扩张的齿轮重新加速。以科技企业、高端制造业为代表的优秀企业引领中国企业出海浪潮&#xff0c;外资企业在华的经营活跃度也在提升。 无论是”外资在华经营“还是”中资出海“&#xff0c;这些具备全球化理想的企业…

0代码0侵入的安卓骨架屏框架----二期优化

本文是对自定义骨架屏框架的优化说明。 针对目前对骨架屏的需求及为了实现骨架屏而付出的繁重劳动&#xff0c;而设计的一款0编码0业务侵入的骨架屏框架。感兴趣的可以先去看看这篇文章&#xff1a;一种简单的Android骨架屏实现方案----0侵入0成本 额&#xff0c;如果不看&am…

玻色量子成功研制光量子计算专用光纤恒温控制设备——“量晷”

​近日&#xff0c;北京玻色量子科技有限公司&#xff08;以下简称“玻色量子”&#xff09;成功研制出一款高精度量子计算专用光纤恒温控制设备——“量晷”&#xff0c;该设备能将光纤的温度变化稳定在千分之一摄氏度量级&#xff0c;即能够做到0.001C的温度稳定维持&#xf…

5道谷歌面试题:即使是天才也要怀疑自己能力了(附GPT4答案)

谷歌google&#xff0c;美国的跨国科技企业&#xff0c;致力于互联网搜索、云计算、广告技术等领域&#xff0c;开发并提供大量基于互联网的产品与服务。 这样一家实力雄厚前景无量的公司是众多求职者梦寐以求的地方&#xff0c;然而&#xff0c;谷歌的面试题却把很多优秀人才…

技术贴 | 一文掌握 Google Test 框架

一、简介 1. 引言 在开发过程中&#xff0c;如何保证代码的质量以及程序的正确性成为了我们亟需解决的问题&#xff0c;其中测试用例成为了不必可少的一部分。测试用例不仅可以帮助我们验证代码的正确性&#xff0c;还能帮助我们捕获潜在的错误&#xff0c;提高代码的可靠性和…

IO模块:钢铁安全绿色生产的智能化助手

钡铼I/O模块以其卓越的性能和可靠性&#xff0c;为钢铁行业的安全绿色生产提供了强有力的支持。这个模块拥有出色的实时监测功能&#xff0c;能够精确捕捉现场设备的工作状态&#xff0c;确保设备的正常运行。通过采用先进的预测性维护技术&#xff0c;钡铼I/O模块能够提前发现…

传统金融机构加入代币化浪潮,新一轮加密周期的重要组成部分?

新加坡金融管理局 (MAS) 成立了由日本金融厅 (FSA)、英国金融行为监管局 (FCA) 和瑞士金融市场监管局 (FINMA) 组成“守护者计划”政策制定者组&#xff08;Project Guardian&#xff09;&#xff0c;正在计划对固定收益、外汇和资产管理产品进行资产代币化试点&#xff0c;以推…

Python 的 Web 自动化测试的实践

Web 测试是软件测试中比较重要的一个分支&#xff0c;而要实现 Web 自动化测试则要求测试人员能熟练掌握自动化测试工具和编程语言。介绍免费开源的 Web 测试工具 Selenium&#xff0c;以及流行的编程语言 Python。根据自动化测试的原理&#xff0c;对网页元素的常用定位方式&a…

smartLink HW-DP新版提供更多扩展功能——用于PROFIBUS和HART系统中物联网集成

Softing工业自动化的smartLink HW-DP网关可独立于控制器访问PROFIBUS DP网络&#xff0c;且新发布的1.30固件版本还提供了更多数据连接和传输的扩展功能。 smartLink HW-DP可无缝集成到PROFIBUS网络中&#xff0c;而不会影响现有设备的运行。该网关还可为新的和现有的PROFIBUS …

非常爆火的流量卡推广上线了

流量卡推广可以通过“聚量推文”申请&#xff0c;一手渠道 现在非常火的推广项目就是流量卡推广了&#xff0c;佣金价格高 普遍的价格是几十上百块&#xff0c;你一天推广10个收入就接近4位数&#xff0c;还是比较可观的 聚量推客专注于app拉新&#xff0c;网推项目&#xff…

博客系统-项目测试

自动化博客项目 用户注册登录验证效验个人博客列表页博客数量不为 0 博客系统主页写博客 我的博客列表页效验 刚发布的博客的标题和时间查看 文章详情页删除文章效验第一篇博客 不是 "自动化测试" 注销退出到登录页面,用户名密码为空 用户注册 Order(1)Parameterized…

MyBatis-Plus使用——配置yml参数 常用的注解@Table,@TableId,@IdType,@TableField,CRUD的API接口

前言 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 本系列博客结合实际应用场景&#xff0c;阐述MyBatis-Plus实际…

Python开发技能实战-通过配置的代理服务器在具有外网连接的环境中在Pycharm中运行python代码

实现功能 打开科学上网工具&#xff0c;使得能够在浏览器科学上网&#xff0c;通过科学上网工具的配置文件&#xff0c;可以看出本地和远程代理的映射关系&#xff0c;此时&#xff0c;远程地址本地地址&#xff0c;远程端口本地端口。 1、在程序中配置请求网页代理请求。不需…

GTS GtsUnofficialApisUsageTestCases Failed

GTS 测试GtsUnofficialApisUsageTestCases失败如下&#xff1a; junit.framework.AssertionFailedError: There are 102 violation(s) com.google.android.gm / Landroid/window/BackEvent;->getProgress()F / BLOCKED / LINKING com.google.android.gm / Landroid/window/…