Search code examples
c#wpfmvvmmvvm-lightfody-propertychanged

Change brushes based on ViewModel property


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?


Solution

  • 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).