unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
8.94k stars 724 forks source link

Uno Doesn't Find Styles in Themes\Generic.xaml #4424

Open robloo opened 4 years ago

robloo commented 4 years ago

Current behavior

Any styles defined in Themes\Generic.xaml are not found.

Expected behavior

This is the standard location for default styles in UWP apps and needs to be supported. It is also common practice to use MergedDictionary to pull in resources from other directories referenced by Generic.xaml.

When Uno searched for an implicit/default style for any TemplatedControl defined in Generic.xaml it should be found.

How to reproduce it (as minimally and precisely as possible)

As described.

Workaround

Define styles in App.xaml. Uno correctly handles nested MergedDictionaries so the following works:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources
                    xmlns="using:Microsoft.UI.Xaml.Controls" />
                <ResourceDictionary
                    Source="ms-appx:///Themes/Generic.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Environment

Nuget Package Version(s): 3.1.6

Affected platform(s):

IDE:

Relevant plugins:

Anything else we need to know?

robloo commented 4 years ago

@davidjohnoliver Is this an easy one to add support for? It will help a lot of apps coming over from UWP and seems pretty important it should work right out of the box.

If you can provide a general idea of where to modify code I can look at this as well.

davidjohnoliver commented 4 years ago

@robloo This is going to be a long answer :p

First of all, there might be a partial solution which addresses the bug you encountered (which might be the most pressing issue in real-world cases): detect if there's a Themes/Generic.xaml in the application, and make any styles in it globally available. Some limited patch is definitely a possibility, so long as it doesn't introduce regressions.

The full problem

First, about styles from FrameworkElement's perspective. The code in Uno is pretty close to what UWP does in this regard. There are 3 levels of styles that I refer to as 'explicit', 'implicit' and 'default.'

The 'explicit style' is whatever is explicitly set to the frameworkElement.Style property. The remaining two are best illustrated by example.

Take the following:

    <Page.Resources>
        <Style TargetType="Button">
            <Setter Property="Background"
                    Value="Fuchsia" />
        </Style>
    </Page.Resources>

    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Hello, world!"
                   Margin="20"
                   FontSize="30" />
        <Button Content="Implicit style" />
        <Button Content="Explicit style">
            <Button.Style>
                <Style TargetType="Button">
                    <Setter Property="Foreground"
                            Value="ForestGreen" />
                </Style>
            </Button.Style>
        </Button>
    </StackPanel>

We're defining an 'implicit style' for Button. (The behaviour would be the same if we defined it in Application.Resources.) Note that the explicit style for the 2nd button replaces the implicit style (it has a green foreground but default gray background). But both implicit and explicit styles are additive with the 'default style'. Ie, both Buttons are inheriting the correct Template with visual states, etc, as well as all other properties set by the default framework style for Button.

The main thing I don't fully understand, and needs to be assessed to handle Theme/Generic.xaml properly, is exactly when <Style TargetType="ns:SomeControl"/> counts as an implicit style, and when it counts as a default style, across all possible cases.

By 'all possible cases' I mean that SomeControl could be defined in 3 different places:

The <Style TargetType="ns:SomeControl"/> declaration could be anywhere downstream from where it's defined (inclusive). It could be detected via FrameworkElement.Resources or Application.Resources (including recursive MergedDictionaries), or it could be detected 'magically' via Themes/Generic.xaml. The use of the control could be anywhere dowstream from where it's defined, inclusive. (Eg in the template of another control, or a Page defined an a class library, or of course in MyApp itself.)

Essentially it needs to be understood, for all combinations of type definition/style declaration/usage, whether the declared style is treated as a 'default style' or an 'implicit style'. For now Uno takes the position that a 'default style' can only be declared in the same assembly where a control is declared, but that might too simplistic, it needs to be fully investigated.

Relevant Uno code

Currently in Uno, default style registrations are generated by XamlCodeGeneration. Note that there's a subtlety that MyApp.GlobalStaticResources.RegisterDefaultStyles() is not called.

Implicit styles are resolved here.

As far as testing, there are some tests already here, and Uno.UI.Tests.ViewLibrary is set up to be able to test how code and Xaml coming from a class library is treated.

robloo commented 4 years ago

@davidjohnoliver Thanks for the response :) I'll have to read through it in more detail later. A few quick comments though:

The main thing I don't fully understand, and needs to be assessed to handle Theme/Generic.xaml properly, is exactly when