wpfdatacontextdesigner

WPF - d.DataContext - how to initiate data in design like at run-time?


I need my SharedViewModel to be set in design time to display data in my UI

as I run the app, I see data on the screen, because OnLoaded runs LoadStuffs

LoadStuffs initiates the SharedViewModel and creates data

but at design time after a rebuild, nothing show up in the designer

<Window x:Class="MyApp.MainWindow"
        mc:Ignorable="d"
        xmlns:local="clr-namespace:MyApp"
        ...
        Loaded="OnLoaded"
        d:DataContext="{Binding RelativeSource={RelativeSource Self}, Path=SharedViewModel}"
    >

At runtime, these get executed

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        LoadStuffs();
    }

    private void LoadStuffs()
    {
        Logger.Log("LoadStuffs() executed!");
        ...
        SharedViewModel = ...
    }

The message "LoadStuffs() executed!" only appears at runtime, never in design

The property of the main class :

private SharedViewModel m_sharedViewModel;
public SharedViewModel SharedViewModel
{
    set
    {
        m_sharedViewModel= value;
    }
    get
    {
        Logger.Log("SharedViewModel getter"); <<<<< this is never called as the log.txt is only created at runtime
        if (m_sharedViewModel == null)
        {
            Logger.Log("Initializing DesignSharedViewModel");
            LoadStuffs();  // Initializes m_sharedViewModel
        }
        return m_sharedViewModel;
    }
}

Thanks for your help on this


Solution

  • The cleanest way of allocating a ViewModel to a View at both design-time and run-time is using the ViewModelLocator pattern.

    1 - Add a class to handle creation of ViewModels.

    public class ViewModelLocator
    {
        public ViewModelLocator()
        {
            // include any IOC setup here if required.
        }
    
        public MainViewModel MainViewModel
        {
            get 
            {
                return new MainViewModel();
    
                // replace this with a call to the IOC container if any injected servies are required.
            }
       } 
    
       // repeat for any other ViewModel types required.
    }
    

    2 - Create an instance of this locator class in App.xaml, making it a application wide resouce.

    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator">
    </Application.Resources>
    

    3 - in each View, reference this locator instance as a source for its DataContext

    <Window
        x:Class="MyApp.MainView"
        ...
        DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=MainViewModel}" />
    

    For a more detailed example including how to have different injected services for design-time and run-time usage, check out my blog post.