AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.29k stars 2.19k forks source link

Default Control Themes #1883

Open grokys opened 6 years ago

grokys commented 6 years ago

Currently when a third-party control is used, the user needs to install a style with a theme for the control into App.xaml otherwise when the control is used nothing will appear. This is obviously not ideal.

The obvious way around this would be to automatically install a default theme style into App.xaml the first time a control was used, but this has some problems:

In WPF/UWP the generic theme isn't added to App.xaml, it's "magically" applied if no theme for the control is found. I suspect this is done for these reasons.

A Potential Solution

I'd like to put forward a solution for this:

<Styles xmlns="https://github.com/avaloniaui">
  <Style Selector="Button" IsTheme="True">
    <Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/>
    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
    <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
    <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="4"/>
    <Setter Property="Template">
      <ControlTemplate>
        <ContentPresenter Name="PART_ContentPresenter"
                          Background="{TemplateBinding Background}"
                          BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}"
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          Content="{TemplateBinding Content}"
                          Padding="{TemplateBinding Padding}"
                          TextBlock.Foreground="{TemplateBinding Foreground}"
                          HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                          VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
      </ControlTemplate>
    </Setter>
  </Style>
  <Style Selector="Button:pointerover /template/ ContentPresenter">
    <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
  </Style>
  <Style Selector="Button:pressed  /template/ ContentPresenter">
    <Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/>
  </Style>
  <Style Selector="Button:disabled">
    <Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
  </Style>
</Styles>

Thoughts?

[1]: Say the automatically installed theme contains a setter for Foreground but the user's theme doesn't. This Foreground setter would be applied to the control even though the user doesn't want it.

wdcossey commented 6 years ago

How the magic of generic.xaml works for WPF (from stackoverflow.com)

For a generic.xaml file (case insensitive) to be something special, two conditions must be met:

It must be in the Themes sub-root folder in the project The assembly must be marked with the ThemeInfoAttribute (usually in AssemblyInfo.cs) Then it serves as the default lookup location for any default styles you wish to apply to your Controls. Note also that for a style to be the default it must declare both its TargetType and x:Key as the Type of Control which is to be styled.

If you wish to add entire themes and theme switching to your application, that is accomplished with some coding, this technique merely defines the default resource dictionary.

ahopper commented 6 years ago

how about a StyleIgnoreInclude option in App.xaml or some other mechanism to ignore or prevent loading the default for advanced uses.

Gillibald commented 6 years ago

Looks like the WPF implementation does a lot in the process of resolving a resource. In the end if non matching resource is found the ThemeInfoAttribute is used to load the resource dictionary that could potatialy have the control theme defined. If the default theme is found earlier it just stops searching. That way you can override default themes several times in the tree. Values are somehow cached but not sure how that works.