You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 编辑器中配置绑定

  1. 创建ViewModel实例:可以把ViewModel做成ScriptableObject(全局数据),或者在场景空物体上挂载ViewModel脚本(单场景数据)。
  2. 给目标Text所在GameObject添加TextPropertyBinding脚本。
  3. 在Inspector面板中:
    • 拖入目标Text组件到Target Text字段
    • 拖入ViewModel实例到ViewModel字段
    • 在Property Name中填入绑定的属性名(比如UserName或UserLevelText)

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:12:18