如何在Caliburn.Micro中处理登录/注销?

6
我刚开始学习Caliburn.Micro,想知道在我的应用程序中处理用户登录/注销周期的最佳方法是什么。我在网上看到一些建议,使用一个空的Shell-View来实现这个功能,该View可在登录视图和主应用程序视图之间切换,并分别使用自定义ViewModel。
我不太喜欢这个解决方案,因为对于我来说,这是两个具有非常不同属性(标题、图标、大小)的独立窗口,将一个窗口更改为另一个窗口的形式似乎不太合适。另一个问题是,登录窗口来自我无法控制的实用库,该库不使用Caliburn.Micro,而是普通的窗口,当用户点击“登录”时,该窗口会给我一个事件。
我还看到了一些建议,在Bootstrapper启动方法中显示此对话框,但我的问题是,用户可以选择“注销”应用程序,这应该再次显示登录对话框。在Bootstrapper中处理View之间的切换似乎是不合适的。
我希望有一种类似于Caliburn Conductor的ApplicationViewModel或ApplicationController,它可以在窗口之间进行切换,但不是在窗口内部切换Views,而是在LoginWindow和MainWindow之间进行切换,并且还应该处理整个应用程序的关闭(这也需要注销)。激活时,它将显示LoginWindow,处理登录事件,然后切换到主窗口(Shell)。如果用户选择“LogOut”,事件应再次冒泡到ApplicationViewModel/Controller,该ViewModel/Controller会停用/关闭MainWindow,执行注销操作,然后再次显示LoginDialog。类似地,Close事件将执行注销操作,但是然后关闭整个应用程序。
所以我的问题是:
1. 你对这个解决方案有什么看法?你有另一个/更好的解决方案吗?
2. 如何实现这个解决方案? ;-)
非常感谢!

当你说“普通的窗口”时,你是指WPF还是WinForms?此外,登录窗口是否会实际执行用户身份验证的工作,或者你需要处理“登录”事件并执行该操作? - Kioshiki
1
WPF窗口会触发一个登录事件,我使用从我的IoC容器获取的身份验证服务来处理它。 - aKzenT
2个回答

19
我认为你的问题的解决方案相当简单。
简而言之,您正在创建一个ViewModel作为Shell,该Shell在应用程序启动时用登录窗口表示。如果用户成功登录,则此窗口关闭,并且在Content窗口中显示相同实例的viewModel。如果用户注销,则再次显示登录窗口。
首先,创建一个接口IShell,其中公开两个委托LoginSuccessful和Logout。
public interface IShell
    {
        Action LoginSuccessful { get; set; }
        Action Logout { get; set; }
    }

接下来创建一个名为ShellViewModel的类,它实现了IShell接口

 public class ShellViewModel : Screen, IShell
    {
        public ShellViewModel()
        {
            LoginSuccessful = delegate { };
            Logout = delegate { };
        }

        public Action LoginSuccessful { get; set; }
        public Action Logout { get; set; }

        public void DoLogin()
        {
            LoginSuccessful();
        }

        public void DoLogout()
        {
            Logout();
        }
    }

DoLoginDoLogout方法是可以绑定到Button或其他适合您的控件的操作。

接下来的步骤是在您的引导程序中重写OnStartupMethod。这需要您选择的IoC框架导出的WindowManagerShellViewModel实例。

protected override void OnStartup(object sender, StartupEventArgs e)
        {
            var windowManager = IoC.Get<IWindowManager>();
            var viewModel = IoC.Get<IShell>();

            viewModel.LoginSuccessful =
                () => GuardCloseAndReopen("Content");

            viewModel.Logout =
                () => GuardCloseAndReopen("Login");

            windowManager.ShowWindow(viewModel, "Login");
        }

        private void GuardCloseAndReopen(string shellViewMode)
        {
            var windowManager = IoC.Get<IWindowManager>();
            var shellScreen = IoC.Get<IShell>() as Screen;

            Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;

            shellScreen.TryClose();

            Application.ShutdownMode = ShutdownMode.OnLastWindowClose;

            windowManager.ShowWindow(shellScreen, shellViewMode);
        }

