tags:

views:

265

answers:

2

Using the MVVM pattern creating WPF applications you have the ViewModel providing data to the View. I've met a situation where I find it reasonable to create WPF objects in my ViewModel, and the View fetches these and shows them. More specifically I have functionality for drawing where I need to store an InkPresenter in the end. I receive the mouse gestures in the code-behind of the view, but pass the events to the ViewModel. The ViewModel handles the mouse event and creates drawing objects that is put in an ObservableCollection such that the View can fetch them out and display them.

The question is; is this okay, or is it bad practice to create WPF objects in the ViewModel classes? And why? If it is okay; are there any best practices or recommendations to dealing with these objects?

+2  A: 

No, it's "not okay" (quotations as I am talking about best practices - saying a ViewModel is no ViewModel because it does so is as wrong) to create and reference WPF (Control-)classes inside your ViewModels.

A very big disadvantages comes in when you try to unittest your ViewModel: The controls need special treatments and represent not-wanted dependencies.

You should only pass the actual "data" of those objects to the ViewModel: For the example with the InkPresenter it would be O.k. in my opinion to send the ViewModel the StylusPoints but not the InkPresenter itself. Even better would be ViewModel classes representing the StylusPoints - they could be mapped to the actual StylusPoints by databinding (I don't know whether this is possible with the InkPresenter) or via some mediators (e.g. using attached properties).

For creation you can use some mediator, too, which you pass e.g. ViewModels and which then creates the suitable WPF-Control and sets the VM as its DataContext.

winSharp93
Thanks for your opinion. I do agree with you. I'm trying to avoid WPF controls in the ViewModels, but sometimes it's just a lot easier to do it that way.
stiank81
Would you say having the ViewModel provide...say a GradientBrush also breaking the "rules"?
Kilhoffer
Personally I'd say it's different. A brush can be seen as a data object, and might be useful and meaningful to provide from the ViewModel to the View. What do you think?
stiank81
Hmm - well - that's something in between.I would try to avoid it where possible and prefer using DataTemplates whose triggers may set colors / brushes based on certain properties. Otherwise there are still ValueConverters.
winSharp93
+1  A: 

I ran into an interesting case for this just today. I'm building a UI that includes a collection of FlowDocuments. These items are presented in a ListView, one of whose cell templates is a RichTextBox.

The first problem I ran into is that the Document property of the RTB isn't a dependency property, so you can't bind to it. Fortunately, some intrepid developer out there ran into this problem before I did, and implemented a RichTextBox subclass with Document overriden as a dependency property.

All was well and good until I implemented drag/drop reordering of the collection. Here I discovered one of the reasons why Document isn't a DP. Changing the order of the collection doesn't actually change the order of the objects they're bound to; the items that moved just refresh their binding targets. And glory be, if a FlowDocument belongs to one RichTextBox, you can't assign it to another. Reordering these FlowDocuments breaks the UI, which is a problem because the whole reason I'm doing this UI in the first place is for reordering these items.

The hammer that I decided to hit this with was to make the RichTextBox a property of my view model. That way, when I move the FlowDocuments around in the collection, the RTBs that own them move with them.

Personally, I don't give a fig about whether or not this class is unit-testable. That's not what makes this the wrong answer.

What makes this the wrong answer is that now that I've put this component into my class, it's not part of the event routing system anymore. If I change the FontFamily on my application-wide FlowDocument style, these controls never hear about it. I also can't bind anything to them. (Not easily, at least.) There are probably other problems I haven't thought through yet.

I'm not quite sure what the right answer in my case is going to be yet. I think that I may have to fix my bindable RichTextBox so that it maintains two FlowDocument objects: the one exposed to the world by its Document getter and setter, and the one inside the control itself. (That is, when something sets the Document property on the object, it saves that value in its private field, and then copies the document's content into the content of the base Document property.)

That's a very long-winded way of saying, "creating WPF objects in the view model seems like a pretty bad idea now that I'm doing it."

Robert Rossney
Indeed seems to be a bad idea to hold WPF objects in the ViewModel.. In my case I might end up making it work - for now. But I can imagine that I can easily run into problems if I need to change something related to this later on.
stiank81