Search code examples
c#wpfmvvm-light

Want a way to write a class with a property that will return a new instance of a ViewModel


We've got a WPF app with a landing page that lists about a dozen or so buttons, all going to new views/viewmodels of that type. Its becoming unwieldy. We've got one viewmodel that lists all of these which basically look like this:

    private void ExecuteViewProgramCommand()
    {
        OpenViewMessage message = new OpenViewMessage();
        CurrentViewModel = message.ViewModel = ViewModelLocator.ProgramVM;
        Messenger.Default.Send<OpenViewMessage>(message);
    }

I've never liked how this was done, as it violates the DRY principle. The only thing that changes in the the above code in the second line, where in this code what changes is ViewModelLocator.ProgramVM. I've been tasked with redoing the landing page, making it more organized and we're going to be adding more launching buttons. I think it would be better to use dependency injection. Also I'm trying to address the need to redesign the display, so that its in a list, rather than buttons scattered about, and in alphabetical order.

First I came up with this class:

public class Tile
{
    public string ModuleName { get; set; }
    public NamedViewModelBase ModuleViewModel { get; set; }
}

(NamedViewModelBase is the name of the viewmodel that's common to all of the viewmodels.) Then I declared a unit test to test this and declared this within the unit test:

List<Tile> tiles = new List<Tile>()
{
    new Tile()
    {
        ModuleName = "Program",
        ModuleViewModel = ViewModelLocator.ProgramVM
    },
    new Tile()
    {
        ModuleName = "Organization",
        ModuleViewModel = ViewModelLocator.OrganizationVM
    }
}

But this quickly became apparent that this was wrong. The assigning in the setter of ViewModelLocator.ProgramVM would instantiate the viewmodel for Program. I don't want that, I'd rather have the calling of instantiating it, such as we have in the ViewModelLocator:

static public ProgramViewModel ProgramVM
{
    get
    {
        if (ServiceLocator.IsLocationProviderSet)
        {
            SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
            return ioc.GetInstanceWithoutCaching<ProgramViewModel>(Guid.NewGuid().ToString());
        }
        else
        {
            return null;
        }
    }
}

So, I'm thinking that I've got to change the Tile class to declare the ModuleViewModel property to something like this: public NamedViewModelBase ModuleViewModel { get; }. But I don't know how I'd instantiate it when defining a List. What is the correct way to resolve this?


Solution

  • This is going to be psuedo codish advice which is kind of on the same track where you already are:

    Assuming BaseViewModel is the base class for all your individual VM's

    1. Create a Dictionary<string, BaseViewModel>
    2. Fill this dictionary up during Application Start time (would look like your tiles List)

      public void PreCreateVMs() { dictionary[Key] = new ConcreteViewModelType(); // Keep adding New Vms here }

    3. In the xaml, bind all your buttons to same Command which takes a string argument (or improvise this with Enum). Pass the correct String Key for each button. Like: Accounts Button click should launch AccountVM which is stored with "AccountVM" key in the dictionary.

    4. In the Command Handler - use the string, lookup the Dictionary find the correct ViewModel and Assign this object to CurrentViewModel

    From maintenance point of view - all you need to add a new ViewModel is to update xaml with a new button, assign correct command parameter string. Use this string key and add the correct VM in the PreCreateVMs method.