1

How would you allow multiple viewmodels to share the same model?

I'm creating the viewmodels in an ApplicationViewModel that is used for switching between views using a DataTemplate with the selected VM.

public ApplicationViewModel()
{
    //Add pages
    BasePageViewModels.Add("Home Page", new HomeViewModel());
    BasePageViewModels.Add("Summary Page", new SummaryViewModel());
    BasePageViewModels.Add("AddTestRun Page", new AddTestRunViewModel());
    //some code here
    CurrentBasePageViewModel = BasePageViewModels["Home Page"];
}

I want to be able to access the same Data class from within each of the created VM's. Ideally I'd pass in the Data class to each ViewModel with a parameter but that then causes setting DataContex within XAML to throw an error because the DataContext has no accessible constructors.

Update

I'm setting the DataContext in the other Views like so:

<UserControl.DataContext>
    <viewModels:SummaryViewModel/>
</UserControl.DataContext>

but doing that creates a new instance of the ViewModel, rather than using the one bound to CurrentBasePageViewModel.

LWood
  • 417
  • 4
  • 16
  • did you think about having ContentControl? – Sasha Oct 07 '15 at 09:01
  • That's how I'm changing the view. The window has a side menu and a ContentControl whose content is bound to the selected VM. – LWood Oct 07 '15 at 09:08
  • Sorry, I did not read carefully post. You can try following - or pass information via parameters, or write a static class or implement interface – Sasha Oct 07 '15 at 09:19
  • or you can set parameters from application viewmodel to basepageviewmodel via properties before loading to currentBasePageViewModel – Sasha Oct 07 '15 at 09:20
  • Then how would I set the DataContext? Setting it from within the XAML of a view creates a new instance of the VM. – LWood Oct 07 '15 at 09:29
  • do you mean datacontext of HomeViewModel, SummaryViewModel? – Sasha Oct 07 '15 at 09:40
  • Yes. I could use a static Data class but that makes unit testing harder. – LWood Oct 07 '15 at 09:46
  • I'm not sure I see the issue? Just pass your model in the VM constructors. There is no need to set the DataContext manually if you are using ContentControls. – GazTheDestroyer Oct 07 '15 at 09:53
  • pass it in constructor in your applicationviewmodel constructor, but create an empty constructor for each type as well, so xaml is happy? if you're concerned about this being dirty - it isnt. just do it. – Dbl Oct 07 '15 at 09:55
  • I don't know if it is suitable for your case, but you may use ViewModel Locator. It allows you to pass constructor parameters to viewmodels. See [this](http://stackoverflow.com/questions/5462040/what-is-a-viewmodellocator-and-what-are-its-pros-cons-compared-to-datatemplates). – Yusuf Tarık Günaydın Oct 07 '15 at 10:07

3 Answers3

1

Definitelly, the solution is to pass model to viemodel's constructor.

Now, how to solve your problem with xaml?

First of all, from your question and posted code it is not clear, what's the problem. (the xaml code is missing).

I just guess, the problem is causing design time datacontext, since it requires parameterless constructor. There are two solutions:

  1. Add parameterless constructor:

    public class MyViewModel {
       public MyViewModel(){
           //design time ctor. Create design time data here
       }
    
       public MyViewModel(MyModel model){...}
    }
    
  2. Create new class for design time datacontext:

    public class MyViewModelDesignTime : MyViewModel {
       public MyViewModelDesignTime() : base(new MyModel()){
           //design time ctor. Create design time data here
       }
    }
    

    and use this class in xaml:

     d:DataContext="{d:DesignInstance l:MyViewModelDesignTime, IsDesignTimeCreatable=True}"
    
Liero
  • 25,216
  • 29
  • 151
  • 297
  • I've updated the question. Creating a parameterless constructor causes the View to create a new VM using that, which leaves the model null. – LWood Oct 07 '15 at 10:25
  • You cant create viewmodel in xaml if you have created it in applicationviewmodel, because you will have two distinct instances of the same class. ContentPresenter sets the datacontext automatically. Try to google for xaml implicit DataTemplates – Liero Oct 07 '15 at 10:35
  • Knew I was missing something obvious. That seems to be it, thanks. – LWood Oct 07 '15 at 10:51
0

try something like this:

HomeViewModel _homeViewModel;
SummaryViewModel _summaryViewModel;
public ApplicationViewModel()
{
    //Add pages
    _homeViewModel = new HomeViewModel();
    _summaryViewModel = new SummaryViewModel();

    //some code here
    _homeViewModel.SomeProperty = 5;
    CurrentBasePageViewModel = _homeViewModel;
}

Then some int property of your view homeViewModel will have value 5. Also you can create property in homeViewModel that will hold reference to current ApplicationViewModel or create interface. Example of the interface:

public interface IName
{       
    /// <summary>
    /// Property name.
    /// </summary>
    string PropertyName { get; }
}

Then make ApplicationViewModel implement this interface and pass it to viewmodel. In viewModel create property. Something like:

public class HomeViewModel
{
     IName _iName;
     public HomeViewModel(IName name)
     {
          _name = name;
     }
}

And your ApplicationViewModel:

public class ApplicationViewModel : IName
{
    HomeViewModel _homeViewModel;
    SummaryViewModel _summaryViewModel;
    public ApplicationViewModel()
    { 
        //Add pages
        _homeViewModel = new HomeViewModel(this);
        _summaryViewModel = new SummaryViewModel();

        //some code here
        _homeViewModel.SomeProperty = 5;
        CurrentBasePageViewModel = _homeViewModel;
    }
}
Sasha
  • 833
  • 1
  • 20
  • 40
0
public ApplicationViewModel()
{
    //shared model
    var model = new MyModel();

    //Add pages
    BasePageViewModels.Add("Home Page", new HomeViewModel(model));
    BasePageViewModels.Add("Summary Page", new SummaryViewModel(model));
    BasePageViewModels.Add("AddTestRun Page", new AddTestRunViewModel(model));

    //some code here
    CurrentBasePageViewModel = BasePageViewModels["Home Page"];
}



<UserControl x:Class="ApplicationView"
             xmlns:vm="clr-namespace:MyApp.ViewModels"
             xmlns:vw="clr-namespace:MyApp.Views">

    <UserControl.Resources>

        <DataTemplate DataType="{x:Type vm:HomeViewModel}">
            <vw:HomeView />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:SummaryViewModel}">
            <vw:SummaryView />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:AddTestRunViewModel}">
            <vw:AddTestRunView />
        </DataTemplate>

    </UserControl.Resources>

    <ContentControl Content={Binding CurrentBasePageViewModel} />

</UserControl>
GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103