I have an application which has multiple screens and talks to a WCF web service. I am using SimpleInjector for my favourite IoC container, and Caliburn Micro for MMVM with WPF.

I have a separate class implementation of ITransportClient that controls the connection to the web service and sends and receives messages.

The application has the following states:

  1. Start up and initialisation of the application.
  2. Display the login form.
  3. Login form performs connection to chat server with ITransportClient.Login(username, password).
  4. If login successful show a new MainWindow view, if failed prompt user for credentials again.
  5. When logged in, MainWindow controls messages sent and received to the WCF service.
  6. When connection is lost with the server (notified by ITransportClientCallback.OnTransportDisconnected), or when the user clicks logout, the Login form is displayed again.

With the technology I have available and this being my first MVVM project I have the following architectural and design questions:

  1. What design pattern should I use to control showing a separate log in screen, then the main screen when connected, and revert back to the login screen when the connection has been lost?
  2. Who should control the connection state of the chat application and where would this code sit? Would this state sit in the bootstrapper class?
  3. Could I have the ITransportClient registered as a persistence instance scope, and have multiple forms control when required by injecting in to the ViewModel that required it (i.e. both the login form and the main window form)?

The ITransportClient interface is below:

public interface ITransportClientCallback
{
    string OnReceivedMessage();
    void OnTransportDisconnected();
}

// transport interface that client calls
public interface ITransportClient
{
    bool SendLogin(string username, string password);
    void SendLogout();
    void SendMessage();
    void RegisterCallback(ITransportClientCallback applicationHostCallback);
}

Along with the Caliburn.Micro bootstrapper code using SimpleInjector:

public class SimpleInjectorBootstrapper : Caliburn.Micro.Bootstrapper
{
    private Container container;

    protected override void Configure()
    {
        this.container = new Container();
        this.container.Register<IWindowManager, WindowManager>();
        this.container.Register<IEventAggregator, EventAggregator>();
        this.container.Register<IAppViewModel, AppViewModel>();
        this.container.RegisterSingle<ITransportClient, Transport.WCF.TransportClient>();
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        return this.container.GetInstance(serviceType);
    }

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
    {
        base.OnStartup(sender, e);
        var appViewModel = this.container.GetInstance<IAppViewModel>();
        var windowManager = this.container.GetInstance<IWindowManager>();
        windowManager.ShowWindow(appViewModel);
    }
}
有帮助吗?

解决方案

What design pattern should I use to control showing a separate log in screen, then the main screen when connected, and revert back to the login screen when the connection has been lost?

In alot of cases, MVVM project is accompanied with a "mediator" or "messenger". All View Models can interact between each other through this mechanism by subscribing and publishing messages. All VMs would subscribe to "UserLoggedOutMessage" and do the necessary response (i.e. change its visual state to readonly, or not show anything at all). A separate bootstrapper can also "redirect" the user to the login screen.

Who should control the connection state of the chat application and where would this code sit? Would this state sit in the bootstrapper class?

One approach is to have an abstraction for the state of the app - this is equivalent to the HTTPContext.Current variable in asp.net. You could have a Session object which has a CurrentState read-only property with corresponding Login(), Logout(), SendMessage() methods which acts as a state machine. All View Model would then subscribe to the events as soon as the state of the app changes - again through the Mediator. The Session object can be a static variable on the bootstrapper, or a singleton injected to all VMs via the IoC.

Could I have the ITransportClient registered as a persistence instance scope, and have multiple forms control when required by injecting in to the ViewModel that required it (i.e. both the login form and the main window form)?

that's definitely the idea. in most MVVM project, you would want to have a single instance of the dependencies injected to the VMs. This allow for more efficient memory use and also allow a clean object oriented model (as opposed to a procedural immutable transaction scripts).

Having said that, if this was an attempt to check for the "application state" I would change the level of the abstraction to something higher e.g. IApplicationStateMachine or IUserSession. This way if you want to ever support multiple user in one application instance, you could in theory have multiple instances of the IUserSession objects.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top