我试图学习WPF和MVVM问题,但遇到了障碍。 这个问题与这个问题类似,但不完全相同(handling-dialog -in-wpf-with-mvvm)…

我有一个使用MVVM模式编写的“Login”表单。

该表单有一个ViewModel,其中包含用户名和密码,它们使用普通的数据绑定绑定到XAML中的视图。 它还有一个“登录”命令,该命令被绑定到表单上的“登录”按钮,agan使用正常的数据绑定。

当“Login”命令触发时,它会调用ViewModel中的一个函数,该函数会触发并通过网络发送数据以登录。当这个函数完成时,有2个动作:

登录无效-我们只是显示一个消息框,一切正常 登录是有效的,我们需要关闭login表单,并让它返回true作为它的dialgresult…

问题是,ViewModel对实际的视图一无所知,所以它如何关闭视图并告诉它返回一个特定的dialgresult ??我可以在代码背后粘贴一些代码,和/或通过视图模型传递视图,但这似乎会击败整个点MVVM完全…


更新

最后,我违背了MVVM模式的“纯度”,让视图发布了一个Closed事件,并公开了一个Close方法。ViewModel会调用view。close。视图仅通过接口了解,并通过IOC容器连接,因此不会损失可测试性或可维护性。

公认的答案是-5票,这似乎相当愚蠢!虽然我很清楚通过“纯粹”解决问题所获得的良好感觉,但我肯定不是唯一一个认为200行事件、命令和行为只是为了避免以“模式”和“纯粹”为名的一行方法有点荒谬的人....


当前回答

假设你的登录对话框是第一个被创建的窗口,在你的LoginViewModel类中试试这个:

    void OnLoginResponse(bool loginSucceded)
    {
        if (loginSucceded)
        {
            Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
            window.Show();

            App.Current.MainWindow.Close();
            App.Current.MainWindow = window;
        }
        else
        {
            LoginError = true;
        }
    }

其他回答

在你需要关闭窗口的地方,简单地把这个放在视图模型中:

  foreach (Window window in Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
                return;
            }
        }

以下是我最初所做的,它确实有效,但它似乎相当冗长和丑陋(全局静态的任何东西都不好)

1: App.xaml.cs

public partial class App : Application
{
    // create a new global custom WPF Command
    public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}

2: LoginForm.xaml

// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />

3: LoginForm.xaml.cs

// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
    DialogResult = true;
    Close();
}

4: LoginFormViewModel.cs

// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
    App.LoggedIn.Execute(this, null);
}

我后来删除了所有这些代码,只让LoginFormViewModel在它的视图上调用Close方法。它最终变得更好,也更容易理解。在我看来,模式的意义在于让人们更容易理解你的应用程序在做什么,在这种情况下,MVVM使它比我没有使用它更难理解,而且现在是一个反模式。

为什么不直接将窗口作为命令参数传递呢?

C#:

 private void Cancel( Window window )
  {
     window.Close();
  }

  private ICommand _cancelCommand;
  public ICommand CancelCommand
  {
     get
     {
        return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
                                                      ( window ) => Cancel( window ),
                                                      ( window ) => ( true ) ) );
     }
  }

XAML:

<Window x:Class="WPFRunApp.MainWindow"
        x:Name="_runWindow"
...
   <Button Content="Cancel"
           Command="{Binding Path=CancelCommand}"
           CommandParameter="{Binding ElementName=_runWindow}" />

这里有很多关于MVVM利弊的评论。就我而言,我同意Nir的观点;这是一个适当使用模式的问题,MVVM并不总是适合。人们似乎愿意牺牲所有最重要的软件设计原则,只是为了让它适合MVVM。

也就是说,. .我觉得你的案子可以做点重构。

在我遇到的大多数情况下,WPF可以让你在没有多个Windows的情况下工作。也许你可以尝试使用框架和页面来代替带有对话框的Windows。

在你的情况下,我的建议是让LoginFormViewModel处理LoginCommand,如果登录无效,将LoginFormViewModel上的属性设置为适当的值(false或一些枚举值,如UserAuthenticationStates.FailedAuthentication)。对于成功登录(true或其他enum值),也可以执行相同的操作。然后使用DataTrigger来响应各种用户身份验证状态,并可以使用简单的Setter来更改Frame的Source属性。

让你的登录窗口返回一个对话框,我认为这是你感到困惑的地方;dialgresult实际上是ViewModel的一个属性。在我使用WPF的有限经验中,当某些事情感觉不对时,通常是因为我在考虑如何在WinForms中做同样的事情。

希望这能有所帮助。

你可以让ViewModel公开一个View注册到的事件。然后,当ViewModel决定关闭视图的时间时,它会触发导致视图关闭的事件。如果你想要传回一个特定的结果值,那么你会在ViewModel中有一个属性。