Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I handle Login/Logout in Caliburn.Micro?

I'm new to Caliburn.Micro and I'm wondering what is the best way to handle user Login/Logout cycles in my application. I saw some suggestions online to implement this using an empty Shell-View which switches between the LoginView and the main application view, each with a custom ViewModel of course.

I don't really like this solution, because for me these are 2 separate windows with very different properties (Title, Icon, Size) and it seems an unclean solution two change one window to look like the other. Another problem is, that the Login Window comes from an utility library which I don't control and which doesn't use Caliburn.Micro, it's a plain old Window which gives me an event when the user clicks "Login".

I also saw suggestions to display this Dialog in the Bootstrapper startup method, but the problem I see with that is that the user can choose to "Logout" of the application which should display the Login dialog again. It seems wrong to me to handle the switching between the Views in the Bootstrapper.

What I would like is to have some sort of ApplicationViewModel or ApplicationController which works like a Caliburn Conductor, but instead of switching between Views inside a Window, it should switch between the LoginWindow and the MainWindow and should also handle Closing of the whole application (which also requires a Logout). On Activation it would show the LoginWindow, handle the Login event and then switch to the Main Window (Shell). If the user chooses to "LogOut", the event should bubble up to the ApplicationViewModel/Controller again which would deactivate/close the MainWindow, perform the Logout and then show the LoginDialog again. Similar a Close event would do the Logout, but then Shutdown the whole application.

So my questions are:

  1. What do you think about this solution and do you have another/better one?
  2. How do I implement this? ;-)

Thanks a lot!

like image 371
aKzenT Avatar asked Aug 15 '12 09:08

aKzenT


2 Answers

I think the solution to your problem is fairly easy.

In a nutshell you are creating one ViewModel as Shell which is represented with a Login Window when the application starts. If the user logs in successfully this window closes and the same instance of the viewModel is displayed in a Content Window. If the user is doing a logout, the Login Window is shown again.

First of all create an interface IShell which exposes two delegates LoginSuccessful and Logout

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

Next create a class ShellViewModel which implements 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();
        }
    }

The methods DoLogin and DoLogout are Actions which can be bound to a Button or whatever control appropriate for you.

Next step is to override the OnStartupMethod in your Bootstrapper. This premises that you have an instance of the WindowManager and ShellViewModel exported by an IoC Framework of your choice.

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

The trick to this is: If the DoLogout method is called, the current window gets closed by calling TryClose on the ShellViewModel. At the same time you prevent the application from being shutdown by setting the Application.ShutdownMode to OnExplicitShutdown. Then using the windowmanager, you create another window in Login Mode by passing "Login" as Context information to the windowManager. This is actually the same ViewModel, however, with a different visual representation.

For Logout you are doing the same thing just around.

To get this working using Caliburn Conventions, you need a special project structure as seen here (and explained there): enter image description here

Now I challenge you to take this code and create a little sample application. Create a Login View (which does Login with a Button or whatever) and create a Content View with a Logout Button using the LoginSuccessful/ Logout Methods.

This will solve your issue with a minimum of Code and classes. Hope this will be helpful to you.

like image 177
Oliver Vogel Avatar answered Oct 05 '22 23:10

Oliver Vogel


I've had a go at creating something that basically works but probably needs a bit more work to be really usable. The fully comments and source can be found on this post Caliburn.Micro Login Window sample on my website.

I used the IEventAggregator of Caliburn.Micro to control the transition between the two windows. You get this code to open the login screen:

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

this same source is used for both the first time the app opens and when the Logout event is published. the Logout event looks like this:

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

When a login is successful it uses this code to open the main window which is based on a ViewModel:

ContentViewModel viewModel;
viewModel = IoC.Get<ContentViewModel>();
viewModel.Username = e.Username;
this.windowManager.ShowWindow(viewModel);
like image 43
Kioshiki Avatar answered Oct 05 '22 23:10

Kioshiki