如何针对含关联属性的模型设计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
相关产品推荐
相关产品推荐

