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

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}

Related

How to inherit from, or override elements of, the Default style for a WinUI 3 control?

I am trying to learn how to use Styles most effectively in WinUI 3 (from WindowsAppSDK 1.1.1) but I'm having difficulty getting simple inheritance to work.
Consider the NavigationViewItem class. I'd like to modify the default style to bind the FontSize and Height properties. The following works in my Page XAML:
<NavigationViewItem x:Uid="Shell_05" helpers:NavigationHelper.NavigateTo="ViewModels._05CreditViewModel"
FontSize="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}"
Height="{Binding ViewModel.CurrentMenuItemHeight, ElementName=shellPage}">
<NavigationViewItem.Icon>
<BitmapIcon UriSource="\Images\credit.png"/>
</NavigationViewItem.Icon>
</NavigationViewItem>
But adding the two properties to a page resource does not (although the FontSize property works in each of the following. It's the Height that doesn't):
<Page.Resources>
<Style TargetType="NavigationViewItem" >
<Setter Property="FontSize" Value="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}" />
<Setter Property="Height" Value="{Binding ViewModel.CurrentMenuItemHeight, ElementName=shellPage}" />
</Style>
</Page.Resources>
Neither does adding the style to a resource dictionary and merging. I've read over what I can find about inheriting styles and the BasedOn="" extension is an explicit way to derive from an existing style in WinUI versions prior to 2.6 (I think). Apparently, WinUI 3 does not require BasedOn. In any case, simply specifying TargetType="NavigationViewItem" doesn't work, but nor does
<Style TargetType="controls:NavigationViewItem" BasedOn="DefaultNavigationViewItemStyle">
The source code for v1.1.1 of the SDK declares a default style for the NavigationViewItem in generic.xaml, but there is no definition for DefaultNavigationViewItemStyle.
I also cannot derive from the default style using
<Style TargetType="controls:NavigationViewItem" BasedOn="{StaticResource {x:Type NavigationViewItem}}">
because x:Type is undefined.
I can do all of the bindings I want in code but I assume it's both clearer and more efficient to do it in XAML.
How do I inherit, derive from, or override a portion of the default style for a WinUI 3 control (not a custom control) in a desktop application, please?
Thanks for any help. Pointers to good XAML for WinUI 3 documentation (or books and articles) would also be greatly appreciated.
In your case the height is most probably not working since page.resources get compiled before object initialization and the height of the CurrentMenuItemHeight is 0. To solve it just set the mode to one way as such
{Binding ViewModel.CurrentMenuItemHeight,Mode=OneWay , ElementName=shellPage}
When you wish to use BasedOn, just say BasedOn={ThemeResource styleName}.
Just make sure the style is actually defined in Generic.xaml file which u can find in "C:\Users\AdminName.nuget\packages\microsoft.windowsappsdk\1.1.1\lib\uap10.0\Microsoft.UI\Themes"
So your final page.resources should be as such:
<Page.Resources>
<Style TargetType="NavigationViewItem" >
<Setter Property="FontSize" Value="{Binding ViewModel.RootShellFontSize, ElementName=shellPage}" />
<Setter Property="Height" Value="{Binding ViewModel.CurrentMenuItemHeight, Mode=OneWay, ElementName=shellPage}" />
</Style>
</Page.Resources>
But it would be much better to use x:Bind instead of Binding. You can view this page to learn more about it https://learn.microsoft.com/en-US/windows/uwp/data-binding/data-binding-in-depth

XAML reuse specific UI elements

