Open SetTrend opened 3 years ago
Hi, thanks for contacting us regarding these issues. Hopefully I can provide some clarification.
Implicit styles do work as intended to apply a style based on control type; the issue here is the scope of the Style
resource. In order for this implicit style to work inside of the DataTemplate
, the Style
resource also needs to be inside of the DataTemplate
, like this:
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Grid.Resources>
<TextBlock Text="{x:Bind}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
When Visual Studio creates a page, it actually creates a new class that's derived from the Page
class. This is seen most clearly in the declaration in the code page. For example, in MainPage.xaml.cs, you can see that the new class is MainPage
and it's derived from Page
.
public sealed partial class MainPage : Page
In UWP, implicit styles are not applied to derived classes, only to the type specified in the Style
. To implicitly apply a Style
to MainPage
, the TargetType
has to be MainPage
, like this:
<Style TargetType="local:MainPage">
<Setter Property="Background" Value="Red" />
</Style>
However, as you noticed, the Visual Studio preview handles this differently, and applies the style when TargetType
is Page
, but not when it's MainPage
. I would guess that this is a bug in Visual Studio.
Thanks a lot, @jwmsft, for your informative reply!
I understood from the documentation that I can apply styles to a whole app in order to give it a unified skin, or, appearance.
So, taking your explanation into consideration, given I just wanted to have ...
Page
s in my app to have a Red
backgroundTextBlock
content in my app, regardless of position, to have a Black
foreground colorWhat should I do?
Hi, @SetTrend, the preferred way to change colors in your app is lightweight styling, which is overriding the system brushes that controls use by creating your own brush with the same key. The class documentation for most controls has a list of resources that you can override.
For the Page
background, you override the ApplicationPageBackgroundThemeBrush
resource:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ApplicationPageBackgroundThemeBrush" Color="Red"/>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
(Since it's a ThemeResource, it needs to be in a theme dictionary. I'm using a dictionary with the key "Default" to keep it simple, but it's better to use both "Light" and "Dark" resources as discussed in XAML theme resources.)
This also assumes that the page background is set to this resource, which it is by default in Visual Studio templates, like this: Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
.
TextBlock
is different from most controls in that it doesn't have an editable template, so, unfortunately, it doesn't support lightweight styling. You have to use a Style
to set the foreground. And, as we already know, a Page- or App-level implicit style won't be applied to TextBlock
s inside a DataTemplate
, so you have to use a separate implicit Style
inside the DataTemplate
or a Style
with a key to change those TextBlock
s . As far as I know, there isn't any way around this.
Hope that helps.
Thank you very much, Jim, for enlightening me! 👍
In the last couple of months I did a thorough research on UWP, and I see a number of issues regarding the resource system regarding brushes/colors. The UWP color system has a number of workarounds (RequestedTheme
, ThemeResource
etc.) to overcome its design flaws.
I believe it's a good time to revise and clean up the whole system. Not sure if I should open a corresponding discussion on GitHub in the UWP team space or rather ProjectReunion.
@jwmsft:
Today, I tried your suggestion, but it doesn't seem to work.
Although I created the lightweight styling attribute you suggested, the page still is displayed in black, not in green as it should have been:
I created a corresponding repository for steps to reproduce.
@SetTrend, it looks like your MainPage.xaml Background is not set to the theme resource. Make sure this line is included in the opening Page tag, Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
, and it should work.
<Page
x:Class="GreenStyling.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
</Page>
I'm confused ...
Following the documentation on lightweight styling, that's not "lightweight" styling.
If I'm required to explicitly reference a style from an object then that's "ordinary" styling.
Would you mind elaborating: Where in particular is the lightweight part in your suggestion?
I couldn't find anywhere in the documentation which of the styles provided by WinRT are utilized by any of the UWP components. Would you mind pointing me to a direction, so I'll be able to tell which WinRT styles to override if I want to change a style aspect application wide?
I agree this is confusing, and we need to improve the documentation for styles and themes.
First, we should be clear about the difference between a Style
and a resource such as a color brush. As we discussed before, you can apply a Style
implicitly by setting the TargetType
. This is not true of resources - you must explicitly reference the resource.
With this line of code, Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
, you're not applying a Style
, you're setting the Page
's Background
property to a value that is defined in a ThemeResource
, which must be done explicitly. Again, Page
is a special case because it doesn't have a default Style
, so you have to set the property directly in the XAML for the page.
Most controls have a default Style
where these references are set, so all you need to do is override the resource value and they are picked up automatically by the default Style
. (You don't need to create an explicit reference to your resource definition because the reference already exists in the default Style
.)
For example, the default Style
for the Button
control looks like this (this is just partial):
<!-- Default style for Windows.UI.Xaml.Controls.Button -->
<Style TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
...
/>
...
Ordinary styling would be to create a new Style
, set new values, and apply the new Style
to a Button
.
<Style TargetType="Button" x:Key="newButtonStyle">
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
etc.
...
</Style>
<Button Style={StaticResource newButtonStyle}/>
(To see how much code this takes, add a Button
to a page in Visual Studio, then right-click the Button
on the design surface and select Edit Template > Edit a Copy. Before lightweight styling, this was the only way to change many of the control's colors.)
Lightweight styling (which should maybe be called themeing instead, because you're not changing the control's Style
) is just re-defining the theme resources that are referenced by the control's Style
.
I'm not sure if this answers your last question, but the default control styles and theme resources can be seen in the Generic.xaml file that's installed with the SDK. By default, that's in C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.19041.0\Generic.
Finally, and I should have thought of this earlier, the XAML team has created a tool that you can use to define color themes for your app: Fluent XAML Theme Editor. Hopefully, this tool will make it easier for you to apply the colors you want accross your app, and the GitHub page provides some more information about themeing and the color resources.
The tool also shows how lightweight styling lets you change colors accross your app without re-styling individual controls by letting you override the resources at different levels. For example, the ButtonBackground color is actually defined like this:
<StaticResource x:Key="ButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="SystemControlBackgroundBaseLowBrush" Color="{StaticResource SystemBaseLowColor}" />
<Color x:Key="SystemBaseLowColor">#33FFFFFF</Color>
Each color in a control Style
has a resource key that references a system brush resource, which in turn references a theme Color
resource. You can override the base resource (ButtonBackground) at the App, Page, or Control level. At the App level only, you can also override the system brush resource, which will change all resources that reference that brush; or override the theme color resource, which will change all the brushes that use that color accross controls. This is what you see in the output of the Fluent XAML Theme Editor.
Thank you, @jwmsft, for your continued support and your very comprehensive and elaborated reply 👍👍👍
I have read the referenced sources you mentioned. They have been very informative.
(Doing so I noticed that the <StaticResource>
element used in generic.xaml
and themeresources.xaml
isn't documented.)
So, I updated my repository (SetTrend/UWP-LightWeight-Styling-Issue) to use a Style
for a Page
's background:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Page">
<Setter Property="Background" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
</Style>
</ResourceDictionary>
</Application.Resources>
So, why does Visual Studio 2019 adhere to my style, while the running app doesn't:
What's wrong here?
@SetTrend, you're very welcome. Regarding the green background working in Visual Studio, but not in the running app, refer back to my initial response to this issue, specifically the section "How can I set page background color using ResourceDictionary?". The TargetType for your style is Page
, but it isn't applied to sub-classes of Page
. I believe the fact that this is handled differently in the VS preview and the running app is a bug in VS. (Although it might be worth asking in the WinUI repo whether it's a bug in XAML.)
StaticResource is documented, but unfortunately, it's hidden away in a hard-to-find area of the docs: https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/staticresource-markup-extension. Hopefully we can do a better job of exposing this content soon. Thanks again for your time in providing this feedback to help us improve.
For those new to XAML, it could be useful to reference Xaml's Styling, Templates, and Resources with reference to CSS and SASS paradigms.
@jwmsft:
Yes, right. On Nov, 3rd, you wrote:
When Visual Studio creates a page, it actually creates a new class that's derived from the
Page
class. This is seen most clearly in the declaration in the code page. For example, in MainPage.xaml.cs, you can see that the new class isMainPage
and it's derived fromPage
.
[On Nov, 4th, I then replied]():
So, taking your explanation into consideration, given I just wanted to have ...
- all
Page
s in my app to have aRed
background [...]What should I do?
For the
Page
background, you override theApplicationPageBackgroundThemeBrush
resource:<Application.Resources> <ResourceDictionary> <ResourceDictionary.ThemeDictionaries> <ResourceDictionary x:Key="Default"> <SolidColorBrush x:Key="ApplicationPageBackgroundThemeBrush" Color="Red"/> [...] </ResourceDictionary> </ResourceDictionary.ThemeDictionaries> </ResourceDictionary> </Application.Resources>
That didn't work, so I replied on Nov, 12th:
Today, I tried your suggestion, but it doesn't seem to work.
Next, you replied on Nov, 12th:
@SetTrend, it looks like your MainPage.xaml Background is not set to the theme resource. Make sure this line is included in the opening Page tag,
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
, and it should work.
But that's not been the answer to my question. My original question was:
How can I assign the same background color to all pages without explicitly assigning a reference to that color to each of the pages manually?
So, on Nov, 13th I wrote:
I'm confused ...
On Nov, 13th you then replied:
With this line of code,
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
, you're not applying aStyle
, you're setting thePage
'sBackground
property to a value that is defined in aThemeResource
, which must be done explicitly. Again,Page
is a special case because it doesn't have a defaultStyle
, so you have to set the property directly in the XAML for the page.
This was a truely enlightening detail 🚀 ... yet it didn't answer my question. From your reply, though, I concluded you were implying that applying a Style
would be the answer.
So I updated my sample repository to utilize a Style
referencing the ApplicationPageBackgroundThemeBrush
property.
But, still, that didn' work.
I have the impression we're going around in circles ...
The quest still is the same: Is it possible to assign a themed style to all of an app's pages without referencing that style in each of the pages? Or is that just some weird request of mine that's not possible to accomplish at the moment?
Yes, I think we've come full circle, and I apologize if I've made it more complicated than needed. The short answer, then, is no, you can't set the page theme for all pages without referencing the style in each page.
However, (at the risk of complicating things again) if by style we mean only the Background, this is already set to ApplicationPageBackgroundThemeBrush
in each page when you create it in Visual Studio, so the recommended way to change it is to leave that reference and override the ApplicationPageBackgroundThemeBrush
resource in App.xaml. But the reference to the resource still needs to exist in each page.
But the reference to the resource still needs to exist in each page.
Aha..!*
OK, with all that valuable explanation of yours that makes sense.
So, this extraordinary Page
behaviour is due to the fact that code-behind custom properties, methods and events get added to the final, derived app Page
class, which makes a page particular from all the other framework components which are immutable (Page
basically being immutable, too, that's why that derivation step becomes necessary at all - which I suppose to be technically realized by inclusion rather than inheritance, due to COM components' inability to be inheritable. Thus, an app's pages don't inherit styles assigned to the Page
component, because they "are" no pages, according to OOP.).
So, as a final result of this thread (please correct me if I'm wrong):
Thus, as a round-up, I suppose the issue is correctly placed here in this present repository. And I propose the documentation to be amended by:
Page
peculiarities in UWP.Page
peculiarities.DataTemplate
, the Style
resource also needs to be inside of the DataTemplate
. (→ this comment above)
I now created a corresponding Visual Studio 2019 issue here.
* German: "I see".
That's a good summary of the issues; thank you for that. I'll open internal work items to track these doc updates.
This issue also applies to GRID. Works in visual studio. Not in standalone app.,
UWP documentation claims that styles can be applied to control types as a whole, by providing a
TargetType
attribute.For instance:
However, that doesn't work.
I created two repositories to provide a reproducable project, demonstrating this issue with the UWP resource handling system:
To verify this issue, I posted two corresponding questions at StackOverflow:
The responses at StackOverflow make me believe that either the Microsoft documentation or the implementation of UWP is faulty.
Before I report this issue to the Microsoft UWP product team, I'll jump off by reporting this issue to the Microsoft UWP documentation team.
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.