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

时间:2012-08-15 09:10:28

标签: c# .net wpf caliburn.micro wpf-4.0

我是Caliburn.Micro的新手,我想知道在我的应用程序中处理用户登录/注销周期的最佳方法是什么。我在网上看到了一些使用空Shell-View实现这一点的建议,它在LoginView和主应用程序视图之间切换,当然每个都有一个自定义ViewModel。

我真的不喜欢这个解决方案,因为对我来说这些是两个独立的窗口,具有非常不同的属性(标题,图标,大小),它似乎是一个不干净的解决方案,两个窗口改变一个看起来像另一个窗口。另一个问题是,登录窗口来自一个我无法控制但不使用Caliburn.Micro的实用程序库,它是一个普通的旧窗口,当用户单击“登录”时,它会给我一个事件。

我还看到了在Bootstrapper启动方法中显示此对话框的建议,但我看到的问题是用户可以选择应该“注销”应该再次显示“登录”对话框的应用程序。处理Bootstrapper中的Views之间的切换似乎是错误的。

我想要的是使用某种类似于Caliburn Conductor的ApplicationViewModel或ApplicationController,但不是在Window内的Views之间切换,它应该在LoginWindow和MainWindow之间切换,并且还应该处理整个Closing应用程序(也需要注销)。在激活时,它将显示LoginWindow,处理Login事件,然后切换到主窗口(Shell)。如果用户选择“LogOut”,则该事件应再次冒泡到ApplicationViewModel / Controller,这将停用/关闭MainWindow,执行Logout,然后再次显示LoginDialog。类似的关闭事件会执行注销,但随后关闭整个应用程序。

所以我的问题是:

  1. 你对这个解决方案有什么看法?你有另一个/更好的解决方案吗?
  2. 我该如何实现? ; - )
  3. 非常感谢!

2 个答案:

答案 0 :(得分:16)

我认为解决问题的方法相当简单。

简而言之,您正在创建一个ViewModel作为Shell,在应用程序启动时用登录窗口表示。如果用户成功登录,则关闭该窗口,并在内容窗口中显示相同的viewModel实例。如果用户正在注销,则会再次显示“登录窗口”。

首先创建一个接口IShell,公开两个代理LoginSuccessfulLogout

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或适合您的任何控件的操作。

下一步是覆盖Bootstrapper中的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方法,则通过调用TryClose上的ShellViewModel来关闭当前窗口。同时,您可以通过将Application.ShutdownMode设置为OnExplicitShutdown来阻止关闭应用程序。然后使用windowmanager,通过将“Login”作为Context信息传递给windowManager,在Login Mode中创建另一个窗口。这实际上是相同的ViewModel,但具有不同的可视化表示。

对于Logout,你正在做同样的事情。

要使用Caliburn约定来实现此功能,您需要一个特殊的项目结构,如此处所示(并解释there): enter image description here

现在我挑战你采取这个代码并创建一个小样本应用程序。创建一个Login视图(使用Button或其他任何方式登录),并使用LoginSuccessful / Logout方法创建一个带有Logout按钮的Content视图。

这将使用最少的代码和类来解决您的问题。希望这对你有所帮助。

答案 1 :(得分:3)

我已经开始创造一些基本上可行的东西,但可能需要更多的工作才能真正有用。完整的评论和来源可以在我的网站Caliburn.Micro Login Window sample上找到。

我使用Caliburn.Micro的IEventAggregator来控制两个窗口之间的过渡。您可以使用此代码打开登录屏幕:

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();
}

第一次打开应用程序和发布Logout事件时,会使用相同的源代码。 Logout事件如下所示:

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);