public string FirstName
{
set
{
_firstName = value;
OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
}
get{ return _firstName;}
}
public abstract ViewModelBase : BindableBase
{
//project specific logic for all viewmodels.
//E.g in this project I want to use EventAggregator heavily:
public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()
}
如果我有应用程序,使用基于页面导航,我还为页面视图模型指定基类。
public abstract PageViewModelBase : ViewModelBase
{
//for example all my pages have title:
public string Title {get; private set;}
}
我可以再上一堂对话课:
public abstract DialogViewModelBase : ViewModelBase
{
private bool? _dialogResult;
public event EventHandler Closing;
public string Title {get; private set;}
public ObservableCollection<DialogButton> DialogButtons { get; }
public bool? DialogResult
{
get { return _dialogResult; }
set { SetProperty(ref _dialogResult, value); }
}
public void Close()
{
Closing?.Invoke(this, EventArgs.Empty);
}
}
public abstract class ViewModelBase : INotifyPropertyChanged
{
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches the desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners.This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
// Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel类的一个例子是:
public class MyViewModel : ViewModelBase
{
private int myProperty;
public int MyProperty
{
get { return myProperty; }
set { SetProperty(ref myProperty, value); }
}
}
7条答案
按热度按时间um6iljoc1#
如果你不知道MVVM框架里面是怎么回事,那么使用MVVM框架就毫无意义。
因此,让我们一步一步地构建您自己的ViewModelBase类。
1.您的ViewModel应该实现
INotifyPropertyChanged
(您理解为什么吗?)[CallerMemberName]
属性不是必需的,但它将允许您写入:OnPropertyChanged();
而不是OnPropertyChanged("SomeProperty");
,这样可以避免在代码中使用字符串常量。请注意,不再推荐使用
OnPropertyChanged(() => SomeProperty)
,因为我们在C# 6中有nameof
操作符。1.实现调用PropertyChanged的属性是一种常见做法,如下所示:
让我们在视图模型库中定义SetProperty:
它只是在属性值改变时触发
PropertyChanged
事件并返回true。当属性值没有改变时它不触发事件并返回false。基本思想是,SetProperty
方法是虚拟的,你可以在更具体的类中扩展它,例如触发验证,或者通过调用PropertyChanging
事件。这很不错。这就是ViewModelBase在此阶段应包含的全部内容。其余内容取决于您的项目。例如,您的应用使用基于页面的导航,并且您编写了自己的NavigationService用于处理来自ViewModel的导航。因此,您可以将NavigationService属性添加到ViewModelBase类,以便您可以在需要时从所有视图模型访问它。
为了获得更多的可重用性并保持SRP,我有一个名为BindableBase的类,它几乎是INotifyPropertyChanged的实现,就像我们在这里所做的那样。我在每个WPF/UWP/Silverligt/WindowsPhone解决方案中重用这个类,因为它是通用的。
然后,在每个项目中,我创建自定义ViewModelBase类派生自BindableBase:
如果我有应用程序,使用基于页面导航,我还为页面视图模型指定基类。
我可以再上一堂对话课:
inn6fuwd2#
以下类可用作WPF项目中的ViewModelBase:
ViewModel类的一个例子是:
为便于编写,可使用以下snippet:
完整的代码可以从here下载。
mpgws1up3#
您有一些核心包来实现MVVM
1.棱镜
对我来说,对于初学者来说更容易的是MVVM轻,因为它提供了一些代码样本。
所以最好是安装这个nuget软件包,看看生成的代码,如果需要的话,可以返回给我们更多的解释。
eufgjt7s4#
在大多数MVVM框架中,ViewModel基类实际上包含很少的代码-通常只是INotifyPropertyChanged和一些帮助函数的实现。
查看MVVM Light的ViewModelBase和ObservableObject类的源代码。ObservableObject主要是INotifyPropertyChanged实现-使用lambda表达式而不是“魔术字符串”作为属性名称。ViewModelBase扩展了ObservableObject,主要是一个实用方法,用于确定您是否在Visual Studio设计器中运行
1zmg4dgp5#
我喜欢this BaseVewModel,它给你的视图模型一个很好的干净的风格。看看各种“之前”和“之后”的比较。当然,没有任何强制性要求-如果您不喜欢BaseViewModel提供的功能,请不要使用它。或者,您可以修改它,因为您有源代码。请特别注意,有三种不同的方法可以实现具有更改通知的属性-选择您理解/感觉舒适的复杂程度。
zvokhttg6#
为了今天重新讨论这个问题,我想在为Visual Studio编写MVVM代码时提供额外的生产力改进。
VisualStudio中的Intellisense可以自动创建
SetProperty
样板方法。为此,我在窗口的XAML中设置了ViewModel然后,每当我引用{Binding Path=NewProperty}
时,右键单击并选择Quick Actions and Refactoring...
(或通过Ctrl .
)。如果没有创建SetProperty
方法,它将在ViewModel类中自动创建。此外,它将生成绑定所需的属性和字段。然而,这种方法有缺点
INotifyPropertyChanged
未实现,OnPropertyChanged
方法未实现(如果需要)1.这需要在每个ViewModel中完成
1.这是特定于Visual Studio的
好处:
1.一旦在项目中定义了
SetProperty
方法,使用Quick Actions and Refactoring...
选项将只为您生成必要的属性和字段。如果您从ViewModelBase
继承,这也适用。下面是由Visual Studio生成的
SetProperty
方法。uyto3xhc7#
}