9

I am new to both WPF and MVVM and a came across an issue when attempting to set the DataContext to the same instance of my ViewModel in two separate views.

This was because:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

would create a new instance of the view model for each view.

To get around this I decided to create a class that stored static instances of each ViewModel I used. Then in the cs file of each view I would then set the DataContext to the appropriate ViewModel from this static class.

This works but doesn't seem the best idea for larger programs where the multiple instances of the ViewModel may be needed simultaneously.

What are better approaches to this problem - Are there sound ways to have multiple Views using the same instance of a ViewModel?

Or is this approach bad practice - Should I be designing a program with one View for every ViewModel?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Alfie
  • 1,903
  • 4
  • 21
  • 32
  • I don't have any answer for you. I have never encountered a situation such as you describe where multiple views would have assigned one view model. Could you tell what you're trying to do? I am just wondering. – Ladislav Ondris Jul 26 '18 at 10:59
  • Caliburn.Micro offers multiple views for one view-model via "cal:View.Context" property. Maybe you can have a look in their source code, how this is technically achieved. – Sven Bardos Jul 26 '18 at 11:07
  • @LadislavOndris So I have created a simple tic tac toe game, the ViewModel contains an instance of the Game class - containing all the information needed for a game, and I have two separate views one for the game board (3x3 grid of buttons) and one for information of the game (whos turn it is, number of wins etc) – Alfie Jul 26 '18 at 11:12

6 Answers6

16

You can instantiate that view model in App.xaml so that it is accessible to the whole application.

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

Then in your views when you want to use that datacontext, you do the following...

DataContext="{StaticResource sharedViewModel}"
Fabulous
  • 2,393
  • 2
  • 20
  • 27
3

I had this same question and I couldn't find a good answer. After thinking about it for a while I came to the conclusion that in most cases it's best to create a one to one mapping between view model and view. So in this situation I would create two separate view models that inherit from a base view model. That way you can put whatever is common in the base view model and add any fields or methods that might be different to the more specific view model. If the view models truly are equivalent then you might want to ask yourself why you have two separate views in the first place. You may consider merging them into one view. It's possible that having two separate views is what you want, but it's just something to consider.

user2481095
  • 2,024
  • 7
  • 22
  • 32
2

Simple and easy as well as one of the recommended approach is implementing ViewModelLocator.

Idea is having defined all the ViewModels in ViewModelLocator class and access the ViewModel wherever needed. Using Same ViewModel in different View will not be a problem here.

    public class ViewModelLocator
{
         private MainWindowViewModel mainWindowViewModel;
  public MainWindowViewModel MainWindowViewModel
    {
        get
        {
            if (mainWindowViewModel == null)
                mainWindowViewModel = new MainWindowViewModel();

            return mainWindowViewModel;
        }
    }
    private DataFactoryViewModel dataFactoryViewModel;
 public DataFactoryViewModel DataFactoryViewModel
    {
        get
        {
            if (dataFactoryViewModel == null)
                dataFactoryViewModel = new DataFactoryViewModel();

            return dataFactoryViewModel;
        }
    }
}

App.xaml

    xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

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

Usage

<Window ...
  DataContext="{Binding Path=MainWindowViewModel, Source={StaticResource ViewModelLocator}}">

refer : So Question codes copied from there.. as i cannot rip the codes from my project..

WPFUser
  • 1,145
  • 7
  • 24
  • What actually are the two return possibilities of the `MainViewModel` property? – Alfie Jul 26 '18 at 12:42
  • @Alfie sorry, im not understanding your question clearly. what i was saying is instead of having the each ViewModels defined as resources in App.Xaml , you can define them in a class and use the class in bindings. it is a recommended approach also. see updated code.. – WPFUser Jul 26 '18 at 12:51
  • This answer is so underrated yet so simple and efficient, thank you – Jhonycage Mar 17 '21 at 00:50
0

I am using the prism framework and was also looking for a solution of using one viewmodel for many (child) views. In prism there are two possible solutions:

  1. Define the viewmodel as singleton (code smell) or
  2. Using the RegionContext (for full description see the prism doc)

According to the prism doc my solution looks like this.

In the prism bootstrapper:

Container.RegisterTypeForNavigation<MyView>("MyView");

In the viewmodel:

private void DisplayView(string viewName)
{
    RegionManager.Regions["ContentRegion"].Context = this;  // set viewmodel
    RegionManager.RequestNavigate("ContentRegion", viewName);
}

In the code behind of each view:

public MyView()
{
    InitializeComponent();
    ObservableObject<object> viewRegionContext = RegionContext.GetObservableContext(this);
    viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;
}

private void ViewRegionContext_OnPropertyChangedEvent(object sender, PropertyChangedEventArgs args)
{
    if (args.PropertyName == "Value")
    {
        var context = (ObservableObject<object>)sender;
        DataContext = context.Value;  // get viewmodel as DataContext
    }
}
Christian
  • 11
  • 2
0

The staticresource/singleton approach (is this what it's called? I'm bad at programming terminology) by @Fabulous is great because it's simple code that's easy to implement, but it also means that your code can execute even when you didn't explicitly want it to and that can cause some headaches.

In my case, it would execute e.g. when rebuilding (not too bad, but not great), when re-opening the solution (ugh), and when editing parts of app.xaml (which would for some reason softlock VS2019 i.e. very bad). As I understand it, people who Know What They're Doing probably already knew this would happen, but it was a pain in the ass as a novice programmer to figure out what was going on haha.

One way to keep the simplicity benefits without ruining your day with phantom-running code is to implement a simple check on whatever your main code is to see if you're running in designtime or not (basically a simplified version of @WPFUser's link).

Example:

using System.ComponentModel;
using System.Windows;

public MainCode()
{
    if (IsInDesignMode() == false)
    {
        //execute code normally
    }
    else
    {
        //do nothing (or maybe display an info message or something)
    }
}

private DependencyObject dummy = new DependencyObject();

private bool IsInDesignMode()
{
    return DesignerProperties.GetIsInDesignMode(dummy);
}

(plus still use all the stuff noted in Fabulous' answer)

I haven't tested this extensively yet so be aware that it may not be 100% foolproof, but so far for me it's worked great in the ways I've tested it.

MHLoppy
  • 1
  • 1
0

I was having the same case, just I was using the both user control one below another in stack panel. so use the common DataContext which I set in StackPanel worked for me for both User controls, as below

<StackPanel>
     <StackPanel.DataContext>
          <vm:RegisterViewModel/>
     </StackPanel.DataContext>

    <local:Register />
    <local:Users />
</StackPanel>
Satish B
  • 1
  • 3