Change brushes based on ViewModel property

2019-08-08 17:16发布

I have an application which has CarViewModel + view (UserControl). What I want to achieve is to change the style of brushes when the bound DataContext Car.Status changes.

I found out how to change the brushes (in code behind of the view):

private void LoadThemeResources(bool isPrepareMode)
{
    if (isPrepareMode)  
    {
        Uri themeUri = new Uri(@"/../Resources/MyBrushes.Light.xaml", UriKind.Relative);
        ResourceDictionary themeDictionary = Application.LoadComponent(themeUri) as ResourceDictionary;
        this.Resources.MergedDictionaries.Add(themeDictionary);
    }
    else
    {
        this.Resources.MergedDictionaries.Clear();
    }
}

By default the application and everthing has a dark theme spread over multiple files. This MyBrushes.Light overwrites some of those.

But I have no clue how I can execute the LoadThemeResources function based on a property change in the ViewModel in a MVVM friendly way.

I can do in the code behind of the view:

var vm = (CarViewModel) DataContext;
vm.Car.PropertyChanged += HandleStatusChanged;

But this is a tight coupling between View and ViewModel.

I can also do it via Messenger (From MVVM Light), but that gets broadcasted throughout the whole application and seems overkill.

Is there an other way? Or preferred way?

2条回答
够拽才男人
2楼-- · 2019-08-08 17:34

You could bind to a property on your ViewModel, and use an IValueConverter in your View to turn that property (whether boolean, status enumeration, whatever) into a Brush to be used.

That is, load the theme/resources in the converter (a deliberate bridge between View and ViewModel) so that your View gets the Brush it wants and your ViewModel only has to expose the 'important' information (the bits that help decide what brush to load). The decision logic is all in the converter.

查看更多
冷血范
3楼-- · 2019-08-08 17:38

I would prepare some attached property (used on UserControl). Bind that property to your view-model and add code logic of LoadThemeResources in the property changed callback, something like this:

public static class ThemeService {
    public static DependencyProperty IsPrepareModeProperty = 
                  DependencyProperty.RegisterAttached("IsPrepareMode", typeof(bool), typeof(ThemeService), 
                  new PropertyMetadata(isPrepareModeChanged));
    public static bool GetIsPrepareMode(UserControl e){
       return (bool) e.GetValue(IsPrepareModeProperty);
    }
    public static void SetIsPrepareMode(UserControl e, bool value){
       e.SetValue(IsPrepareModeProperty, value);
    }
    static void isPrepareModeChanged(object sender, DependencyPropertyChangedEventArgs e){
       var u = sender as UserControl;
       u.LoadThemeResources((bool)e.NewValue);
    }        
}
//you need some public method of LoadThemeResources
public void LoadThemeResources(bool isPrepareMode) {
     //...
}

Usage in XAML:

<UserControl ...
             local:ThemeService.IsPrepareMode="{Binding Car.Status}">
      <!-- ... -->
</UserControl>

You can also declare a normal DependencyProperty for your UserControl's class and use that instead of the attached property (the usage is just the same).

查看更多
登录 后发表回答