10

I'm using the MVVM pattern in our WPF application to allow for comprehensive unit testing. The MVVM pattern itself is working great, however I'm struggling to adapt the pattern in a way that means I can use the design-time data support of WPF.

As I'm using Prism the ViewModel instances are generally injected into the constructor of the view, like so

public MyView(MyViewModel viewModel)
{
    DataContext = viewModel;
}

Dependencies for the ViewModel are then injected into the constructor, like so

public class MyViewModel
{
    public MyViewModel(IFoo foo, IBar bar)
    {
        // ...
    }

    // Gets and sets the model represented in the view
    public MyModel { get; set; }

    // Read-only properties that the view data binds to
    public ICollectionView Rows { get; }
    public string Title { get; }

    // Read-write properties are databound to the UI and are used to control logic
    public string Filter { get; set; }
}

This is generally working really well except when it comes to design data - I wanted to avoid compiling design-data specific classes into my released assembly and so I opted to use the {d:DesignData} approach instead of the {d:DesignInstance} approach, however in order for this to work correctly my ViewModel now needs to have a parameterless constructor. In addition, I also often need to change additional properties either to have setters or to be modifiable collections in order to be able to set these properties in XAML.

public class MyViewModel
{
    public MyViewModel()
    {
    }

    public MyViewModel(IFoo foo, IBar bar)
    {
        // ...
    }

    // Gets and sets the model represented in the view
    public MyModel { get; set; }

    // My read-only properties are no longer read-only
    public ObservableCollection<Something> Rows { get; }
    public string Title { get; set; }

    public string Filter { get; set; }
}

This is worrying me:

  • I have a parameterless constructor that is never intended to be called and isn't unit tested
  • There are setters for properties that only the ViewModel itself should be calling
  • My ViewModel is now a jumbled mixture of properties that should be modified by the view, and those that shouldn't - this makes it tricky to tell at a glance which piece of code is responsible for maintaining any given property
  • Setting certain properties at design time (e.g. to see styling on the Filter text) can actually end up invoking ViewModel logic! (so my ViewModel also needs to be tollerant of otherwise mandatory dependencies being missing at design time)

Is there a better way to get design-time data in a WPF MVVM application in a way that doesn't compromise my ViewModel in this way?

Alternatively should I be building my ViewModel differently so that it has more simple properties with the logic separated out somewhere else.

Justin
  • 84,773
  • 49
  • 224
  • 367

2 Answers2

2

First, I would recommend you to have a look at this video where Brian Lagunas provides several best practices about MVVM. Brian is - at least - involved in the development of Prism, as his name appears in the nuget packages information. Didn't check further.

On my side I only use bits of Prism, and my Model and ViewModel always offer blank constructors (like what Brian shows), the data context is assigned in the view's XAML, and I set the properties values like :

<MyView.DataContext>
  <MyViewModel />
</MyView.DataContext>

and

public void BringSomethingNew()
{      
  var myView = new View();
  (myView.DataContext as ViewModel).Model = myModel;

  UseMyView();
}

Another benefit with this approach is that the ViewModel is created once, with the same path at design and run time, so you create less objects and save GC efforts. I find this elegant.

With regards to the setters, the design data will still work if you make them private, like:

public string MyProp { get; private set; }

Ok, customize it to manage NotifyPropertyChange at your convenience, but you've got the idea.

Now, I don't have yet a solution to manage ObesrvableCollections (I face the same problem, although putting multiple values in XAML sometimes work... ???), and yes, I agree that you have to manage the case when the properties are not set, like setting default values in the constructor.

I hope this helps.

-1

I too have worked with NUnit testing with WPF and MVVM implementation. However, my version is reversed from yours. You are creating the view first, then creating the model to control it.

In my version, I create the MVVM model FIRST and can unit test it till the cows come home and not worry about any visual design... if the model is broken, so too will the visual implementation.

in my MVVM model, I have a method to "GetTheViewWindow". So, when I derive from my MVVM baseline, each view model has its own view its responsible for. So via a virtual method, each instance will do its own new view window when being applied for production.

public class MyMVVMBase
{
   private MyViewBaseline currentView;

   public MyMVVMBase()
   { // no parameters required }

   public virtual void GetTheViewWindow()
   { throw new exception( "You need to define the window to get"; ) }
}

public class MyXYZInstanceModel : MyMVVMBase
{
   public override void GetTheViewWindow()
   {
      currentView = new YourActualViewWindow();
   }
}

Hope this helps as an alternative to what you are running into.

DRapp
  • 47,638
  • 12
  • 72
  • 142
  • 2
    It's a little weird to me that your VMs then have a dependency on your views. – Greg D Feb 09 '13 at 21:58
  • @GregD, not to me. I should be able to have this model that I can query data, set flags on what may be presented to any external "view" via exposing getters/setters. I'm just saying that in this example, it doesnt REQUIRE the view, but if I want to launch one, each view model has its own purpose, such as a maintenance screen, transaction header/detail processing, whatever. If there's a corresponding view, I just have the hook to say ... go call the view associated with this mvvm handler. – DRapp Feb 10 '13 at 01:25
  • @Justin, can you clarify to me what you mean/intend about "design-time" data. My model manager creates necessary connections to database and has corresponding methods to "get" data and make available into lists, data tables, getters/setters as needed. So, any "VIEW" can bind to the entire model manager. the model manager also has "ICommand" handlers for things such as add, save, cancel to push data changes BACK to the server. – DRapp Feb 10 '13 at 19:11
  • Like [this](http://blogs.msdn.com/b/mcsuksoldev/archive/2010/08/27/designdata-mvvm-support-in-blend-vs2010-and-wpf-silverlight.aspx) - the ability to provide dummy data at design-time so that you an see in VS / Blend what the styling is going to look like when its populated with data. – Justin Feb 11 '13 at 08:43
  • @Justin, well put, and I respect your need for pre-run-time data to simulate the interface from a visual designer perspective. – DRapp Feb 11 '13 at 12:21
  • @DRapp Its very definitely in the "Nice to have" category, but it is so very nice to have :) – Justin Feb 11 '13 at 12:46
  • 3
    1. This is not MVVM! Your view model should never have knowledge of the View. 2. I don't see anywhere how this solves the OP's problem of using design data. – Quark Soup Mar 08 '14 at 16:22