Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Control Themes #1883

Open
grokys opened this issue Sep 9, 2018 · 3 comments
Open

Default Control Themes #1883

grokys opened this issue Sep 9, 2018 · 3 comments

Comments

@grokys
Copy link
Member

grokys commented Sep 9, 2018

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:

  • This style would be evaluated even if the user has installed a theme themselves, increasing
    load time
  • If a user wants to override the theme, Setters from this style could "leak through" and be
    applied to the control even though from the users point of view the style hasn't been installed[1]

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:

  • A [DefaultTheme(url)] attribute can be applied to controls to point to the default theme
  • An extra property is added to Style: IsTheme
  • This property is set to true in a theme style:
<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>
  • When a theme style matches a control, evaluation of styles for that control stops. To allow this
    a theme style should probably not allow class selectors etc.
  • If all styles have been evaluated for a control and no theme style has been encountered, the
    style referenced by the [DefaultTheme] attribute is applied

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
Copy link
Contributor

wdcossey commented Sep 9, 2018

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
Copy link
Contributor

ahopper commented Sep 10, 2018

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

@Gillibald
Copy link
Contributor

Gillibald commented Sep 11, 2018

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.

@grokys grokys mentioned this issue Jun 29, 2022
2 tasks
@maxkatz6 maxkatz6 reopened this Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants