2

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?

RvdK
  • 19,580
  • 4
  • 64
  • 107

2 Answers2

1

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.

pete the pagan-gerbil
  • 3,136
  • 2
  • 28
  • 49
  • You mean a pure XAML solution? Do you have any example code how that would look? – RvdK Oct 09 '15 at 15:57
  • I think I've misunderstood you slightly - what are you trying to do? Why are you loading a whole themedictionary based on a single Boolean value? – pete the pagan-gerbil Oct 09 '15 at 19:16
  • I'm trying to style a UserControl (and inner UserControls). Lot's of brushes will need to be replaced. These brushes are used troughout the Car UserControl and childs. – RvdK Oct 09 '15 at 22:32
  • A converter is preferable to modifying a resource, if you base the converter on an enumeration of values, then you can ensure colour options for all Staus values. – kidshaw Oct 10 '15 at 06:47
1

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

King King
  • 61,710
  • 16
  • 105
  • 130
  • 1
    Thanks it works! Is it really neceserry to use a seperate class instead of doing it directly in the UserControl code? I'm getting some errors trying to do so: http://stackoverflow.com/questions/33076560/specify-binding-of-dependencyproperty-in-usercontrol-itself/33076908 – RvdK Oct 12 '15 at 09:42
  • @RvdK I believe you can do so, but in your code from that question, you actually make it understand `Status` as an attached property, but it's of course not, try replacing `views:CarView.Status` with just `Status`. – King King Oct 12 '15 at 09:46
  • If I do that, it will give errors. The property 'Status' was not found in type 'UserControl'" – RvdK Oct 12 '15 at 10:05
  • @RvdK yes, that's correct. It looks for that property on `UserControl`, you can try also changing the tag, instead of `UserControl`, use your custom user control name, don't forget to use prefix namespace defined inside your UserControl, e.g: `...` – King King Oct 12 '15 at 10:13