0

As part of my bachelors thesis, I'm trying to develop something akin to a painting program. That means, I have a toolset, be it selection, drawing, highlighting, etc.

I'll have a canvas that displays my current model, based on a set of rectangles/spheres/polygons I have already drawn and created.

Some tools require the view to react differently based on the tools. Rough Examples:

I have a "New Line" tool. The view now displays points you can connect from when you hover over them.

I have a "Selection" tool. When I hover over an element, it's entire color changes.

I have a "New Element" tool. When I move my mouse, a shadow of the element follows my cursor until I press the left mouse buttons.

Now, here's where I'm a little stuck:

All these tools require wildly different behaviour from the view, and not only that, it also requires the view to be dynamic based on calculated properties. I got a few ideas how I could architect my system, but I would rather get nudged into a direction before I do a huge mistake. Here's my questions and thoughts :

Q: First of all, where would I even put that interactive code? I can't do it in XML, since it requires calculation, but putting it in the ViewModel is also not correct since the ViewModel is not supposed to know about the View. Thus, do I put it in the Code of the View itself? That also seems kinda strange to me.

Now to my architectural ideas:

Idea 1: I could do a new View + Viewmodel for each tool. When A new tool is selected, the view and viewmodel are simply exchanged in a frame, and all the behaviour is encapsulated inside the new view + ViewModel. However, that seems like it not only tightly couples each View and ViewModel together, it also feels like a lot of boilerplate code.

Idea 2: Each Tool itself has "Command" class based on a ICommand interface that requires a reference to the view as well as every possible option of User Interaction. Then the ViewModel delegates the UserInput onto the current Command, which can then manipulate the given ViewInstance to display things it wants. This however feels very inflexible, as if I'm just delegating the problem from Idea 1 somewhere else, and incapable of future growth. Whenever I want to add a new way for a user to interact, I'd have to go and adjust the interface, and perhaps all underlying commands if I didn't provide a default implementation.

None of these really satisfy me, and I'm feeling like I'm missing a crucial step for this part of the architecture. I'd gladly appreciate any pointer. Thank you for your time and reading!

  • I'd recommend looking at the Microsoft Prism sample app and Prism documentation to look at the way MVVM and WPF are used together here, with Prism being used to solve most of the typical WPF+MVVM problems: prismlibrary.github.io/docs/wpf/legacy/Introduction.html – Ben Cottrell Apr 18 at 12:38
  • 1
    @BenCottrell: Prism is a fine tool, and we use it here at work. It's a heavy lift, however, if you're not well-versed in the enterprise patterns that it uses. And I'm not sure how much of it would be applicable to a painting program. Why would you even use MVVM for a painting program? – Robert Harvey Apr 18 at 14:38
  • @RobertHarvey You're right that MVVM might be a bit heavy for a program this small, however there's more to it than just pure painting (analyzing data, simulations, etc. etc...) so it's a good way for me to learn the architecture more in detail in this scope! – Joe Degler Apr 18 at 14:42
  • Well, you don't really need Prism to achieve MVVM. What Prism mostly brings to the table is modularity. – Robert Harvey Apr 18 at 14:46
1

Q: First of all, where would I even put that interactive code? I can't do it in XML, since it requires calculation, but putting it in the ViewModel is also not correct since the ViewModel is not supposed to know about the View. Thus, do I put it in the Code of the View itself? That also seems kinda strange to me.

First, why shouldn't the ViewModel know anything about the View? It's what manages the interaction between the View and the Model. It just shouldn't contain View elements. I.e. it shouldn't have a ComboBox as one of the properties. It should prepare the data that drives the combobox so the View is free to swap out a ComboBox for a ListBox, etc.

I'm not exactly sure where your problem lives. I've done a lot of Map work where we project shapes onto a map. In that scenario we have the concept of a geo coordinate in lat/lon pairs that need to be projected on to a canvas. That canvas has to perform the calculation the actual X,Y location on the screen.

To accomplish that, I had to create a custom Panel or Canvas. That Panel took care of positioning objects on screen, and the View Models were always bound to the lat/lon coordinates. You can check out an abandoned project of mine that demonstrates that capability: https://github.com/bloritsch/Discarta

Specifically look at MapLayer which extends a VirtualizingPanel to handle the arrangement. It's a little more complicated since I have the concept of being able to plug in different projections (pseudo Mercator and equirectangular), however the math is straight forward. The MapLayer is far more performant than attempting to shoehorn the functionality into an IValueConverter or IMultiValueConverter solution.

Once you've separated the overall layout problem, your more specific interactions in the ViewModel are more straightforward.


There used to be a great tutorial on how to create your own radial gauge using MVVM and WPF, but I couldn't find it since there are so many implementations of it laying around now.

The tutorial was great in that it showed how to perform the calculations necessary in a the View to position and rotate the gauge needle, and have the ViewModel calculate the supporting data to display the elements on screen. It even solved the problem of binding physical dimensions of the view to the view model so you could perform those supporting calculations.

The input from the Model was simply the values that showed min, max, and actual magnitude to display. The view model bound to those values so the min, max and intermediate points were available to the View to draw the arch for the gauge background. The actual magnitude controlled the rotation of the needle.

The bottom line is that your View Models should perform the translation between the Model and the View. If you have to do some complicated math in the ViewModel, that's OK. That said, if you need a custom control for certain parts of your application, don't shy away from that.

  • Hey! Thank you very much for your response. I've gone ahead and adjusted my mindset a little bit, and think I've gotten a pretty good idea on how I can structure the canvas now - using something similar to a CompositeCollection where I put in generic "shapes" on the ViewModel side, and writing multiple DataTemplates as well as a DataTemplateSelector on XML side that correcly parses these generic shapes to the correct WPF shapes to be rendered with correct colors. Is that a sensible approach? – Joe Degler Apr 18 at 14:45
  • It sounds like it. That's similar to the way I handled plotting shapes on my map. You may run into corner cases, but you can work around those. For example, I had to bind certain values to the object instance that was derived to the data template. But the general approach should work. – Berin Loritsch Apr 18 at 14:51
  • @JoeDegler, just a heads up, in a similar mapping application that bound DataTemplates to view models I had to rebind the important properites in the view model to the component instance of the data template. That particular project was using attached properties. If you use attached properties, you may have to do something similar. – Berin Loritsch May 1 at 17:10
  • Hey! Thank you for coming back to me. I decided to go with a slightly different approach, where I still have the generic shapes in an observable collection, but due to the nature of the application (and flexibility reasons) I modify the Style of each shape in code, so I'm not really working with DataTemplates anymore (although I think I definitely could), just with generic Shapes that have already been created in code, since WPF seems to be able to parse them to their correct type without any further help. – Joe Degler May 1 at 17:18
  • Also, sorry for forgetting to mark your answer. Did that now. – Joe Degler May 1 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.