从另一个 ViewModel 调用函数

Call a function from another ViewModel

我是这门语言的初学者,正在努力学习更多关于最佳实践以及如何做得更好的方法...

我有这个 repo 作为示例应用程序:https://github.com/Albvadi/NavigationMVVM

如果你运行它,一切正常。您可以导航到其他视图并增加在所有视图中共享的计数器。

但是,如果您在第 24 行取消注释 MainViewModel.cs 文件中的 ActualView 分配并将登录视图放在 InitialView 前面,我不知道如何重定向用户登录成功后Initialview

当登录正确时,我将用户数据填充到 ManagerData 中,所有这些我都需要调用 MainViewModel 中的函数以将用户重定向到初始视图。我怎样才能从 LoginViewModel 调用另一个 MainViewModel 而不是从视图调用?

更新:添加相关代码

App.xaml

<Application
    x:Class="NavigationMVVM.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:NavigationMVVM"
    xmlns:Views="clr-namespace:NavigationMVVM.Views"
    xmlns:ViewModels="clr-namespace:NavigationMVVM.ViewModels"
    StartupUri="MainWindow.xaml">
    
    <Application.Resources>
        <DataTemplate
            DataType="{x:Type ViewModels:MainViewModel}">
            <local:MainWindow />
        </DataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:LoginViewModel}">
            <Views:Login />
        </DataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:InitialViewModel}">
            <Views:Initial />
        </DataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:FirstViewModel}">
            <Views:First />
        </DataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:SecondViewModel}">
            <Views:Second />
        </DataTemplate>
    </Application.Resources>
</Application>

MainWindow.xaml

<Window
    x:Class="NavigationMVVM.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:NavigationMVVM"
    xmlns:viewModels="clr-namespace:NavigationMVVM.ViewModels"
    mc:Ignorable="d"
    WindowStartupLocation="CenterScreen"
    Title="MainWindow"
    Height="450"
    Width="800">
    <Window.DataContext>
        <viewModels:MainViewModel />
    </Window.DataContext>

    <Grid>
        <ContentControl
            Content="{Binding ActualView}" />
    </Grid>
</Window>

BaseViewModel.cs

using System.ComponentModel;

namespace NavigationMVVM.Common
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        private BaseViewModel _ActualView;
        public BaseViewModel ActualView
        {
            get => _ActualView;
            set
            {
                _ActualView = value;
                RaisePropertyChanged(null);
            }
        }

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        #endregion
    }
}

MainViewModel.cs

using NavigationMVVM.Common;
using NavigationMVVM.Models;
using System.Windows.Input;

namespace NavigationMVVM.ViewModels
{
    public class MainViewModel : BaseViewModel
    {
        public DataManager SharedData = new DataManager();

        public InitialViewModel InitialVM;
        public FirstViewModel FirstVM;
        public SecondViewModel SecondVM;
        public LoginViewModel LoginVM;

        public MainViewModel()
        {
            LoginVM = new LoginViewModel(SharedData);
            InitialVM = new InitialViewModel(SharedData);
            FirstVM = new FirstViewModel(SharedData);
            SecondVM = new SecondViewModel(SharedData);

            ActualView = InitialVM;
            //ActualView = LoginVM;
        }

        public ICommand DisplayFirstView
        {
            get
            {
                return new RelayCommand(action => ActualView = FirstVM,
              canExecute => true);
            }
        }

        public ICommand DisplaySecondView
        {
            get
            {
                return new RelayCommand(action => ActualView = SecondVM,
              canExecute => true);
            }
        }

        public ICommand DisplayInitialView
        {
            get
            {
                return new RelayCommand(action => ActualView = InitialVM,
              canExecute => true);
            }
        }
    }
}

LoginViewModel.cs

using NavigationMVVM.Common;
using NavigationMVVM.Models;
using System.Diagnostics;
using System.Windows.Input;

namespace NavigationMVVM.ViewModels
{
    public class LoginViewModel : BaseViewModel
    {
        private DataManager _DataManager;

        public ICommand LoginCmd;
        public RelayCommand DoLoginCmd { get; }


        private string _Username;
        public string Username
        {
            get => _Username;
            set
            {
                _Username = value;
                RaisePropertyChanged(null);
            }
        }

        private string _Password;
        public string Password
        {
            get => _Password;
            set
            {
                _Password = value;
                RaisePropertyChanged(null);
            }
        }

        private string _MessageInfo;
        public string MessageInfo
        {
            get => _MessageInfo;
            set
            {
                _MessageInfo = value;
                RaisePropertyChanged(null);
            }
        }

        public LoginViewModel(DataManager sharedData)
        {
            _DataManager = sharedData;
            DoLoginCmd = new RelayCommand(param => DoLogin(), canExec => (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)));
        }

        public void DoLogin()
        {
            if (Username == "admin" && Password == "password")
            {
                _DataManager.User.Name = "Administrator";
                _DataManager.User.Mail = "admin@company.com";

                MessageInfo = "Login OK!... How to redirect??";
            }
            else
            {
                MessageInfo = "Username or Password incorrect!";
            }

        }
    }
}

谢谢。

有多种方法可以完成您想要的。我将 post 在 MVVM 模式中工作时非常常见的两种方法
基于事件的方法:

 public MainViewModel()
        {
            LoginVM = new LoginViewModel(SharedData);
            LoginVM.PropertyChanged += LoginVM_PropertyChanged;
            InitialVM = new InitialViewModel(SharedData);
            FirstVM = new FirstViewModel(SharedData);
            SecondVM = new SecondViewModel(SharedData);

            //ActualView = InitialVM;
            ActualView = LoginVM;
        }

        private void LoginVM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
           if(sender.GetType() == typeof(LoginViewModel) && e.PropertyName == "MessageInfo")
            {
                var loginVM = (LoginViewModel)sender;
                if (loginVM.MessageInfo == "OK")
                {
                    ActualView = InitialVM;
                }
            }
        }

Cunstructor 注入:

        private Action _loginAction;
        public LoginViewModel(DataManager sharedData, Action loginAction )
        {
            _DataManager = sharedData;
            DoLoginCmd = new RelayCommand(param => DoLogin(), canExec => (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)));
            _loginAction = loginAction;
        }

        public void DoLogin()
        {
            if (Username == "admin" && Password == "password")
            {
                _DataManager.User.Name = "Administrator";
                _DataManager.User.Mail = "admin@company.com";
             
                MessageInfo = "Login OK!... How to redirect??";
                _loginAction.Invoke();
            }
            else
            {
                MessageInfo = "Username or Password incorrect!";
            }

        }

 public MainViewModel()
        {
            LoginVM = new LoginViewModel(SharedData, () => ActualView = InitialVM);            
            InitialVM = new InitialViewModel(SharedData);
            FirstVM = new FirstViewModel(SharedData);
            SecondVM = new SecondViewModel(SharedData);
            //ActualView = InitialVM;
            ActualView = LoginVM;
        }

旁注:在 BaseViewModel 上放置 属性“ActualView”是一个相当奇怪的选择