Overriding Resources from Generic.xaml in UWP Applications - xaml

This is my Generic.xaml where I define default background color for my control (CustomControlBackground):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TomShane.Framework.Controls">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="CustomControlBackground" Color="Gray"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style TargetType="local:CustomControl">
<Setter Property="Background" Value="{ThemeResource CustomControlBackground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomControl">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In App.xaml I want to override the default background from Generic.xaml:
<Application
x:Class="TomShane.Framework.Demo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TomShane.Framework.Demo"
xmlns:ctrl="using:TomShane.Framework.Controls"
RequestedTheme="Dark">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="CustomControlBackground" Color="Red"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
With this technique I am able to override UWP system resources (like SystemControlBackgroundBaseLowBrush), but when I try to override the CustomControlBackground, the control's background stays always gray.
What is the thing I am missing?

The logic is wrong.
We can only override upper scope ResourceDictionary in lower scope ResourceDictionary. Actually, it's about the resource lookup behavior but not about which xaml file is loaded first
In WinRT xaml app, the Resource Dictionary scope looks like the following(not MS official picture):
Based on above, we can override system default resource in app.xaml, we can override system/application resource in page.xaml, and etc.
So in your code, actually, the resource defined in generic.xaml will override the resource you defined in app.xaml. If we simply remove the resource CustomControlBackground from the generic.xaml, you will get the red background.
[update]
If you do want to have a default theme, you can keep the design, but we cannot use the x:Default for the resourcedictionary.
For example, if the requestedtheme is Dark(in your case). You can simply use the following to override the default one in app.xaml.
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="CustomControlBackground" Color="Yellow"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Or you can only put the theme into a separate file and reference it only in the client app's app.xaml. But you have to rewrite the file to use different values.

Its unfortunate that there isn't a good way to define theme resources for custom controls in such a way that they can be overridden in the same way that the built-in resources, like SystemControlBackgroundBaseLowBrush, can be. But there is a workaround if you're willing to move things around at runtime. The trick is to end up with a situation where your default resources are not contained in Generic.xaml, but are instead defined in a resource dictionary that is merged with the application's resources, where they CAN be overridden. There's more than one way to accomplish this.
One approach would be to create a separate resource dictionary that has an x:Class with an associated code file. Define your default theme resources in it. Then figure out a way to merge an instance of the class into the application's resources at runtime. One way would be in the static constructor of your control. Be careful not to merge more than one instance.
A somewhat different approach, which I like better, is to use code-behind of the resource dictionary in which you define the control's default style. But you can't have code-behind for Generic.xaml, because the framework will never instantiate an instance of the class. Instead, move the style into its own resource dictionary (with x:Class and code-behind) and add an instance of the new dictionary to the merged dictionaries of Generic.xaml. You will be able to move things around in the constructor of this resource dictionary, after the call to InitializeComponent. For example, if in the xaml file you define a resource dictionary in the MergedDictionaries collection and place your theme resources in in it, then at runtime you would be able to remove that dictionary from the MergedDictionaries collection and merge it into the application's resources.
A limitation of either approach is if you want to override resources by theme, i.e. Light and Dark. Placing an overriding resource directly into the resources of App.xaml will work, but will not allow different values for different themes. To achieve this, it is necessary to define the overriding resources inside the ThemeResources of a resource dictionary which itself is placed in the application resource's MergedDictionaries. (I don't know but it kind of seems like this might be a bug.)
CustomControl.xaml:
<ResourceDictionary x:Class="CustomControlLibrary.Themes.CustomControlTheme"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="CustomControlBackground" Color="#000000" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="CustomControlBackground" Color="#FFFFFF" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="local:CustomControl">
<Setter Property="Background" Value="{ThemeResource CustomControlBackground}" />
</Style>
</ResourceDictionary>
CustomControl.xaml.cs:
namespace CustomControlLibrary.Themes
{
partial class CustomControlTheme
{
public CustomControlTheme()
{
InitializeComponent();
if(MergedDictionaries.Count > 0)
{
var themeResources = MergedDictionaries[0];
MergedDictionaries.RemoveAt(0);
Application.Current.Resources.MergedDictionaries.Insert(0, themeResources);
}
}
}
}
Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="using:CustomControlLibrary.Themes">
<ResourceDictionary.MergedDictionaries>
<themes:CustomControlTheme />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Related

