2
votes

I know this question is somewhat basic, but at the current moment, I am completely lost as to how I should add a new ViewModel to my ViewModelLocator class in MVVM Light Toolkit.

My current implementation looks like so:

First assume that I have a Window named Settings, a ViewModel named SettingsViewModel and a ViewModelLocator ViewModelLocator.

First I call CreateSettings() in the VieModelLocator constructor:

public ViewModelLocator()
{
    if (ViewModelBase.IsInDesignModeStatic)
    {

    }
    else
    {
        CreateSettings();
    }

    CreateMain();
}

Note that this will always run as I'm not using blend and build the application each time I try to run it. Now for the `CreateSettings() method.

I had no idea what I was doing so I tried to play it safe and model everything after the methods used for creating and managing the MainViewModel.

public static void CreateSettings()
{
    if (_settings == null)
    {
        _settings = new SettingsViewModel();
    }
}

Then another few methods modeled after those used for the MainViewModel:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
    "CA1822:MarkMembersAsStatic",
    Justification = "This non-static member is needed for data binding purposes.")]
public SettingsViewModel Settings
{
    get
    {
        return SettingsStatic;
    }
}

public static SettingsViewModel SettingsStatic
{
    get
    {
        if (_settings == null)
        {
            CreateSettings();
        }

        return _settings;
    }
}

And in my Settings Window Xaml:

<Window x:Class="_5500A_Auto_Calibrator.Settings"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Settings" Height="300" Width="300"
    DataContext="{Binding Source={StaticResource Locator}, Path=Settings}">
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

The window is then opened from my MainViewModel like so:

Settings settings = new Settings();
settings.Show();

If I try this, I receive an exception:

"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '4' and line position '39'."

And an inner exception of:

"Cannot find resource named 'Locator'. Resource names are case sensitive."

I've read up on errors involving a Window's inability to find the Locator resource, but most have to do with blend.

My current take is that I'm doing something wrong, but there's so little documentation as to adding new ViewModels that I'm unsure what I'm doing wrong.

Edit:

My App.Xaml:

<Application x:Class="_5500A_Auto_Calibrator.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="clr-namespace:_5500A_Auto_Calibrator.ViewModel"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             StartupUri="MainWindow.xaml"
             mc:Ignorable="d">

    <Application.Resources>
        <!--Global View Model Locator-->
        <vm:ViewModelLocator x:Key="Locator"
                             d:IsDataSource="True" />
    </Application.Resources>

</Application>
2
Hey dante, did this answer your question? (if yes, you can mark it as answered, if not, you can elaborate some more :)Noctis

2 Answers

3
votes

This is how it usually looks:

    public class ViewModelLocator
{
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
        }
        else
        {
            SimpleIoc.Default.Register<IDataService, DataService>();    
        }

        SimpleIoc.Default.Register<MainViewModel>();
    }

    /// <summary>
    /// Gets the Main property.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
        "CA1822:MarkMembersAsStatic",
        Justification = "This non-static member is needed for data binding purposes.")]
    public MainViewModel Main
    {
        get
        {
            return ServiceLocator.Current.GetInstance<MainViewModel>();
        }
    }

    ...
}

the ViewModelLocator is static, yours doesn't seem to be. It usually sits in the ViewModel folder (assuming you installed mvvmlight with the nuget and then added a new wvvm project.

it then proceeds to have the 2 cases for design and for runtime. (if you don't use it, you can skip the if (IsInDesignMode) ... bit, and just put your logic. (though it's a shame, since it's nice to have a preview of some fake data in VS designer ...)

Adding new ViewModels usually involves creating a property of that type, and registering them with the locator, so you can then retrieve them ... but this differs and can be done differently I believe ...

Hope this help, and if there's anything else I can help with, do let me know.

0
votes

Your data context binding tries to apply earlier, than resource is declared. Try to declare binding this way (of course, this should help only if either MainSkin.xaml or application resources contain Locator resource):

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skins/MainSkin.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>
<Window.DataContext>
    <Binding Source="{StaticResource Locator}" Path="Settings"/>
</Window.DataContext>