C# wpf mvvm处理ViewModel中的集合属性及其值在模型中的传播
C# wpf mvvm处理ViewModel中的集合属性及其值在模型中的传播,c#,wpf,design-patterns,mvvm,binding,C#,Wpf,Design Patterns,Mvvm,Binding,ViewModel中绑定到View中元素的属性通常从 某些模型类实例,当值在视图中更新时,该属性将值的更改传播到基础模型类实例中。 但是,如果在ViewModel中有一个属性返回,例如根据值构造的observedcollection 在模型中,视图中的值的更改仅更改ObservableCollection中的值。它不会将值的更改传播到基础模型类实例中 例如,您有一个模式对话框(视图),显示该对话框及其数据上下文(视图模型),并且视图模型包含模型类的实例。 现在 情况A)关闭对话框时,您的Vie
ViewModel
中绑定到View
中元素的属性通常从
某些模型类实例,当值在视图中更新时,该属性将值的更改传播到基础模型类实例中。
但是,如果在ViewModel
中有一个属性返回,例如根据值构造的observedcollection
在模型中,视图中的值的更改
仅更改ObservableCollection中的值。它不会将值的更改传播到基础模型类实例中
例如,您有一个模式对话框(视图
),显示该对话框及其数据上下文(视图模型
),并且视图模型包含模型类的实例。
现在
- 情况A)关闭对话框时,您的ViewModel只有返回非集合类型(int、string…)的属性
您知道模型类实例具有更新的值
- 情况B)您有一个ViewModel,该ViewModel的属性公开了一个ObservableCollection,当您关闭对话框时,model类
实例不包含更新的值
处理这一问题的常见模式/做法是什么?
我认为您可以有一个方法将ViewModel
中存在的数据写入模型实例,并在对话框关闭时调用此方法。但就MVVM和WPF的哲学而言,这似乎是不自然的
请记住,前面提到的例子只是一个例子。这通常适用于任何视图模型交互
如果有人不明白我想描述的内容:
让我们来上一节示范课
public enum A
{
A1,
A2,
A3
}
public class MyModel
{
private float[][] array;
public MyModel()
{
array = new float[Enum.GetNames(typeof(A)).Length][];
foreach (A a in EnumUtil.GetValues<A>())
{
array[(int) a] = new float[3]; // any number, really, but it will be referenced later in text
}
}
public A EnumerationA { get; set; }
public float this[A a, int b]
{
get
{
return array[(int) a][b];
}
set
{
array[(int)a][b] = value;
}
}
public float[] ArraySlice
{
get
{
return array[(int) EnumerationA];
}
}
}
公共枚举A
{
A1,
A2,
A3
}
公共类MyModel
{
专用浮点[][]数组;
公共MyModel()
{
array=newfloat[Enum.GetNames(typeof(A)).Length][];
foreach(EnumUtil.GetValues()中的A)
{
数组[(int)a]=new float[3];//实际上可以是任何数字,但稍后将在文本中引用它
}
}
公共枚举{get;set;}
公共浮动本[A,int b]
{
得到
{
返回数组[(int)a][b];
}
设置
{
数组[(int)a][b]=值;
}
}
公共浮动[]阵列片
{
得到
{
返回数组[(int)枚举A];
}
}
}
和一个ViewModel类
public class MyViewModel : ViewModelBase
{
private MyModel _myModel;
private ObservableCollection<float>[] _tmp = new ObservableCollection<float>[Enum.GetNames(typeof(A)).Length];
public MyViewModel()
{
// in constructor we will add 3 values to each collection
}
public A EnumerationA
{
get
{
return _myModel.EnumerationA;
}
set
{
if (Enum.Equals(_myModel.EnumerationA, value) == false)
{
_myModel.EnumerationA = value;
RaisePropertyChanged("EnumerationA");
RaisePropertyChanged("ArraySlice");
}
}
}
public ObservableCollection<float> ArraySlice
{
get
{
return _tmp[(int) EnumerationA];
}
}
}
公共类MyViewModel:ViewModelBase
{
私有MyModel\u MyModel;
私有ObservableCollection[]_tmp=新ObservableCollection[Enum.GetNames(typeof(A)).Length];
公共MyViewModel()
{
//在构造函数中,我们将向每个集合添加3个值
}
公共普查
{
得到
{
返回_myModel.EnumerationA;
}
设置
{
if(Enum.Equals(_myModel.EnumerationA,value)==false)
{
_myModel.EnumerationA=值;
RaisePropertyChanged(“枚举A”);
RaisePropertyChanged(“ArraySlice”);
}
}
}
公共可观测采集阵列切片
{
得到
{
返回_tmp[(int)枚举a];
}
}
}
ArraySlice可以绑定到任何数据网格等…因此,在本例中,当用户完成时,存储在ObservableCollections数组中的值不会保存在模型实例中
但是,如果我们没有返回ObservaleCollection的属性,而是有三个属性,每个属性都返回float,那么这些值将很容易传播到属性setter中的底层模型类实例中
最好我欢迎一个想法/解决方案,在视图/视图模型/模型之间的交互结束后,不需要我做任何事情 @Sheridan-你好,你告诉我我可以通知你这个问题-在你的情况下我没有得到什么A,为什么你说简单属性直接传播到模型?是否将值保存回ViewModel中每个setter中的模型?@Alejandro是的,对于简单值,我在ViewModel中没有备份ViewModel属性的字段。我通常只使用模型的字段/属性。因此,在setter中,我直接写回模型。我认为这是正确的方法。这有什么问题吗?我真的不喜欢这种方法,但这当然是可能的。问题是,它绕过了任何验证用户输入的机会,这可能会使模型在某些点处于无效状态,甚至可能对足够复杂的属性和组合抛出异常。此外,如果用户想要取消编辑,您也无法轻松地取消用户编辑。我的首选方法是将所有内容映射到ViewModel,然后将其映射回,或者使用DTO并绑定到该DTO,然后将其转换回模型。另一个选项是订阅
CollectionChanged
,然后从模型中添加/删除