通知Model中的异步更改的ViewModel

时间:2016-02-10 13:48:28

标签: c# wpf mvvm

我对MVVM比较陌生,我想知道构建应用程序的最佳方法。以下是我的模型示例:

  public class ModelSource : ModelBase
  {
    #region Fields

    private int _isLoading;

    private BackgroundWorker worker = new BackgroundWorker();

    private ObservableCollection<PCDatabase> _databases;

    #endregion //Fields

    #region Properties

    public ObservableCollection<PCDatabase>Databases
    {
        get
        {
            if (_databases == null)
            {

                _databases = new ObservableCollection<PCDatabase>();
            }
            return _databases;
        }
        set
        {
            _databases = value;
            this.OnPropertyChanged("Databases");
        }
    }

    public int IsLoading
    {
        get
        {
            return _isLoading;
        }

        set
        {
            _isLoading = value;
            OnPropertyChanged("IsLoading");
        }
    }


    #endregion

    #region Methods

    /// <summary>
    /// Gets all Databases from the Server
    /// </summary>
    public void getDatabasesAsync(ConfigDatabaseConnection _currentConfig)
    {
      //execute SQL Query...
    }

(ModelBase实现了INotifyPropertyChanged)。

这是我对应的ViewModel:

namespace DbRestore.ViewModel
{
public class ViewModelSource : ViewModelBase
{
    private ObservableCollection<PCDatabase> _databases;

    private ModelSource _modelSource;

    private ICommand _populateDatabaseCommand;

    public ConfigDatabaseConnection _currentConfig;

    public ViewModelSource()
    {
        this.ModelSource = new ModelSource();
    }

    #region Commands

    /// <summary>
    /// Command that opens a Database Connection Dialog 
    /// </summary>
    public ICommand OpenDataBaseConnectionCommand
    {
        get
        {
            if (_populateDatabaseCommand == null)
            {
                _populateDatabaseCommand = new RelayCommand(
                    param => this.PopulateDatabases()
                    );
            }
            return _populateDatabaseCommand;
        }
    }

    public ObservableCollection<PCDatabase> Databases
    {
        get
        {
            return _databases;
        }

        set
        {
            _databases = value;
            OnPropertyChanged("Databases");
        }
    }

    #endregion //Commands

    public void PopulateDatabases()
    {
        ModelSource.getDatabasesAsync(_currentConfig);
    } 

调用ModelSource.getDatabasesAsync(_currentConfig)在我的模型中获取我的SQL数据。由于我的一些SQL查询非常复杂,我实现了一个异步运行这些查询的后台工作程序。

如何将数据存入我的ViewModel,该ViewModel绑定到我的View?或者我的设计方法总体上是错误的?

我考虑过并尝试过的事情:

  • 直接绑定到模型:工作,但我被告知这是一个
    不好的做法,应用程序逻辑应该驻留在模型中。

  • 将SQL查询移动到ViewModel中:也可以,但后来我的模型 class似乎是多余的 - 它只是一个自定义数据类型。

  • 同步运行查询并直接分配Observable 我的模型中的集合在我的ViewModel中的Observable Collection中。也 工作,但后来我遇到了BackgroundWorker的问题, 因为ViewModel不知道查询实际完成的时间。

2 个答案:

答案 0 :(得分:2)

  1. 将所有数据库逻辑移动到服务(aka存储库)类中。
  2. 只要您不需要围绕特定模型的任何特殊视图相关逻辑,就可以直接绑定到模型属性,而不是为每个模型创建十几个ViewModel代理类。因此,公开PCDatabase的集合是可以的。
  3. 由于您使用的是BackgroundWorker,我假设您使用的是.NET Framework 3.5并且没有TPL。

    public interface IPCDatabaseRepository
    {
        void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler);
    }
    
    public class PCDatabaseRepository : IPCDatabaseRepository
    {
        public void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler)
        {
            var worker = new BackgroundWorker();
    
            worker.DoWork += (sender, args) =>
            {
                args.Result = // Execute SQL query...
            };  
    
            worker.RunWorkerCompleted += (sender, args) =>
            {
                resultHandler(args.Result as IList<PCDatabase>);
                worker.Dispose();
            };
    
            worker.RunWorkerAsync();
        }
    }
    
    public class ViewModelSource : ViewModelBase
    {
        private readonly IPCDatabaseRepository _databaseRepository;
        private ObservableCollection<PCDatabase> _databases;
        private bool _isBusy;
    
        public ViewModelSource(IPCDatabaseRepository databaseRepository /*Dependency injection goes here*/)
        {
            _databaseRepository = databaseRepository;
            LoadDatabasesCommand = new RelayCommand(LoadDatabases, () => !IsBusy);
        }
    
        public ICommand LoadDatabasesCommand { get; private set; }
    
        public ObservableCollection<PCDatabase> Databases
        {
            get { return _databases; }
            set { _databases = value; OnPropertyChanged("Databases"); }
        }
    
        public bool IsBusy 
        { 
            get { return _isBusy; }
            set { _isBusy = value; OnPropertyChanged("IsBusy"); CommandManager.InvalidateRequerySuggested(); }
        }
    
        public void LoadDatabases()
        {
            IsBusy = true;
    
            _databaseRepository.GetPCDatabasesAsync(results =>
            {
                Databases = new ObservableCollection(results);
                IsBusy = false;
            });
        }
    

答案 1 :(得分:1)

你看过这些文章吗?

异步编程:异步MVVM应用程序的模式:数据绑定 https://msdn.microsoft.com/en-us/magazine/dn605875.aspx

异步编程:异步MVVM应用程序的模式:命令 https://msdn.microsoft.com/en-us/magazine/dn630647.aspx

这些应该涵盖一个好的策略,尤其是在使用async / await时。