跨视图模型共享模型的C#WPF最佳实践

时间:2020-05-31 22:35:10

标签: wpf viewmodel

  1. 我正在编写一个小型科学程序。它有很多变量作为输入。
  2. 用户必须在窗口(或用户界面)中输入所有输入变量。但是,我无法将所有输入都放在一个窗口(XAML)中。因此,我创建了几个视图,用户只需按NEXT按钮即可在下一个视图中输入数据。
  3. 所有这些视图都具有关联的ViewModel。它们都继承自基本ViewModel。
  4. 所以,我的问题是:我是否要在基本ViewModel中编写所有变量的属性?像这样:
namespace ScienceProgram
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {

        #region Usual Boiler-plate stuff for BindableBase

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }       
        #endregion

        #region Properties (All scientific parameters and calculations )

        public double ParamA { get; set; }
        public double ParamB { get; set;  }
        // ...
        // Lots of parameters //
        // ...
        public double ParamA23 { get; set;  }


        public double TotalLength()
        {
            return ParamA + ParamB + ParamA23;                
        }

        // ...
        // Lots of other methods 
        // ...

        #endregion 

    }
}
  1. 或者我为所有输入参数创建一个单独的类(例如ScienceParameters.cs),并执行以下操作(这是我正在做的事情):
namespace ScienceProgram
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {

        // Global Parameter // Shared across ViewModels //
        public static ScienceParameters scienceParameters = new ScienceParameters ();

        #region Usual Boiler-plate stuff for BindableBase

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }       
        #endregion

    }
}
  1. 我正在使用后一种方法。当用户在文本框中输入数据时,我只需将其存储为:
namespace ScienceProgram
{
    public class UserInputView1ViewModel : BaseViewModel
    {
        #region User Input 
        private double _paramA;
        public double ParamA
        {
            get { return _paramA; }
            set
            {                
                _paramA = value;                
                OnPropertyChanged("ParamA");

                // Store Value // 
                scienceParameters.ParamA = value;
            }
        }
    }
}
        #endregion
  1. 哪种方法是正确的?或者,还有更好的方法?
  2. 我一直在寻找一些最佳实践,但我一直直接转到EventAggregator并使用单例。我认为我不需要那些。
  3. 正如许多人所说,我认为解决方案是“将模型传递给视图模型的构造函数。”但是我对如何做感到有些困惑。那不会在每个viewModel中创建一个新实例吗?
  4. 对不起,如果这听起来像一个愚蠢的问题,但是我在过去一周一直在寻找一个直接的答案,并且还没有找到解决方案。

非常感谢。

1 个答案:

答案 0 :(得分:1)

您已经关闭。我看到您正在复制可能会导致维护问题的数据存储的区域。根据您所说的,我认为您不需要拥有BaseViewModel的方法(但是,如果您按照此处的回答进行操作,那么我将向您展示如何使BaseViewModel与其用途相比更加有用)。

如果我的理解正确,您有多个视图,因为您希望用户按“ NEXT”进入下一个导航视图。每个视图都有一个关联的ViewModel。但在幕后,您希望将所有属性收集到一个类实例中。

对我来说,这里的最佳实践是学习MVVM体系结构。 MVVM的主要学习是了解“关注点分离”。主要是,您的数据(即用户的所有输入值,也称为“模型”)与您在UI中提供给它们的Views和ViewModels无关。阅读相关内容,您会更好地掌握这种良好做法。

这就是我要怎么做。

1)创建一个Model类(ScienceParameters.cs),它将保存您希望用户输入的所有属性。示例:

public class ScienceParameters
{
        public double ParamA { get; set; }
        public double ParamB { get; set;  }
        // ...
        // Lots of parameters //
        // ...
        public double ParamA23 { get; set;  }


        public double TotalLength()
        {
            return ParamA + ParamB + ParamA23;                
        }  
}

然后在提供给用户的每个UI上,向他们显示一个视图和一个关联的ViewModel,使他们可以访问此数据的“查看/获取”或“存储/设置”部分。示例:

public class UserInput1ViewModel: INotifyPropertyChanged
{
        public UserInput1ViewModel(ScienceParameters model)
        {
            this.Model = model;
        }

        public ScienceParameters Model { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }       

        public double ParamA
        {
            get { return this.Model.ParamA; }
            set
            {   
                // Store the user's data directly into the Model, not the ViewModel!
                this.Model.ParamA = value;                
                OnPropertyChanged(nameof(this.ParamA));  // <-- avoid magic words like "ParamA" in quotes, this is bad coding and can cause maintenance issues.
            }
        }

}
public class UserInput2ViewModel: INotifyPropertyChanged
{
        public UserInput2ViewModel(ScienceParameters model)
        {
            this.Model = model;
        }

        public ScienceParameters Model { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }       

        public double ParamB
        {
            get { return this.Model.ParamB; }
            set
            {   
                this.Model.ParamB = value;                
                OnPropertyChanged(nameof(this.ParamB));
            }
        }

}

额外功劳:

如果要消除在每个ViewModel中编写PropertyChanged垃圾的重复性,则将其放入BaseViewModel。

public class BaseViewModel : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }       
}

然后从您的ViewModels中删除所有那些PropertyChanged行,并使用基来定义每个ViewModel:

public class UserInput1ViewModel: BaseViewModel
{
   // ...
}