C# mvvm解决嵌套模型和视图模型
我试图理解何时使用模型,何时查看模型等等。尤其是嵌套列表。所以我的viewmodels处理事件和命令,并在basic中修改模型。。。因此,在我的基本示例中,它看起来主要像是viewmodel引用了模型和委托属性C# mvvm解决嵌套模型和视图模型,c#,wpf,mvvm,C#,Wpf,Mvvm,我试图理解何时使用模型,何时查看模型等等。尤其是嵌套列表。所以我的viewmodels处理事件和命令,并在basic中修改模型。。。因此,在我的基本示例中,它看起来主要像是viewmodel引用了模型和委托属性 public string Name { get { return _model.Name; } set { _model.Name = value.T
public string Name
{
get { return _model.Name; }
set
{
_model.Name = value.Trim();
RaisePropertyChanged("Name");
}
}
但是如果我有一个模型列表,它也有一个模型列表,那么现在会发生什么呢。
示例:具有待办事项列表的用户。那我现在能做什么呢?我的第一个想法是创建UserViewModel
&TodoViewModel
。因此,我有一个observeCollection
,每个UserViewModel
都有一个observeCollection
每次添加Todo时,我都会创建Todo
和TodoViewModel
将Todo对象绑定到TodoViewModel
等等?这听起来像是一个大过载的对象
我要严格吗?为什么这里需要ViewModels?我错过了什么?为什么不在模型中实施InotifyProperty更改?事件处理和命令是不同的文件,我可以将参数绑定到它们。所以模型不知道视图,视图也不需要视图模型?我不确定最好的方法,但是如果模型需要一些特殊的逻辑来处理,我会为模型创建特殊的视图模型。若它并没有特殊的逻辑,或者使用它需要两行代码,那个么我在高级视图模型中做所有的工作,就像你们案例中的用户一样。然后,如果它变大了,我会重构它,并将代码移到嵌套视图模型中。我不确定最好的方法,但如果模型需要一些特殊的逻辑来使用它,我会为它创建特殊的视图模型。若它并没有特殊的逻辑,或者使用它需要两行代码,那个么我在高级视图模型中做所有的工作,就像你们案例中的用户一样。然后,如果它变大,我会重构它,并将代码移动到嵌套视图模型。视图模型仅用于视图。它充当模型和视图之间的粘合代码 **这是什么意思** 例如,您有一个
用户列表
页面或表单(视图)。然后您将拥有一个UserListViewModel
,它保存所有DTO(数据传输对象)。您不应直接将这些DTO作为属性公开,而应作为带有属性的字段公开,这些属性遵循数据绑定的INotifyPropertyChanged
实现。在视图模型中,您可以向视图公开命令(委托),以执行诸如http请求、数据处理、I/O等操作。但是,对于DTO的任何更改,您应该正确地通知视图,以便视图知道发生了什么,如果他们需要知道的话
不要将INotifyPropertyChanged应用于视图模型中的每个模型或DTO,因为正如您所说,这只是另一种开销
MVVM模式的一个更具OCD的版本是MVPVM(模型-视图-演示者-视图模型),其中命令和数据操作在演示者内部完成,而不是在视图模型上完成,视图模型严格地作为数据绑定的模型。视图模型仅用于视图。它充当模型和视图之间的粘合代码 **这是什么意思** 例如,您有一个
用户列表
页面或表单(视图)。然后您将拥有一个UserListViewModel
,它保存所有DTO(数据传输对象)。您不应直接将这些DTO作为属性公开,而应作为带有属性的字段公开,这些属性遵循数据绑定的INotifyPropertyChanged
实现。在视图模型中,您可以向视图公开命令(委托),以执行诸如http请求、数据处理、I/O等操作。但是,对于DTO的任何更改,您应该正确地通知视图,以便视图知道发生了什么,如果他们需要知道的话
不要将INotifyPropertyChanged应用于视图模型中的每个模型或DTO,因为正如您所说,这只是另一种开销
MVVM模式的一个更加OCD的版本是MVPVM(Model View Presenter View Model),其中命令和数据操作在Presenter内完成,而不是在视图模型上完成,这使得视图模型严格地作为数据绑定的模型。如果严格遵循MVVM的概念,那么,连接值和模型的属性的实现就不正确了。 此实现假定ViewModel具有关于模型内部结构和功能的“知识”。 而ViewModel不应该有这样的知识 在MVVM的“严格”实现中,模型没有属性。 她只有方法和事件。 ViewModel调用public方法并将所需的值传递给它。 之后模型的状态是否会更改-ViewModel不知道。 如果状态已更改,则模型将引发事件。 收到此事件后,ViewModel将读取必要的数据并更新其属性,这些属性用于视图中的绑定 演示代码: 型号
public delegate void NameChangedHandler(object sender, string newName);
public class ModelName
{
public event NameChangedHandler NameChangedEvent;
private string name;
public void SendName(string name)
{
// Some business logic to handle the accepted value.
// You can transfer to the server, change other values and the like.
// In this example is simply stored in a private field.
name = name.Trim();
if (this.name != name)
{
this.name = name;
NameChangedEvent?.Invoke(this, this.name);
}
}
}
public class ViewModelName : BaseINPC
{
private ModelName model = new ModelName();
public ViewModelName()
=> model.NameChangedEvent += NameChangedMethod;
private void NameChangedMethod(object sender, string newName)
=> Name = newName;
private string _name;
public string Name { get => _name; set => Set(ref _name, value); }
protected override void OnValueChange<T>(ref T oldValue, T newValue, string propertyName)
{
base.OnValueChange(ref oldValue, newValue, propertyName);
if (propertyName == nameof(Name))
model.SendName(Name);
}
}
INPC的基本实施
/// <summary>Base class implementing INotifyPropertyChanged.</summary>
public abstract class BaseINPC : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Called AFTER the property value changes.</summary>
/// <param name="propertyName">The name of the property.
/// In the property setter, the parameter is not specified. </param>
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary> A virtual method that defines changes in the value field of a property value. </summary>
/// <typeparam name = "T"> Type of property value. </typeparam>
/// <param name = "oldValue"> Reference to the field with the old value. </param>
/// <param name = "newValue"> New value. </param>
/// <param name = "propertyName"> The name of the property. If <see cref = "string.IsNullOrWhiteSpace (string)" />,
/// then ArgumentNullException. </param>
/// <remarks> If the base method is not called in the derived class,
/// then the value will not change.</remarks>
protected virtual void Set<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = "")
{
if (string.IsNullOrWhiteSpace(propertyName))
throw new ArgumentNullException(nameof(propertyName));
if ((oldValue == null && newValue != null) || (oldValue != null && !oldValue.Equals(newValue)))
OnValueChange(ref oldValue, newValue, propertyName);
}
/// <summary> A virtual method that changes the value of a property. </summary>
/// <typeparam name = "T"> Type of property value. </typeparam>
/// <param name = "oldValue"> Reference to the property value field. </param>
/// <param name = "newValue"> New value. </param>
/// <param name = "propertyName"> The name of the property. </param>
/// <remarks> If the base method is not called in the derived class,
/// then the value will not change.</remarks>
protected virtual void OnValueChange<T>(ref T oldValue, T newValue, string propertyName)
{
oldValue = newValue;
RaisePropertyChanged(propertyName);
}
}
///实现INotifyPropertyChanged的基类。
公共抽象类BaseINPC:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
///在属性值更改后调用。
///属性的名称。
///在属性设置器中,未指定参数。
public void RaisePropertyChanged([CallerMemberName]字符串propertyName=”“)
=>PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
///定义属性值的值字段中的更改的虚拟方法。
///属性值的类型。
///引用具有旧值的字段。
///新的价值观。
///属性的名称。如果,
///然后是一个例外。
///如果派生类中未调用基方法,
///那么该值将不会更改。
受保护的虚拟无效集(参考T oldV