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

MVVM模式下多Load实现类的NotifyPropertyChanged与访问器实现咨询

多类型载荷的MVVM规范实现方案

核心思路:抽象分层+泛型基类统一管理

要搞定这个问题,关键是把纯数据的Model和带交互/通知的ViewModel彻底分开,同时通过抽象基类统一处理多类型载荷的共性逻辑,避免重复代码。

1. 先给Model做减法:只保留纯数据

所有载荷类(SurfaceLoad、PointLoad等)只负责存储数据,绝对不碰任何交互或通知逻辑。先定义一个抽象基类统一共性属性:

public abstract class Load
{
    // 通用数据属性:比如载荷大小、方向
    public double Magnitude { get; set; }
    public LoadDirection Direction { get; set; }
}

// 具体载荷类只加专属数据
public class SurfaceLoad : Load
{
    public double AppliedArea { get; set; }
}

public class PointLoad : Load
{
    public double XPosition { get; set; }
    public double YPosition { get; set; }
}

LoadContainer也保持原样,只负责管理Load实例的集合,不涉及任何UI相关逻辑。

2. 构建ViewModel抽象基类:封装共性通知逻辑

创建一个泛型抽象基类,继承INotifyPropertyChanged,把所有通用的属性访问器和通知逻辑都放在这里,具体ViewModel只需要实现专属属性:

public abstract class LoadViewModelBase<TLoad> : INotifyPropertyChanged 
    where TLoad : Load, new()
{
    protected readonly TLoad _model;
    public event PropertyChangedEventHandler PropertyChanged;

    // 支持传入已有Model或新建Model
    protected LoadViewModelBase() => _model = new TLoad();
    protected LoadViewModelBase(TLoad existingModel) => _model = existingModel ?? new TLoad();

    // 通用通知方法
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 封装通用属性的访问器,带变更通知
    public double Magnitude
    {
        get => _model.Magnitude;
        set
        {
            if (_model.Magnitude != value)
            {
                _model.Magnitude = value;
                OnPropertyChanged();
            }
        }
    }

    public LoadDirection Direction
    {
        get => _model.Direction;
        set
        {
            if (_model.Direction != value)
            {
                _model.Direction = value;
                OnPropertyChanged();
            }
        }
    }

    // 暴露底层Model,方便和LoadContainer同步
    public TLoad Model => _model;
}

3. 具体载荷ViewModel:实现专属属性

每个具体载荷对应一个ViewModel,继承抽象基类后,只需要实现自己的专属属性访问器即可:

public class SurfaceLoadViewModel : LoadViewModelBase<SurfaceLoad>
{
    public SurfaceLoadViewModel() : base() { }
    public SurfaceLoadViewModel(SurfaceLoad model) : base(model) { }

    public double AppliedArea
    {
        get => _model.AppliedArea;
        set
        {
            if (_model.AppliedArea != value)
            {
                _model.AppliedArea = value;
                OnPropertyChanged();
            }
        }
    }
}

public class PointLoadViewModel : LoadViewModelBase<PointLoad>
{
    public PointLoadViewModel() : base() { }
    public PointLoadViewModel(PointLoad model) : base(model) { }

    public double XPosition
    {
        get => _model.XPosition;
        set
        {
            if (_model.XPosition != value)
            {
                _model.XPosition = value;
                OnPropertyChanged();
            }
        }
    }

    public double YPosition
    {
        get => _model.YPosition;
        set
        {
            if (_model.YPosition != value)
            {
                _model.YPosition = value;
                OnPropertyChanged();
            }
        }
    }
}

4. LoadContainer的ViewModel适配:管理ViewModel集合

为LoadContainer创建对应的ViewModel,负责把Model集合转换成ViewModel集合,同时处理集合变化的同步:

public class LoadContainerViewModel : INotifyPropertyChanged
{
    private readonly LoadContainer _modelContainer;
    private ObservableCollection<LoadViewModelBase<Load>> _loadViewModels;

    public event PropertyChangedEventHandler PropertyChanged;

    public LoadContainerViewModel(LoadContainer container)
    {
        _modelContainer = container;
        // 把现有Model批量转换成ViewModel
        _loadViewModels = new ObservableCollection<LoadViewModelBase<Load>>(
            _modelContainer.Loads.Select(ConvertModelToViewModel)
        );
        // 监听ViewModel集合变化,同步回Model容器
        _loadViewModels.CollectionChanged += LoadViewModels_CollectionChanged;
    }

    public ObservableCollection<LoadViewModelBase<Load>> LoadViewModels
    {
        get => _loadViewModels;
        set
        {
            if (_loadViewModels != value)
            {
                _loadViewModels = value;
                OnPropertyChanged();
            }
        }
    }

    // Model转ViewModel的工厂方法,处理多类型匹配
    private LoadViewModelBase<Load> ConvertModelToViewModel(Load model)
    {
        return model switch
        {
            SurfaceLoad sl => new SurfaceLoadViewModel(sl),
            PointLoad pl => new PointLoadViewModel(pl),
            AreaLoad al => new AreaLoadViewModel(al),
            _ => throw new ArgumentOutOfRangeException(nameof(model), "未知载荷类型")
        };
    }

    private void LoadViewModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // 同步新增/删除的ViewModel到Model容器
        if (e.NewItems != null)
        {
            foreach (var vm in e.NewItems.Cast<LoadViewModelBase<Load>>())
            {
                _modelContainer.Loads.Add(vm.Model);
            }
        }
        if (e.OldItems != null)
        {
            foreach (var vm in e.OldItems.Cast<LoadViewModelBase<Load>>())
            {
                _modelContainer.Loads.Remove(vm.Model);
            }
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

额外优化小技巧

  • 加入属性验证:在ViewModel的setter里可以加业务逻辑验证,比如载荷大小不能为负,验证不通过就不更新Model也不触发通知。
  • 统一命令处理:如果需要对载荷执行删除、复制等操作,可以在ViewModel基类里添加ICommand属性,统一管理交互命令。
  • 简化映射:如果Model和ViewModel属性很多,可以用AutoMapper这类工具自动转换,减少重复的属性包装代码。

内容的提问来源于stack exchange,提问作者Leon Bohmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:59:16