With Xamarin, I have a small UI element which acts as a content divider:
<BoxView StyleClass="contentDivider"
HeightRequest="2"
WidthRequest="1000"
Margin="3, 0"/>
Since I use this a number of times I wanted to be able to have the code written down once, and reuse that code - just like a class with its instance (DRY). It's most likely me being a blind bat and not being able to find how it's done. So, how can I reuse XAML elements?
You can do this with ContentViews (https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/controls/layouts#contentview), which probably works better for larger reuse cases (using more XAML in the ContentView).
Yet, for such a small single element example as yours, you could really just consider using a global style (https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/styles/xaml/application) which its looks like you already have with StyleClass="contentDivider", as long as you only want to override properties on a single element (like your BoxView).
Just add HeightRequest, WidthRequest and Margin to your style and your done.
<Style x:Key="contentDivider" TargetType="BoxView">
<Setter Property="HeightRequest" Value="20" />
<Setter Property="WidthRequest" Value="20" />
<Setter Property="Margin" Value="0,99,0,0" />
... etc
</Style>

Xamarin Forms DatePicker Format Style

I have run out of ideas for trying to change the DatePicker Format property in XAML using OnIdiom. I have tried using XAML styles, I have tried defining the string in a Constants.cs file and referencing it from XAML. The only other way I can think of would be to extended the DatePicker and set it in there. Has anyone gotten this to work?
Things that did not work are listed below.
Global Style with OnIdiom:
<Style TargetType="DatePicker">
<Setter Property="Format">
<OnIdiom x:TypeArguments="x:String"
Tablet="D"
Phone="d"/>
</Setter>
</Style>
Global style referencing a string defined in my Constants.cs class file, which uses Device.Idiom == TargetIdiom.Phone ? "d" : "D";
<Style TargetType="DatePicker">
<Setter Property="Format" Value="{x:Static map:Constants.LongDateTimeStringFormat}"/>
</Style>
Trying to only define the string with a key:
<OnIdiom x:Key="LongDateTimeStringFormat"
x:TypeArguments="x:String"
Tablet="D"
Phone="d"/>
Trying to only define the string with a key another way:
<x:String x:Key="LongDateTimeStringFormat">
<OnIdiom x:TypeArguments="x:String"
Tablet="D"
Phone="d"/>
</x:String>
It also does not work when trying to use <OnIdiom> within the <DatePicker.Format> directly, without styles, or trying to set the DatePicker.Format to {x:Static map:Constants.LongDateTimeStringFormat}.
Well... it seems to be working now in a different project. Adding the following global style does not give me a format exception like it did yesterday.
<Style TargetType="DatePicker">
<Setter Property="Format">
<OnIdiom x:TypeArguments="x:String"
Tablet="D"
Phone="d"/>
</Setter>
</Style>
May have to investigate a little later.

UWP ResourceDictionary Style Error: The parameter is incorrect

I'm making a ResourceDictionary of common styles that are used throughout my application and one of them is:
<Style x:Key="ME_BASE_AppbarButtonSaveStyle"
TargetType="AppBarButton">
<Setter Property="Label"
Value="Save" />
<Setter Property="ToolTipService.ToolTip"
Value="Save" />
<Setter Property="Icon">
<Setter.Value>
<FontIcon FontFamily="Segoe MDL2 Assets"
Glyph="" />
</Setter.Value>
</Setter>
</Style>
It's all ok if I apply the style only one AppbarButton on the Page, but if I want to have two buttons with the same style, I get the following error:
The parameter is incorrect
It's of ok (no error) if I remove the icon property out of the style...
But that's kind of missing the point...
Anyone experienced something similar? Perhaps...
Thank you for all the help.
Error HRESULT E_Fail has been returned from a call to a COM component.
This error will occurred when you use this style for the second AppBarButton. This error usually happens when a reference to a style or an event handler that does not exist or is not with the context of the XAML, you can see the exception information of your problem:
If you read this document: XAML resources must be shareable, you will find:
Custom types used as resources can't have the UIElement class in their inheritance, because a UIElement can never be shareable (it's always intended to represent exactly one UI element that exists at one position in the object graph of your runtime app).
Whether a Icon property of AppBarButton or a FontIcon derives from UIElement, so I guess this is the reason why can't this property be styled in the resource dictionary.
Besides, I will consider if this is a right direction to define the Icon property for each AppBarButton in the style, normally I'd like give each button a different icon as content.
But if you insist to do this, I can provide you a workaround method by defining the Content of the AppBarButton, this is the construction of your AppBarButton:
You use a FontIcon as the content of the AppBarButton, so we can modify your style like this:
<Style x:Key="ME_BASE_AppbarButtonSaveStyle" TargetType="AppBarButton">
<Setter Property="Label" Value="Save" />
<Setter Property="ToolTipService.ToolTip" Value="Save" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<FontIcon FontFamily="Segoe MDL2 Assets"
Glyph="" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

Overriding Resources from Generic.xaml in UWP Applications

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>