这个技巧是:如果调用DoLogout方法,则通过在ShellViewModel上调用TryClose来关闭当前窗口。同时,通过将Application.ShutdownMode设置为OnExplicitShutdown,防止应用程序被关闭。然后,使用windowmanager,通过将“Login”作为上下文信息传递给windowManager,在登录模式下创建另一个窗口。实际上,这是相同的ViewModel,但具有不同的可视表示。
对于Logout,你需要做同样的事情,只是在外面包裹一层。
要使用Caliburn Conventions使其工作,需要像这里所示(并在那里解释)的特殊项目结构:
现在我挑战你使用此代码创建一个小型示例应用程序。 创建一个Login视图(其中包含使用按钮或其他控件进行登录),并创建一个带有注销按钮的Content视图,使用登录成功/注销方法。
这将用最少的代码和类来解决您的问题。希望对您有所帮助。

我也对这个很感兴趣。在你的例子中,ShellViewMode是什么?它应该是ShellViewModel还是只是一个字符串?此外,aKzenT表示登录窗口已经存在,那么这会如何影响你的设计? - Kioshiki
ShellViewMode 是一个打字错误。当然,这是一个将上下文信息传递给窗口管理器的字符串。您还可以将此上下文设置为枚举,这是一个好主意。对于现有的登录窗口,请将此 xaml 放入 Login.xaml,并将 UI 设置为 viewModel 中的现有操作。这可能并不那么复杂,因为大多数登录对话框只是一个带有用户名/密码文本框的登录/取消按钮。然后,ShellViewModel 可以通过验证委托来完成授权,以解耦此逻辑并使其在运行时可更改。 - Oliver Vogel
1
我认为在这种情况下使用字符串是正确的,但你说枚举会更好。我觉得你还没有理解我对现有登录窗口的想法(虽然我可能理解错了)。我考虑的是这样一种情况:必须使用来自二进制程序集的现有窗口,也许在公司中他们有一个 WPF 窗口或者甚至是 WindowsForms 表单,必须用于登录过程并公开事件等(可能是由于验证等原因)。我认为这将使找到一个干净的解决方案变得非常困难。 - Kioshiki
不错的解决方案,虽然像@Kioshiki提到的那样,我不确定我能像这样应用它,因为LoginWindow在另一个程序集中。而且我不确定在这里“共享”视图模型是否是个好主意。对于登录可能无所谓,但想象一下管理各种复杂窗口。在它们之间共享一个视图模型可能不是最好的主意。尽管如此,还是很好的解决方案。谢谢! - aKzenT

3
我尝试创建了一个基本可用的东西,但可能需要更多的工作才能真正使用。完整的注释和源代码可以在我的网站上找到这篇文章Caliburn.Micro登录窗口示例
我使用了Caliburn.Micro的来控制两个窗口之间的转换。您可以使用以下代码打开登录屏幕:
public void Handle(LoginEvent message)
{
    LoginWindow loginWindow = new LoginWindow();
    loginWindow.Login += new EventHandler<LoginEventArgs>(this.LoginWindow_Login);
    loginWindow.Cancel += new EventHandler(LoginWindow_Cancel);
    loginWindow.ShowDialog();
}

这个源代码用于应用程序首次打开时和发布注销事件时。注销事件如下:

public void Handle(LogoutEvent message)
{
    Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
    message.Source.TryClose();
    Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
    this.events.Publish(new LoginEvent());
}

当登录成功后,它会使用以下代码打开基于 ViewModel 的主窗口:

ContentViewModel viewModel;
viewModel = IoC.Get<ContentViewModel>();
viewModel.Username = e.Username;
this.windowManager.ShowWindow(viewModel);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接