Unity中基于MVVM与INotifyPropertyChanged实现属性到组件的绑定问题
在Unity中实现MVVM属性到UI组件的自动绑定
1. 实现ViewModel基类(封装INotifyPropertyChanged)
先写一个通用基类,避免每个ViewModel重复实现接口逻辑:
using System.ComponentModel; using System.Runtime.CompilerServices; public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { if (Equals(backingField, value)) return false; backingField = value; OnPropertyChanged(propertyName); return true; } }
再写业务具体的ViewModel,比如用户信息类:
public class UserViewModel : ViewModelBase { private string _userName; public string UserName { get => _userName; set => SetProperty(ref _userName, value); } private int _userLevel; public string UserLevelText => $"当前等级: {_userLevel}"; public int UserLevel { get => _userLevel; set { if (SetProperty(ref _userLevel, value)) { // 通知依赖属性更新 OnPropertyChanged(nameof(UserLevelText)); } } } }
2. 编写View层绑定脚本
以Text控件为例,写一个绑定脚本监听ViewModel变化并更新UI:
using UnityEngine; using UnityEngine.UI; using System.ComponentModel; public class TextPropertyBinding : MonoBehaviour { [SerializeField] private Text targetText; [SerializeField] private ViewModelBase viewModel; [SerializeField] private string propertyName; private void Start() { if (viewModel == null || targetText == null || string.IsNullOrEmpty(propertyName)) { Debug.LogError("绑定参数不完整,请检查ViewModel、Text组件和属性名"); return; } // 初始赋值 RefreshText(); // 订阅属性变化事件 viewModel.PropertyChanged += OnPropertyUpdated; } private void OnPropertyUpdated(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == propertyName || e.PropertyName == string.Empty) { RefreshText(); } } private void RefreshText() { var propertyInfo = viewModel.GetType().GetProperty(propertyName); if (propertyInfo != null) { targetText.text = propertyInfo.GetValue(viewModel)?.ToString(); } else { Debug.LogError($"ViewModel中不存在名为{propertyName}的属性"); } } private void OnDestroy() { // 取消订阅避免内存泄漏 if (viewModel != null) { viewModel.PropertyChanged -= OnPropertyUpdated; } } }
3. 编辑器中配置绑定
- 创建ViewModel实例:可以把ViewModel做成ScriptableObject(全局数据),或者在场景空物体上挂载ViewModel脚本(单场景数据)。
- 给目标Text所在GameObject添加
TextPropertyBinding脚本。 - 在Inspector面板中:
- 拖入目标Text组件到
Target Text字段 - 拖入ViewModel实例到
ViewModel字段 - 在
Property Name中填入绑定的属性名(比如UserName或UserLevelText)
- 拖入目标Text组件到
4. 测试属性更新
在任意逻辑中修改ViewModel属性,比如:
var userVM = FindObjectOfType<UserViewModel>(); userVM.UserName = "测试用户"; userVM.UserLevel = 15;
此时对应的Text控件会自动更新显示内容。
常见问题排查
- UI无响应:检查ViewModel属性的setter是否调用了
SetProperty/OnPropertyChanged;确认绑定脚本已正确订阅事件;检查属性名拼写是否一致。 - 内存泄漏:必须在
OnDestroy中取消订阅事件,否则ViewModel销毁后,View的引用会导致内存无法释放。 - 嵌套属性绑定:如果属性是嵌套对象,需要给嵌套对象也实现INotifyPropertyChanged并单独订阅,或者在嵌套属性变化时触发外层ViewModel的
OnPropertyChanged。
内容的提问来源于stack exchange,提问作者Junaid Pathan
相关产品推荐
相关产品推荐

