Hi all!
What I'm trying to achieve is to change 2 colors in the theme (accent colors basically) keeping everything else same as it comes out of the box.
What I don't want to do is to copy the default styles in my project, mainly because I'd like to be always up to date with the latest changes coming in the new versions/SRs and I don't want my code blown up with hundreds of lines of those styles just because I want to change 2 colors.
My current approach is changing the resources in the resource dictionaries of the original theme and registering a new theme at runtime at an early startup. The code is as following:
private static void CreateCustomMetroTheme(IEnumerable<Type> controlTypes, ResourceDictionary myColors) { var accentColorKey = "Color_024"; var metroDictionary = ThemeManager.GetResourceSet("Metro", ThemeManager.AllGroupingsLiteral); var accentColor = (Color)metroDictionary[accentColorKey]; var newAccentColor = (Color)myColors["AccentColor"]; ChangeColor(metroDictionary, accentColor, newAccentColor); metroDictionary[accentColorKey] = newAccentColor; foreach (var controlType in controlTypes) { foreach (var grouping in ThemeManager.GetGroupingsForType(controlType)) { ThemeManager.Register("CustomMetro", grouping, metroDictionary); } } } private static void ChangeColor(ResourceDictionary dictionary, Color oldColor, Color newColor) { foreach (var key in dictionary.Keys) { if (dictionary[key] is SolidColorBrush brush && brush.Color == oldColor) { dictionary[key] = new SolidColorBrush(newColor); } } foreach (var mergedDictionary in dictionary.MergedDictionaries) { ChangeColor(mergedDictionary, oldColor, newColor); } }
This works pretty well for most of the stuff, I'm testing on a XamDataGrid: the group by box, group by prompt, group by plus/minus icons in a pressed state, DateTimeField editor - all this is correctly displayed with the new color (in my case kind of dark blue). The only problem I found so far is the scrollbar. For some reason, the Thumb in a pressed state is still displayed with the old color, see the picture below:
Do you have any idea why the scrollbar doesn't get the new brush (I guess it might be smth to do with how the triggers are working with the resources in WPF)? Can you think of any possible workaround for this issue? Or maybe there is some alternative approach in order to change all the usages of the accent color in a metro theme with relatively few lines of code?
Thanks in advance!
Hello,
Thank you for contacting Infragistics. I put together a sample with your code but I cannot reproduce the behavior. Please take a look and update/reattach. Thanks. Note, I did speak with the developers and I don't think simply looping through the theme and changing it that way will be enough to do what you want. It's best to write a style against the theme using the basedon option or import the theme as you described and change it that way if a theme cannot be changed via a style.
GroupbyThemingTest.zip
Hello Michael,
thanks for your reply!
Unfortunately I cannot use your proposed approach with based on, because the only thing that was not working was a scrollbar and the scrollbar is not a separate element that I can reference like XamDataGrid. And, as I wrote before, I don't want to copy the full styles, because I want to be consistent with future changes as well as to be clean in my code.
However, I've managed to develop my approach further to cover the issue with the scrollbars, so now the scrollbars also get my color instead of the default one. As I expected, it is smth to do with the way how the ControlTemplates with Triggers are evaluating the resources, so what I needed to do is also to replace the brushes inside those ControlTemplates.
For the ones, who are interested in this approach, here is the final code of the method changing the brushes:
private static void ChangeColor(ResourceDictionary dictionary, Color oldColor, Color newColor) { foreach (var key in dictionary.Keys) { if (dictionary[key] is SolidColorBrush brush && brush.Color == oldColor) { dictionary[key] = new SolidColorBrush(newColor); } if (dictionary[key] is not Style style) { continue; } if (style.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Control.TemplateProperty)?.Value is not ControlTemplate controlTemplate) { continue; } foreach (var trigger in controlTemplate.Triggers.OfType<Trigger>()) { foreach (var setter in trigger.Setters.OfType<Setter>()) { if (setter.Value is SolidColorBrush setterBrush && setterBrush.Color == oldColor) { setter.Value = new SolidColorBrush(newColor); } } } } foreach (var mergedDictionary in dictionary.MergedDictionaries) { ChangeColor(mergedDictionary, oldColor, newColor); } }