Where should I put WPF specific code when using MV

2020-07-09 09:36发布

I'm just getting up to speed on MVVM, but all the examples I've seen so far are binding View controls to simple non-WPF specific data types such as strings and ints. However in our app I want to be able to set a button's border brush based on a number in the Model.

At the moment, I translate the number into a brush in the ViewModel to keep the View XAML only, but is that right?

I don't like putting WPF specific code in the ViewModel, but equally I don't like the idea of putting code-behind on my View panel.

Which is the best way?

Thanks

标签: c# wpf mvvm
2条回答
不美不萌又怎样
2楼-- · 2020-07-09 09:59

At the moment, I translate the number into a brush in the ViewModel to keep the View XAML only, but is that right?

No, not really.

Ideally, you should keep WPF dependencies out of your ViewModel. This helps allow your application to be more testable, but also easily translatable to Silverlight or other technologies in the future.

WPF provides a mechanism for this exact scenario, however: IValueConverter. It is very easy to make a ValueConverter that does the translation from an integer, string, or any other type, into a brush. The Data Binding Overview shows an example of translating from a Color to a Brush using a Value Converter.

This is a much better design in the long run... "Brushes" and other WPF concepts are really part of the View - they aren't tied to your logic. Your ViewModel should think in terms of state, and your View should translate that state to a specific way to represent the state.

Say you want to use a "red" brush to display an error. Instead of the ViewModel exposing a brush, it should expose some primitive (ie: a bool property) such as IsInErrorState. The View should decide how to represent this - whether it be via a red brush, a big warning, etc... Converters allow this to happen in a purely XAML manner.


In your case, the ValueConverter is easy. Since you're going from a Number -> Brush (though I'd recommend using a custom Enum instead of an int), you can just do something like:

[ValueConversion(typeof(int), typeof(SolidColorBrush))]
public class IntToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int option = (int)value;
        switch(option)
        {
            default:
                return Brushes.Black;
            case 1: 
                return Brushes.Red;
            case 2: 
                return Brushes.Green;
           // ...
        }

    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // No need to convert back in this case
        throw new NotImplementedException();
    }
}
查看更多
疯言疯语
3楼-- · 2020-07-09 10:00

Try custom ValueConverter.

What's the purpose of keeping your view XAML only? Keeping ViewModel clean makes sense because of testability and SoC. But no codebehind?

查看更多
登录 后发表回答