使用MVVM Light Framework在ViewModel中实现SOLID

问题描述 投票:0回答:1

我目前正在将我的代码重构为符合SOLID。 SOLID原则已成为大多数开发人员的习惯,而我却忽略了学习该原则。但是现在我需要它!

我的ViewModel包含事件,字段,属性(ObseravbleCollection和其他数据类型),ICommand,为特定视图创建的方法,并且只有当该集合将在其他视图中使用时,某些ViewModel才包含属性和方法。因此,对于某些列表,它更像是可重用的VM。即用于管理员工的视图和用于出于其他目的列出员工的视图。

我正在尝试使用WPF理解并制作一个简单的Login UI,而我的ViewModel看起来像这样。

using GalaSoft.MvvmLight.Command;

using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;

using UnderstandingSOLID.Models;
using UnderstandingSOLID.Services.API;

namespace UnderstandingSOLID.ViewModels
{
    public class ViewModel_Login : VMBase
    {
        #region events
        public EventHandler OnLoggedIn;
        #endregion

        #region vars

        #endregion

        #region properties
        public ObservableCollection<Model_ServerType> ServerTypes { get; set; } = new ObservableCollection<Model_ServerType>();

        private Model_ServerType _SelectedServerType = new Model_ServerType();
        public Model_ServerType SelectedServerType
        {
            get { return _SelectedServerType; }
            set
            {
                Set(nameof(SelectedServerType), ref _SelectedServerType, value);
            }
        }

        private string _ServerName = null;
        public string ServerName
        {
            get { return _ServerName; }
            set { Set(nameof(ServerName), ref _ServerName, value); }
        }

        private string _Username = null;
        public string Username
        {
            get { return _Username; }
            set { Set(nameof(Username), ref _Username, value); }
        }

        private string _Password = null;
        public string Password
        {
            get { return _Password; }
            set { Set(nameof(Password), ref _Password, value); }
        }

        private bool _ShowLoginUI = false;
        public bool ShowLoginUI
        {
            get { return _ShowLoginUI; }
            set { Set(nameof(ShowLoginUI), ref _ShowLoginUI, value); }
        }
        #endregion

        #region commands
        public ICommand Command_Connect { get; set; }
        public ICommand Command_Help { get; set; }
        #endregion

        #region ctors
        public ViewModel_Login()
        {
            InitCommands();

            // used only in UWP & WPF
            // or anything that supports design time updates
            if (base.IsInDesignMode)
            {
                DesignData();
            }
        }
        #endregion

        #region command methods
        void Command_Connect_Click()
        {
            Connect();
        }

        void Command_Help_Click()
        {
            // does nothing at the moment
        }
        #endregion

        #region methods
        void InitCommands()
        {
            if (Command_Connect == null) Command_Connect = new RelayCommand(Command_Connect_Click);
            if (Command_Help == null) Command_Help = new RelayCommand(Command_Help_Click);
        }

        /// <summary>
        /// codes here are messy since they are only used for desining the UI
        /// </summary>
        void DesignData()
        {
            this.ShowLoginUI = true;
            this.ServerTypes.Clear();

            string[] serverTypes = new string[]
            {
                "Database Engine",
                "Analysis Services"
            };

            for (int i = 0; i < serverTypes.Length; i++)
            {
                this.ServerTypes.Add(new Model_ServerType()
                {
                    Id = i,
                    ServerType = serverTypes[i]
                });
            }

            this.ShowDlgMsg("Title", "Failed to login");
        }

        // this is called in Loaded event in MainPage.xaml.cs
        public async Task RefreshData()
        {
            this.ServerTypes.Clear();

            string[] serverTypes = new string[]
            {
                "Database Engine",
                "Analysis Services",
                "Reporting Services",
                "Integration Services"
            };

            for(int i = 0; i < serverTypes.Length; i++)
            {
                this.ServerTypes.Add(new Model_ServerType()
                {
                    Id = i,
                    ServerType = serverTypes[i]
                });
            }
        }

        async Task Connect()
        {
            if(this.SelectedServerType.ServerType != null && await ApiClient.I.Login(this.Username, this.Password))
            {
                this.HideDlgMsg();

                // navigate to main page
                // but we'll just Invoke an event for simplicity

                this.OnLoggedIn?.Invoke(this, null);
            }
            else
            {
                this.ShowDlgMsg("Error", "Failed to login");
            }
        }
        #endregion
    }
}

VMBase看起来像这样

using GalaSoft.MvvmLight;

namespace UnderstandingSOLID.ViewModels
{
    public abstract class VMBase : ViewModelBase
    {
        private bool _ShowMessage = false;
        public bool ShowMessage
        {
            get { return _ShowMessage; }
            set { Set(nameof(ShowMessage), ref _ShowMessage, value); }
        }

        private string _MessageTitle = null;
        public string MessageTitle
        {
            get { return _MessageTitle; }
            set { Set(nameof(MessageTitle), ref _MessageTitle, value); }
        }

        private string _MessageBody = null;
        public string MessageBody
        {
            get { return _MessageBody; }
            set { Set(nameof(MessageBody), ref _MessageBody, value); }
        }

        public virtual void ShowDlgMsg(string title, string message)
        {
            this.ShowMessage = true;
            this.MessageTitle = title;
            this.MessageBody = message;
        }

        public void HideDlgMsg()
        {
            this.ShowMessage = false;
        }
    }
}

因此,这就是我通常在ViewModel中编写所有成员的方式。不用介意我如何在ViewModel_Login中的Connect方法中调用Login。只是简化了。

那么在这种情况下您如何实现SOLID原理?

示例登录应用程序在空闲状态下看起来像这样enter image description here

登录失败enter image description here

已登录enter image description here

可以在这里下载仓库https://github.com/Nullstr1ng/UnderstandingSOLID

c# mvvm solid-principles
1个回答
1
投票

编程原理就像盗版代码:与实际规则相比,它们更像是准则,它们并不总能改进。

您的VM看起来不错-这就是您执行ViewModels的方式。我使用不同的框架,所以我会做一些不同的事情,但是:

  • 您在ViewModel = GREAT中不使用视图类引用! (您仍应向我们展示View,至少是CodeBehind,因为这是MVVM中最棘手的部分)
  • APIClien t应该是一个依赖项,实例应该已经在构造函数中作为实例传递,并且接口od IAPIClient与静态属性相对应
  • [如果您真的要遵循SOLID,则每个ViewModel应该具有其自己的接口:IViewModel_Login,包含该视图使用的所有公共方法,属性和事件(实际上可以用来制作您可以指定的ViewModel_Login_Design类在XAML中仅返回一些静态值[有d:DataContext])
  • 您应该提取一个类来处理来自VMBase的消息-也许不在基础中使用它,而是将其注入使用它的ViewModels中。 Base应该只具有几乎所有ViewModel都通用的东西。

我还将在此处链接到ReactiveUI,因为我坚信其设计以及围绕IObservable的关注点以及将DynamicData用于动态集合是远远优于其他任何MVVM框架的经验。

© www.soinside.com 2019 - 2024. All rights reserved.