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

如何针对含关联属性的模型设计MVVM架构及UI更新逻辑?

模型层设计

首先让Model1和Model2实现INotifyPropertyChanged接口,确保属性变更能通知UI。同时Model2的Property4设计为计算属性,依赖List<Model1>中元素的属性值,并且要监听列表内元素的属性变化,以及列表本身的增删操作。

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Model1 : INotifyPropertyChanged
{
    private int _property1;
    private int _property2;
    private int _property3;

    public int Property1
    {
        get => _property1;
        set
        {
            if (_property1 != value)
            {
                _property1 = value;
                OnPropertyChanged();
            }
        }
    }

    public int Property2
    {
        get => _property2;
        set
        {
            if (_property2 != value)
            {
                _property2 = value;
                OnPropertyChanged();
            }
        }
    }

    public int Property3
    {
        get => _property3;
        set
        {
            if (_property3 != value)
            {
                _property3 = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

public class Model2 : INotifyPropertyChanged
{
    private int _property1;
    private int _property2;
    private ObservableCollection<Model1> _model1List;

    public int Property1
    {
        get => _property1;
        set
        {
            if (_property1 != value)
            {
                _property1 = value;
                OnPropertyChanged();
            }
        }
    }

    public int Property2
    {
        get => _property2;
        set
        {
            if (_property2 != value)
            {
                _property2 = value;
                OnPropertyChanged();
            }
        }
    }

    public ObservableCollection<Model1> Model1List
    {
        get => _model1List;
        set
        {
            if (_model1List != value)
            {
                if (_model1List != null)
                {
                    // 取消旧列表元素的事件订阅
                    foreach (var item in _model1List)
                    {
                        item.PropertyChanged -= Model1Item_PropertyChanged;
                    }
                    _model1List.CollectionChanged -= Model1List_CollectionChanged;
                }

                _model1List = value;

                if (_model1List != null)
                {
                    // 订阅新列表的元素变更和集合变更事件
                    foreach (var item in _model1List)
                    {
                        item.PropertyChanged += Model1Item_PropertyChanged;
                    }
                    _model1List.CollectionChanged += Model1List_CollectionChanged;
                }

                OnPropertyChanged();
                OnPropertyChanged(nameof(Property4)); // 列表变化时触发Property4更新
            }
        }
    }

    // 计算属性,这里以Property1的最大值为例
    public int Property4
    {
        get
        {
            if (Model1List == null || Model1List.Count == 0)
                return 0;
            return Model1List.Max(m => m.Property1);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

    private void Model1Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // 当Model1的任意属性变化时,触发Property4的更新
        OnPropertyChanged(nameof(Property4));
    }

    private void Model1List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        // 处理列表元素的增删,订阅/取消订阅事件
        if (e.NewItems != null)
        {
            foreach (Model1 item in e.NewItems)
            {
                item.PropertyChanged += Model1Item_PropertyChanged;
            }
        }

        if (e.OldItems != null)
        {
            foreach (Model1 item in e.OldItems)
            {
                item.PropertyChanged -= Model1Item_PropertyChanged;
            }
        }

        // 列表元素变化时,更新Property4
        OnPropertyChanged(nameof(Property4));
    }
}

核心说明:

  • 用ObservableCollection替代List,自带集合变更通知,能监听元素的增删动作。
  • Model2订阅每个Model1的PropertyChanged事件,只要任意Model1的属性变化,就触发Property4的更新。
  • Property4为只读计算属性,每次获取时重新计算值,确保始终同步最新状态。
视图模型层设计

视图模型作为视图和模型的中间层,持有Model2实例,提供页面交互所需的命令,同时自身实现INotifyPropertyChanged以支持额外视图属性的绑定。

public class DetailsModel2ViewModel : INotifyPropertyChanged
{
    private Model2 _model2;

    public Model2 Model2
    {
        get => _model2;
        set
        {
            if (_model2 != value)
            {
                _model2 = value;
                OnPropertyChanged();
            }
        }
    }

    // 示例:添加Model1元素的命令
    public ICommand AddModel1Command { get; }

    public DetailsModel2ViewModel()
    {
        // 初始化示例数据
        Model2 = new Model2
        {
            Property1 = 10,
            Property2 = 20,
            Model1List = new ObservableCollection<Model1>
            {
                new Model1 { Property1 = 5, Property2 = 15, Property3 = 25 },
                new Model1 { Property1 = 8, Property2 = 18, Property3 = 28 }
            }
        };

        AddModel1Command = new RelayCommand(AddModel1);
    }

    private void AddModel1()
    {
        Model2.Model1List.Add(new Model1 { Property1 = 0, Property2 = 0, Property3 = 0 });
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

// 简单的RelayCommand实现(使用MVVM框架可直接用框架自带实现)
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute();
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

核心说明:

  • 视图模型持有Model2实例,视图直接绑定到Model2的属性即可实现数据同步。
  • 提供AddModel1Command这类交互命令,处理页面上的用户操作(如添加列表元素)。
  • 如需添加视图专属属性(如表单验证状态),可在视图模型中扩展并实现通知逻辑。
视图层设计

以MAUI/WPF的XAML视图为例,绑定视图模型属性,实现数据展示与编辑:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="YourNamespace.DetailsModel2Page"
             Title="Model2详情">
    <ContentPage.BindingContext>
        <local:DetailsModel2ViewModel />
    </ContentPage.BindingContext>

    <ScrollView>
        <StackLayout Padding="10" Spacing="15">
            <!-- 展示并编辑Model2的Property1和Property2 -->
            <Entry Placeholder="Model2 Property1" Text="{Binding Model2.Property1, Mode=TwoWay}" />
            <Entry Placeholder="Model2 Property2" Text="{Binding Model2.Property2, Mode=TwoWay}" />

            <!-- 展示并编辑Model1列表 -->
            <Label Text="Model1列表" FontSize="Large" FontAttributes="Bold" />
            <ListView ItemsSource="{Binding Model2.Model1List}" HasUnevenRows="True">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal" Spacing="10" Padding="5">
                                <Entry Placeholder="Property1" Text="{Binding Property1, Mode=TwoWay}" WidthRequest="100" />
                                <Entry Placeholder="Property2" Text="{Binding Property2, Mode=TwoWay}" WidthRequest="100" />
                                <Entry Placeholder="Property3" Text="{Binding Property3, Mode=TwoWay}" WidthRequest="100" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            <!-- 添加Model1元素按钮 -->
            <Button Text="添加Model1" Command="{Binding AddModel1Command}" />

            <!-- 展示计算属性Property4 -->
            <Label Text="Property4 (Model1 Property1最大值):" />
            <Label Text="{Binding Model2.Property4}" FontSize="Large" FontAttributes="Bold" />
        </StackLayout>
    </ScrollView>
</ContentPage>

核心说明:

  • 视图的BindingContext设置为DetailsModel2ViewModel,完成数据绑定的基础配置。
  • 用Entry控件绑定可编辑属性,通过Mode=TwoWay确保UI修改同步到模型。
  • ListView绑定Model2.Model1List,每个列表项直接绑定Model1的属性,实现批量编辑。
  • Property4直接绑定到模型的计算属性,当Model1属性或列表变化时自动更新UI显示。

内容的提问来源于stack exchange,提问作者Markiian Khylynskyi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 21:10:52