Tooltip color changed to System Theme change though Application Requested Theme is different

I am developing an UWP application. I have theme change option inside my application. When I change theme inside my application, it works good. But when change the system theme to other color, Tooltip of my application is changed. All other controls are working good.
To change theme inside application I used below code:
FrameworkElement root = uIElement != null ? (FrameworkElement)Window.Current.Content : (FrameworkElement)uIElement;
if (root != null)
{
root.RequestedTheme = appMode; // appMode is and ElementTheme
}
I found a solution on internet which suggests to use Tooltip style :
<Image.ToolTip>
<ToolTip Background="Black">
<Grid>
...
</Grid>
</ToolTip>
</Image.ToolTip>
But it has a limitation. I can write this style and bind from xaml only. But can't bind from styles.
I have some styles for button/toggleButton/etc written in CommonStyles.xaml. I wanted to add this custom tooltip style to those styles, but I couldn't. Only I could is to add from xaml.
As I have too many controls in my application, it is very tough to add "customToolTip" style from every control.
Can anyone please help me regarding this??
Update for Applicaiton.Resources
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Styles/CommonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<Color x:Key="ToolTipBG">White</Color>
<Color x:Key="ToolTipFG">Black</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<Color x:Key="ToolTipBG">Black</Color>
<Color x:Key="ToolTipFG">White</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<SolidColorBrush x:Key="CustomToolTipBG" Color="{ThemeResource ToolTipBG}" />
<SolidColorBrush x:Key="CustomToolTipFG" Color="{ThemeResource ToolTipFG}" />
<StaticResource x:Key="ToolTipBackground" ResourceKey="CustomToolTipBG" />
<StaticResource x:Key="ToolTipForeground" ResourceKey="CustomToolTipFG" />
</ResourceDictionary>
</Application.Resources>
Still Tooltip changing color for System theme changes.
Update Screen Capture
Application and System Both in Dark mode:
Application changes to light mode:
Application changes to dark mode:
System changes to light mode:
It contains default ToolTip style in the general.xaml file, you could custom it in Application.Resources and change the default ToolTipBackground ThemeResource as your custom value. And if you just want to fix Background property, you could also update ToolTipBackground StaticResource separately like the following and place it in Application.Resources.
<SolidColorBrush x:Key="ToolTipBackground" Color="Red" />
Update
But I need different value for different theme.
<ResourceDictionary.ThemeDictionaries>
<!-- Default Theme Dictionary -->
<ResourceDictionary x:Key="Default">
<Color x:Key="CustomColor">Red</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="CustomColor">Green</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<SolidColorBrush x:Key="CustomToolTipColor" Color="{ThemeResource CustomColor}" />
<StaticResource x:Key="ToolTipBackground" ResourceKey="CustomToolTipColor" />

How do I create constants to be used in Xamarin.Forms XML

As an Android developer I'm used to work with #dimen/-constants in Androids XML. I find this future useful because it allows me to easily change multiple places that should have the same pixel-length together.
Does Xamarin.Forms have a similar features that I can use?
Well what you looking for are ResourceDictionaries
XAML resources are definitions of objects that can be shared and re-used throughout a Xamarin.Forms application.
These resource objects are stored in a resource dictionary.
A ResourceDictionary is a repository for resources that are used by a Xamarin.Forms application. Typical resources that are stored in a ResourceDictionary include styles, control templates, data templates, colours, and converters.
In XAML, resources that are stored in a ResourceDictionary can then be retrieved and applied to elements by using the StaticResource markup extension. In C#, resources can also be defined in a ResourceDictionary and then retrieved and applied to elements by using a string-based indexer. However, there's little advantage to using a ResourceDictionary in C#, as shared objects can simply be stored as fields or properties, and accessed directly without having to first retrieve them from a dictionary.
Creating and Consuming a ResourceDictionary
Resources are defined in a ResourceDictionary that is then set to one of the following Resources properties:
The Resources property of any class that derives from Application.
The Resources property of any class that derives from VisualElement
A Xamarin.Forms program contains only one class that derives from Application but often makes use of many classes that derive from VisualElement, including pages, layouts, and controls. Any of these objects can have its Resources property set to a ResourceDictionary. Choosing where to put a particular ResourceDictionary impact where the resources can be used:
Resources in a ResourceDictionary that is attached to a view such as Button or Label can only be applied to that particular object, so this is not very useful.
Resources in a ResourceDictionary attached to a layout such as StackLayout or Grid can be applied to the layout and all the children of that layout.
Resources in a ResourceDictionary defined at the page level can be applied to the page and to all its children.
Resources in a ResourceDictionary defined at the application level can be applied throughout the application.
The following XAML shows resources defined in an application level ResourceDictionary in the App.xaml file created as part of the standard Xamarin.Forms program:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<Color x:Key="PageBackgroundColor">Yellow</Color>
<Color x:Key="HeadingTextColor">Black</Color>
<Color x:Key="NormalTextColor">Blue</Color>
<Style x:Key="LabelPageHeadingStyle" TargetType="Label">
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="TextColor" Value="{StaticResource HeadingTextColor}" />
</Style>
</ResourceDictionary>
</Application.Resources>
Beginning in Xamarin.Forms 3.0, the explicit ResourceDictionary tags are not required. The ResourceDictionary object is created automatically, and you can insert the resources directly between the Resources property-element tags:
<Application ...>
<Application.Resources>
<Color x:Key="PageBackgroundColor">Yellow</Color>
<Color x:Key="HeadingTextColor">Black</Color>
<Color x:Key="NormalTextColor">Blue</Color>
<Style x:Key="LabelPageHeadingStyle" TargetType="Label">
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="TextColor" Value="{StaticResource HeadingTextColor}" />
</Style>
</Application.Resources>
Each resource has a key that is specified using the x:Key attribute, which becomes it dictionary key in the ResourceDictionary. The key is used to retrieve a resource from the ResourceDictionary by the StaticResource markup extension, as demonstrated in the following XAML code example that shows additional resources defined within the StackLayout:
<StackLayout Margin="0,20,0,0">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="LabelNormalStyle" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource NormalTextColor}" />
</Style>
<Style x:Key="MediumBoldText" TargetType="Button">
<Setter Property="FontSize" Value="Medium" />
<Setter Property="FontAttributes" Value="Bold" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<Label Text="ResourceDictionary Demo" Style="{StaticResource LabelPageHeadingStyle}" />
<Label Text="This app demonstrates consuming resources that have been defined in resource dictionaries."
Margin="10,20,10,0"
Style="{StaticResource LabelNormalStyle}" />
<Button Text="Navigate"
Clicked="OnNavigateButtonClicked"
TextColor="{StaticResource NormalTextColor}"
Margin="0,20,0,0"
HorizontalOptions="Center"
Style="{StaticResource MediumBoldText}" />
</StackLayout>
For more detailed information kindly take a look here
Apart from the StaticResource as mentioned above, 2 other ways to do it.
First one is, Having static class for constants and refer them in the XAML.
public static class GlobalSetting
{
public static double ImageRotation { get { return 180; } }
}
In the Xaml, you need to add this namespace in the page directive,
xmlns:gb="clr-namespace:XXXX.StaticData"
and use the constant in the xaml code as below,
<Image Source="icon_back.png" Rotation="{x:Static gb:GlobalSetting.BackImageRotation}" HeightRequest="24" </Image>
Second approach is, having a constant parameter in the BaseViewModel, and binding them in the Xaml code.
Sounds to me like you want to define constants/styles in a ResourceDictionary:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/resource-dictionaries.
In the ResourceDictionary you can define your constants/styles by key, in your XAML you can then refer to them as follows:
Color={StaticResource MyColorFromDictionary}

Override theme resource in the scope of a Page

I want to override a theme resource, specifically the SystemAccentColor, in the scope of a specific page.
I have successfully done it in application wide scope. But I can't do it for a specific page.
XAML in App.xaml (this works fine)
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Color x:Key="SystemAccentColor">#862E2D</Color>
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush" Color="Black"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
XAML in Page.xaml (this doesn't work)
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Color x:Key="SystemAccentColor">#111111</Color>
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush" Color="Black"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<!-- Control to test that the resource is overriden. The `ProgressBar` uses the accent color by default -->
<ProgressBar
Height="10"
Grid.ColumnSpan="3"
VerticalAlignment="Top"
IsIndeterminate="True" />
I don't know why the resource is not overridden in the scope of the page.
I have tried removing the override from App.xaml and only overriding the resources in Page.xaml but that doesn't work either.
However if I do this
XAML in Page.xaml
<ProgressBar
Height="10"
Grid.ColumnSpan="3"
VerticalAlignment="Top"
IsIndeterminate="True"
Foreground="{StaticResource SystemControlHighlightAccentBrush}"/>
Then the ProgressBar gets the correct foreground value. Which means that the ovverride does take place.
Does anybody know why this is happening? Or how I can override a theme resource for a specific page?
The best way is using Visual Studio or 'Blend for Visual Studio'. I'm using Visual Studio
Right click on the Element you want to edit (in your case, Progress bar) > select 'Edit Template' > 'Edit a Copy'
A 'Create Style Resource' shows up, Enter a name for your style & in the 'Define in' section, select 'This Document' (this ensures the style only applies to the page/window and not app wide) and click okay
Immediately, the Progress Bar includes a style attribute (see last image) using the new style you created. You can go ahead and modify the code snippet inserted by the IDE to suite your imagination.
I hope this helps out.

Using a ResourceDicitonary in a Windows Phone 8.1 Portable Class Library

I have a Windows Phone 8.1 Portable Class Library with a ResourceDictionary in Themes/Generic.xaml
Now, I want to declare styles there and use them in other Controls I have, in my ResourceDictionary I have something like:
<Style x:Key="myCustomStyle" TargetType="ProgressBar">
{...}
</Style>
And in my Control, something like:
<Page.Resources>
<ResourceDictionary x:Key="dictionary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
{...}
<ProgressBar IsIndeterminate="True" Style="{StaticResource myCustomStyle}" />
But this does not work, but if I have the Style in my Control, it works fine, it's the ResourceDictionary reference that's not working.
How can I make it work?

ListViewItem selection colors overriding

I need to override predefined style of the ListViewItem to not have visible selection. I know how to do it if I copy the whole style into my resources and edit it. But I can't believe there is no lighter way than copy-past the whole style.
So I found that default ListViewItem style uses the following brushes for the selection:
<UserControl.Resources>
<SolidColorBrush x:Key="ListViewItemPointerOverBackgroundThemeBrush" Color="Yellow" />
<SolidColorBrush x:Key="ListViewItemSelectedBackgroundThemeBrush" Color="Yellow" />
<SolidColorBrush x:Key="ListViewItemSelectedForegroundThemeBrush" Color="Yellow" />
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBackgroundThemeBrush" Color="Yellow" />
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBorderThemeBrush" Color="Yellow" />
</UserControl.Resources>
Note: I've put all those brushes to my UserControl as well as set them all to the yellow color. But I don't have any yellow color in my UI, alas.
So the question is: how can I force the default template to use my overridden brushes?
And second(optional) question: maybe I'm doing it wrong and there is a better way to achieve my goal?
As Vasile said you need to override the brushes, this has to be done on App level, as far as I know you will need to template the whole control if you only want to change one control or on one page.
If you are curious, you can find all the brushes under:
C:\Program Files (x86)\Windows Kits\8.0\Include\WinRT\Xaml\Design
To override the listview colors you add this in your App.xaml/resource dictionary, I've added some comments here so you can see which brush does what:
<ResourceDictionary x:Key="Default">
<!--After selection - Background-->
<SolidColorBrush x:Key="ListViewItemSelectedBackgroundThemeBrush" Color="Yellow"></SolidColorBrush>
<!--When pointer hovers over an item - Background-->
<SolidColorBrush x:Key="ListViewItemPointerOverBackgroundThemeBrush" Color="Red"></SolidColorBrush>
<!--When the item is selected (first few milliseconds) - Background-->
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBackgroundThemeBrush" Color="Green"></SolidColorBrush>
<!--When the item is selected (first few milliseconds) - Border-->
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBorderThemeBrush" Color="Black"></SolidColorBrush>
</ResourceDictionary>
You have to override them in App.xaml file, something like this (at least that's how I did) :
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBorderThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ListViewItemPointerOverBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ListViewItemSelectedPointerOverBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ListViewItemSelectedBackgroundThemeBrush" Color="Transparent" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
If you need more customization (in my case the ListView items were some pictures), here's a pretty useful link for changing default Color Controls .
In Windows 8.1 Universal App in shared App.xaml I use this code
<Application
x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XXX">
<Application.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="ListViewItemPointerOverBackgroundThemeBrush" Color="Transparent" />
</ResourceDictionary>
</Application.Resources>
</Application>
I have been trying similar things before with no luck. I think you just need to update the entire template.