There is something I had to learn while designing medical imaging / data visualization software in WPF/MVVM:
If your domain model deals with visualization, then your ViewModel has to contain visual information.
Let's take an example of an accounting system. There you can get every application functionality purely in the model, but it would be weird for an human to deal with all the accounting concepts without visualizing them.
Enter the View, purely as an aid (a true Graphical User Interface in a Human-Computer-Interaction sense), so the human operator can see the state of the underlying information, which has not a visual by itself.
On the other hand, let's take your extreme example: an application whose very purpose is to manipulate images. That is, the core Domain Model class is the Composite Image, with its layers, and each one with its pixel effects, and masks, and so on.
One core concept of MVVM, Data Binding totally loses its sense here: how are you suppose to have a property in the ViewModel, which is bound to a property in the View, if the very thing you are representing is already composed of pixels? At least, I suppose, your code-behind would need to be very rich (instead of non-existant in purist by-the-book MVVM).
What I have been doing lately (the current application I am developing has a custom realtime plotter) is more or less like this:
My Model layer is a Domain Model, containing only the conceptual classes related to the broad problem, and the rules by which the objects cooperate with one another. Nothing application-related (that is, which might vary between different applications working on the same domain) lives in this layer, which by the way does not contain WPF infrastructure. Good class candidates would be
The ViewModel layer would contain application logic, that is, how the current application chooses to manipulate the domain model objects. Here we start to use WPF related classes. For example, I most probably would represent a
CompositeCanvas with a
WriteableBitmap, since it can be directly used as the
Source property of an
Image control via classic Data Binding.
The View layer would be the dumbest as possible. Apart from the skinned input controls (toolbar buttons, custom mouse cursors and manipulator handlers), the actual CompositeImage would be composed of one or more WriteableBitmaps stacked into a Canvas or Grid or ViewBox (or a combination of them).
One important thing to consider is how the layers interact to each other:
- If you have independent layers which don't interact with one another - that is, an application less sophisticated than Photoshop - then you can composite in the View, stacking multiple
Image controls over one another inside a grid.
- Now if you want to implement blending modes, you might prefer to have a single
Image to represent every layer, and apply the composition to its BitmapSource, which might be a
DrawingImage with a
DrawingGroup containing multiple
ImageDrawing, one for each layer. That transfer the compositing to the ViewModel.
- Now if you want full control, so that you can apply sophisticated image-processing algorithms, represent your image as arrays (of double, that would be a good thing), change color modes, and do other Clever Things, then you could perfectly has your layers represented in the model, and have some operation to, say, transfer that array to a WriteableBitmap in the ViewModel, which then automatically updates the View's Image.
It is interesting to "visualize" (pun intended) how, for example, drawing with a self-smoothing, brushed pencil would work: with the selected tool, you click on the image and drag. You could implement your DataContext (ViewModel) as an Interface, say
ITool and make mouse events like MouseMove call methods on the ViewModel. You would then store the drawing-space (not screen-space) coordinates in an array, which could be continuously smoothed by some algorithm defined in the model. Then, you can regenerate your image source (that is, render it in the model), and in the ViewModel you just set its result to the
CanvasSource property, and the property changed event would automatically update the View. This is quite different than most mouse-drawing demos found using WPF.
Hope this helps!