Is it possible to make an instance of a non-static class available to an entire WPF application?

StackOverflow https://stackoverflow.com/questions/21945898

Question

I have a WPF application that that consists of a MainWindow, along with a variety of Page objects that can accessed through the MainWindow. I currently have a User class being instantiated in the ViewModel of the MainWindow which defines permissions properties which are bound to various properties in the corresponding View:

public NavigationViewModel()
{
    _currentUser = new User(_currentServerConnection, Environment.UserName);

Now, I would like to access this same User instance in each of the Page object's ViewModels. What is the most appropriate way of doing this from a design standpoint? I have read multiple threads on StackOverflow, and have come to the conclusion that I should either make this class static (which doesn't make sense here, this class has a state), or instantiate it in each ViewModel. Are there any other options I could be missing? Oh, I read about Singleton classes as well, and that is obviously advised against. I feel like I am missing some fundamental concept here.

Was it helpful?

Solution

Singleton's are not advised against. Singletons used in the wrong place or the wrong way are advised against.

Your entire question is screaming as the "correct" and "proper" example of when it is good to use a singleton.

If you diagram your design and a large number of your objects in your code point to the exact same instance of another object, and no second instance of that object is ever needed, then you have found the correct use for a singleton. From what you have described, you are in this situation.

Now, you could also use a static. The only difference between a static and a singleton is that a singleton isn't instantiated until first use. Since you probably have a login after load, you might as well wait until the first login attempt occurs to instantiate your singleton. So that is why you would use a singleton over a static.

public class GlobalSettings : IGlobalSettings
{
    public static GlobalSettings Instance
    {
        get { return _Instance ?? (_Instance = new GlobalSettings()); }
    } static GlobalSettings _Instance;

    public GlobalSettings() 
    {
    }

    // More methods/properties here
}

Singletons can also work just fine in multithreaded applications with lock. See this article: http://msdn.microsoft.com/en-us/library/ff650316.aspx

Singletons are also very easy to Unit Test or use in IOC. Anyone who says otherwise just hasn't realized that you can simply use an interface to describe the singleton and then all your objects that use the singleton can have an instance of that Interface. I use lazy property injection as shown below unless an IOC libray is already in place. I wouldn't include the bloat of an IOC library just for this.

    public static IGlobalSettings Settings
    {
        get { return _Settings ?? (_Settings = GlobalSettings.Instance); }
    } static IGlobalSettings _Settings;

Or you can also have your IOC Controller/factory just return your Singleton instance every time as your interface.

OTHER TIPS

The simplest way to have any shared data in all of your view models is to have those properties declared in a common base class. If all of your view models extend this base class, then its properties will be available to them all.

If you wanted a bit more depth, you could have a class that implements the Singleton Pattern to ensure that there is only ever one instance. I have a few large WPF Applications that have base view models that provide access to a number of ...Manager classes that provide various services to all of the extending view models.

One of those is the StateManager class, which implements the Singleton Pattern and basically holds all of the properties that are common throughout the application. This is the property from the base class that exposes it:

public StateManager StateManager
{
    get { return StateManager.Instance; }
}

And this is how I use it in the UI:

<RadioButton IsChecked="{Binding StateManager.SomeValue}" Content="all" />

Another approach is to use something that manages creation and lifetime of objects, such as inversion of control container.

If you decide to explore this approach, all you need to do is:

  1. Have single entry point in your application (composition root) where you tell container what objects and how will be available (in WPF, this would be App)
  2. Design your classes so that they can utilize dependency injection (i.e. your view models will not create user instance, but instead expect that it will be provided via constructor injection)
  3. Stop worrying about objects lifetime from view model code (it should not be within view model responsibilities-scope)

Example with Autofac:

// App.xaml.cs
var builder = new ContainerBuilder();
builder.RegisterType<User>()
    .WithParameter("userName", Environment.UserName)
    // tell container only one instance of this object should be ever created
    .SingleInstance();


// ViewModel.cs
public ViewModel(User user)
{
    // we don't care at this point whether user is single instance or not;
    // it's container's responsibility to handle it for us
    this.user = user;
}

Injecting current server connection to user might be problematic (as it won't be available at composition root setup), but simply using factory method pattern should be enough to overcome this issue.

It depends a bit on the relationship between User and the various view models.

One way is if there is one logical User instance and all the view models are reflecting the state of this single user. In that case I think the most logical path is to have a single User instance that is passed between all of the view models.

The singleton model could also work well here assuming your code is single threaded. However I'm generally against using this pattern on mutable objects. Today your app is single threaded, tomorrow it might not. It's so easy to forget you made this critical assumption a week or year from now and end up putting yourself in a very bad place

On the other hand if there is a different logical User instance for each view model then just create a new one every time.

Using static class and static fields is one way of doing it.

Another way is to pass the user class object as a parameter to the constructor of the view models of your pages.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top