Kinnara / ModernWpf

Modern styles and controls for your WPF applications
MIT License
4.45k stars 447 forks source link

Best way to override colours for controls? #2

Closed geometrikal closed 2 years ago

geometrikal commented 4 years ago

I want to give the buttons a border (in another project that uses the nuget package). How should I override the color key in my own resource dictionary?

For example, in Dark.xaml there is

<m:StaticResource x:Key="ButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />

If I want to set this to SystemControlBackgroundBaseMediumHighBrush what is the best way that will still respect the theme settings?

Kinnara commented 4 years ago
  1. Add a theme dictionary inside the <ui:ThemeResources> element in App.xaml:

    <ui:ThemeResources>
    <ui:ThemeResources.ThemeDictionaries>
        <ResourceDictionary x:Key="Dark">
    
        </ResourceDictionary>
    </ui:ThemeResources.ThemeDictionaries>
    </ui:ThemeResources>
  2. If you need to reference the built-in resources, merge them first:
    
    <ResourceDictionary x:Key="Dark">
    <ResourceDictionary.MergedDictionaries>
        <ui:DefaultThemeResources Key="Dark" />
    </ResourceDictionary.MergedDictionaries>

3. Redefine the resource you want to override:
```xaml
<ResourceDictionary x:Key="Dark">
    <ResourceDictionary.MergedDictionaries>
        <ui:DefaultThemeResources Key="Dark" />
    </ResourceDictionary.MergedDictionaries>
    <ui:StaticResource x:Key="ButtonBorderBrush" ResourceKey="SystemControlBackgroundBaseMediumHighBrush" />
</ResourceDictionary>

Currently this doesn't seem to take effect at design time, but does work at run time. And usually you'll need to override the brushes used by other states (such as ButtonBorderBrushPointerOver) as well.

geometrikal commented 4 years ago

Thank you.

On more question if I may. If I want to create a new colour that is derived from the accent colour, e.g. if none of the resource accent colours are exactly the right shade, is there an easy way to derive one?

BTW thanks for you work on this library. It is really really helpful, especially ContentDialog.

Kinnara commented 4 years ago

Glad to hear it's of help. Regarding the question, unfortunately there’s no built-in way to do this. For a starter something like this might work for simple scenarios:

public static class SolidColorBrushHelper
{
    public static readonly DependencyProperty NegativeColorProperty =
        DependencyProperty.RegisterAttached(
            "NegativeColor",
            typeof(Color),
            typeof(SolidColorBrushHelper),
            new PropertyMetadata(Colors.Transparent, OnNegativeColorChanged));

    public static Color GetNegativeColor(SolidColorBrush brush)
    {
        return (Color)brush.GetValue(NegativeColorProperty);
    }

    public static void SetNegativeColor(SolidColorBrush brush, Color value)
    {
        brush.SetValue(NegativeColorProperty, value);
    }

    private static void OnNegativeColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var brush = (SolidColorBrush)d;
        brush.Color = GetNegativeColor((Color)e.NewValue);
    }

    private static Color GetNegativeColor(Color color)
    {
        return Color.FromRgb((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B));
    }
}
<SolidColorBrush x:Key="NegativeAccentColorBrush" local:SolidColorBrushHelper.NegativeColor="{DynamicResource SystemAccentColor}" />
geometrikal commented 4 years ago

Thanks, I did a variation on that with another dep prop to set the shading amount and it works.

I notice that some colours don't appear at all in the designer if the keys were overridden as <ui:StaticResource ..>. Change it to <DynamicResource ..> they appear in the designer but though an error when the app runs 😅 It will work if defined as a SolidColorBrush based off a DynamicResource, e.g.

<SolidColorBrush x:Key="ButtonBackground"
                     Color="{DynamicResource SystemBaseLowColor}"
                     Opacity="0.5" />

Also when creating an xaml with <ui:ContentDialog ..> as a base, nothing appears at all in the designer.

Kinnara commented 4 years ago

Using StaticResource this way is commonly seen in UWP and supported by the designer. Unfortunately in WPF I have to use a hack/workaround to get it working and the designer doesn't like it. To keep things in sync with UWP/WinUI I guess we'll have to live with it for now. Using a SolidColorBrush should work too. One thing to note is that it's probably safer to use Color="{StaticResource SystemBaseLowColor}" here. Using DynamicResource might cause issues mentioned here in the "Troubleshooting theme resources" section.

I've committed a fix for the ContentDialog issue and will push an update to NuGet soon. Thanks for reporting this.

Sylvain2703 commented 3 years ago

Hello,

Is it still possible to override the default resources by taking into account the theme on v0.9.2? As DefaultThemeResources doesn't exist anymore, I don't know how to get it works.

In advance, thanks for your help and for this great library!

EDIT: It's okay, I just found

<ui:ThemeResources>
    <ui:ThemeResources.ThemeDictionaries>
        <ResourceDictionary x:Key="Light" ui:ThemeDictionary.Key="Light">
            <StaticResource x:Key="TextControlBorderBrush" ResourceKey="SystemControlForegroundBaseMediumBrush" />
            <StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumHighBrush" />
        </ResourceDictionary>
        <ResourceDictionary x:Key="Dark" ui:ThemeDictionary.Key="Dark">
            <StaticResource x:Key="TextControlBorderBrush" ResourceKey="SystemControlForegroundBaseMediumBrush" />
            <StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumHighBrush" />
        </ResourceDictionary>
        <ResourceDictionary x:Key="HighContrast" ui:ThemeDictionary.Key="HighContrast">
            <StaticResource x:Key="TextControlBorderBrush" ResourceKey="SystemControlForegroundBaseMediumBrush" />
            <StaticResource x:Key="TextControlBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumHighBrush" />
        </ResourceDictionary>
    </ui:ThemeResources.ThemeDictionaries>
</ui:ThemeResources>