如何在MVVM中编写ViewModelBase

Dri*_*100 23 c# wpf mvvm viewmodel

我是WPF编程环境中的新手.我正在尝试使用MVVM设计模式编写程序.

我做了一些研究并阅读了一些与之相关的文章,很多时候我遇到过这个问题

ViewModelBase

我知道它是什么..但是我可以具体知道我应该从哪里开始写出我自己的ViewModelBase吗?就像......真正了解正在发生的事情而不会变得太复杂.谢谢 :)

Lie*_*ero 72

如果您不知道内部发生了什么,那么使用MVVM框架是没有价值的.

让我们一步一步地构建您自己的ViewModelBase类.

  1. ViewModelBase是所有视图模型的通用类.让我们将所有常见逻辑移到这个类.

  2. 你的ViewModel应该实现INotifyPropertyChanged(你明白为什么吗?)

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    [CallerMemberName]属性不是必需的,但它允许你写: OnPropertyChanged();而不是OnPropertyChanged("SomeProperty");,所以你将避免代码中的字符串常量.例:

    public string FirstName
    {
        set
        {
            _firtName = value;
            OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
        }
        get{ return _firstName;}
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,OnPropertyChanged(() => SomeProperty)不再推荐这个,因为我们nameof在C#6中有操作员.

  3. 通常的做法是实现调用PropertyChanged的属性,如下所示:

    public string FirstName
    {
        get { return _firstName; }
        set { SetProperty(ref _firstName, value); }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    让我们在viewmodelbase中定义SetProperty:

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;
        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    它只是PropertyChanged在属性值更改并返回true时触发事件.当值未更改并且返回false时,它不会触发事件.基本思想是,该SetProperty方法是虚拟的,您可以在更具体的类中扩展它,例如触发验证,或通过调用PropertyChanging事件.

这很漂亮.这是您在此阶段应包含的所有ViewModelBase.其余的取决于你的项目.例如,您的应用程序使用页面基本导航,并且您已编写自己的NavigationService以处理来自ViewModel的导航.因此,您可以将NavigationSerivce属性添加到ViewModelBase类,因此,如果需要,您可以从所有视图模型中访问它.

为了获得更多的可重用性并保持SRP,我有一个名为BindableBase的类,它几乎是我们在这里完成的INotifyPropertyChanged的实现.我在每个WPF/UWP/Silverligt/WindowsPhone解决方案中重用此类,因为它是通用的.

然后我在每个项目中创建从BindableBase派生的自定义ViewModelBase类:

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>()   
}
Run Code Online (Sandbox Code Playgroud)

如果我有app,它使用基于页面的导航我也为页面视图模型指定基类.

public abstract PageViewModelBase : ViewModelBase
{
    //for example all my pages has title:
    public string Title {get; private set;}
}
Run Code Online (Sandbox Code Playgroud)

我可以有另一个类用于对话框:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 男人......谢谢你的分享!这绝对让我更好地了解了这件事是什么哈哈...欣赏:) (2认同)

Moh*_*ari 8

下面的类可以用作 WPF 项目中的 ViewModelBase:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

ViewModel类的一个例子是:

public class MyViewModel : ViewModelBase
{
    private int myProperty;
    public int MyProperty
    {
        get { return myProperty; }
        set { SetProperty(ref myProperty, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

为了便于编写,可以使用以下代码段

<?xml version="1.0" encoding="utf-8"?>  
<CodeSnippets  
    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">  
    <CodeSnippet Format="1.0.0">  
        <Header>  
            <Title>OnPropertyChanged</Title>  
        </Header>      
      <Snippet>
          <SnippetTypes>
            <SnippetType>SurroundsWith</SnippetType>
            <SnippetType>Expansion</SnippetType>
          </SnippetTypes>
          <Declarations>
            <Literal>
              <ID>TYPE</ID>
              <ToolTip>Property type</ToolTip>
              <Default>int</Default>
            </Literal>
            <Literal>
              <ID>NAME1</ID>
              <ToolTip>Property name</ToolTip>
              <Default>MyProperty</Default>
            </Literal>
          </Declarations>
            <Code Language="CSharp">  
                <![CDATA[private $TYPE$ _$NAME1$;
public $TYPE$ $NAME1$
{
    get => _$NAME1$; 
    set => SetProperty(ref _$NAME1$, value);    
}]]>  
            </Code>  
        </Snippet>  
    </CodeSnippet>  
</CodeSnippets>  
Run Code Online (Sandbox Code Playgroud)


Orc*_*usZ 5

您有一些nuget包来实现MVVM

  1. MVVM灯
  2. MVVM交叉
  3. 棱镜

对我来说,MVVM light对初学者来说比较容易,因为它提供了一些代码示例。

因此,最好是安装此nuget软件包,查看生成的代码,如果需要,请联系我们以获取更多说明。

  • 我想说些有关NuGet的信息,但我看到了您有关_“了解生成的代码,如果需要的话,请给我们返回更多解释” _。:) (2认同)