Pergunta

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!

Foi útil?

Solução

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.

Licenciado em: CC-BY-SA com atribuição
scroll top