问题
Here is an example case to elaborate:
I am dynamically creating a simple Bar Graph using an ItemsControl in my View and binding the items to a collection of BarViewModels (each containing percentage a value) in my BarGraphViewModel.
Each bar should have a different color. The colors should be chosen from a collection e.g. {Color1, Color2, ..}
The collection itself is constant but the number of bars will depend on the circumstances.
A simple solution would be to create a simple BarViewModel like so:
public class BarViewModel
{
public int Percentage { get; set; }
public SolidColorBrush Stroke { get; private set; }
public BarGraphViewModel(SolidColorBrush stroke)
{
Stroke = stroke;
}
}
(I left out property changed and validation implementation for brevity)
Now I could just create a BarViewModels from my BarGraphViewModel for each percentage and pass in the appropriate ColorBrush created from my Color collection.
Then in Xaml I would create a simple ItemsTemplate that will bind to these properties.
Only now, since it contains a property of type SolidColorBrush, my ViewModel depends on the Presentation framework and should I want to use it in another environment it will have to be changed.
Does this therefore break MVVM best practices, or is it acceptable (you gotta draw the line somewhere or things get too complicated)
I just wanted to see what other people think about this and if there are other solutions that keep the ViewModel totally ignorant of the Presentation Layer without getting too complicated. I could imagine that ValueConverters could help?
回答1:
In the original Presentation Model pattern described by Martin Fowler, the view "asks" the view-model how to display itself. This seems to favor putting color and size properties on your view-model rather than triggers in your view. The application of this pattern within WPF, however, is somewhat different. In WPF, you typically define how the view looks by using Styles and DataTemplates in the view. Returning specific colors directly from the view-model would be contrary to this approach. So the short answer is: no, don't put color properties on your view-model.
Also within the original Presentation Model pattern, the view-model is an abstraction of the view. So instead of it returning the exact color, it would be preferable to return a "key" that the view can then use to look up the actual color. For example, instead of PersonViewModel.FaceColor returning Red, you'd have PersonViewModel.Mood returning Angry. The view could then use a Style or DataTemplate trigger that translates this to the actual Red color.
So that's my answer and I'm sticking by it, but it's also interesting to consider arguments in the other direction. For one, putting color properties on your view-model is still unit-testable, which seems has become the primary critera for what's okay in the view-model.
Remaining "agnostic" to the view technology isn't a huge factor in either direction. The goal to maintain binary compatibility of view-models with other view technologies is realistic only within the XAML family. Moving all your view-models to their own project which lacks a direct dependency on WPF is a nice idea. But you would have to exclude anything that uses ICommand, or make an exception for a WindowsBase.dll reference. Practically speaking, though, it won't buy you much. We're pretty much glued to Micrsoft technologies! If you decide to port to another GUI framework, then my guess is you're looking at source code conversion. I'm waiting to see if Microsoft goes under before I try it ;) Porting could include changing your color types, if you decided to put them in your view-model. Although not a reason against color properties on your view-model, it's not necessary a reason for it either.
回答2:
Theoretically, the situation you described is against best practice in MVVM. But there is a simple solution to clean your view model up. You should create your own type for representing the color in view model - it can be string, int or enum. Then you can write custom ValueConverter (implementing IValueConverter) to convert view model color type into presentation framework dependent color representation. Converter should be used together with bounding expression. Example on converting bound values is here .
回答3:
I see no technical reasons why your VM above is bad, but would personally use a Brush
rather than a specific subclass. I don't believe this breaks MVVM best practices because your VM is a model of your view. It does not necessarily have to be agnostic of the technology on which your view is built.
However, there are other reasons why it might be a bad idea. Will your view model likely be used in a non-WPF environment? If so, you'll want an abstraction that can then be translated to a platform-specific construct, with WPF's Brush
being one such example. And converters aren't necessarily required for this. Your abstraction can itself be a view model, which then has platform-specific subclasses. For example, you might have a Color
view model, and a WpfColor
view model inheriting from that.
Do you have designers on your team? If so, the use of a Brush
in your VM may inhibit their ability to customize the UI. Your specific case above may be an outlier, but generally speaking, designer and developer collaboration benefits from having the VM expose state, and the view rendering that state. As soon as the VM dictates the visual appearance in any capacity, you've limited the decisions your designers can make.
In my experience, the second reason is a far more common one not to include UI-specific constructs in your VM.
来源:https://stackoverflow.com/questions/1896612/are-wpf-related-properties-inside-a-viewmodel-a-violation-of-mvvm-best-practices