Avoid to refer styles in all pages - Xamarin.Forms - xamarin

I'm working on a Xamarin Forms app for mobile cross platform.
I found how to apply styles to my pages and controls in this way:
Styles/HeaderStyle.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.HeaderStyle">
<Style x:Key="Header" TargetType="StackLayout">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="BackgroundColor" Value="Green"/>
</Style>
</ResourceDictionary>
Views/Page.xaml
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.HomePage"
xmlns:local="clr-namespace:App;"
NavigationPage.HasNavigationBar="false">
<ContentPage.Resources>
<ResourceDictionary MergedWith="local:HeaderStyle">
</ResourceDictionary>
</ContentPage.Resources>
<!-- Some other page content -->
</ContentPage>
I have some doubt about this implementation:
- I can't figure out how to add multiple style files
- I've to add the style file references to all the pages
I've tried to add the reference in App.xaml in some way like this
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="theme:Resources1"/>
<ResourceDictionary Source="theme:Resources2"/>
</ResourceDictionary.MergedDictionaries>
But without success.

If you have styles that you use everywhere, you can put them in a global style.
In your App.xaml you can define a ResourceDictionary just like you have now. For example:
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="BorderColor" Value="Lime" />
<Setter Property="BorderRadius" Value="5" />
<Setter Property="BorderWidth" Value="5" />
<Setter Property="WidthRequest" Value="200" />
<Setter Property="TextColor" Value="Teal" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
Then in your page you should be able to refer to them without having to declare the merging of the dictionary, like so:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ApplicationStylesPage" Title="Application" Icon="xaml.png">
<ContentPage.Content>
<StackLayout Padding="0,20,0,0">
<Button Text="These buttons" Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating" Style="{StaticResource buttonStyle}" />
<Button Text="application style overrides" Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
If you were to override the style, you could do so by declaring a style in that page, styles lower in the hierarchy take precendence over the styles higher up.
You could also choose not to add the x:Key attribute on a style to make it implicit. That way you do not have to declare the Style property on a control.

Related

Why .NET-Maui Global Styles are not working?

I have declared global styles in .Net Maui and trying to access it from one of the pages but it's throwing exceptions
Microsoft.Maui.Controls.Xaml.XamlParseException: Position 10:37. Type converter failed: Exception has been thrown by the target of an invocation.
Microsoft.Maui.Controls.Xaml.XamlParseException: Position 8:34. StaticResource not found for key Primary.
App.xaml code
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp"
x:Class="MyApp.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Style x:Key="redLabelStyle"
TargetType="Label">
<Setter Property="TextColor"
Value="Red"/>
<Setter Property="FontSize"
Value="Small"/>
<Setter Property="FontAttributes"
Value="Bold"/>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor"
Value="Green"/>
<Setter Property="FontSize"
Value="Small"/>
<Setter Property="FontAttributes"
Value="Bold"/>
</Style>
</Application.Resources>
MainPage.xaml code
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
NavigationPage.HasNavigationBar="False"
x:Class="MyApp.MainPage">
<VerticalStackLayout HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Label Style="{StaticResource redLabelStyle}"
Text="Global Style Red Label"/>
<Label Text="GLobal Style Green Label"/>
<Label Text="GLobal Style Green Label"/>
</VerticalStackLayout>
</ContentPage>
Note: This is the default app created by .Net Maui.
This is the wrong place i think </ResourceDictionary>
Try this :
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="redLabelStyle"
TargetType="Label">
<Setter Property="TextColor"
Value="Red"/>
<Setter Property="FontSize"
Value="Small"/>
<Setter Property="FontAttributes"
Value="Bold"/>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor"
Value="Green"/>
<Setter Property="FontSize"
Value="Small"/>
<Setter Property="FontAttributes"
Value="Bold"/>
</Style>
</ResourceDictionary>
</Application.Resources>
Also give the second style a name. <Style TargetType="Label"> a name in x:Key=""
Like
x:Key="greenLabelStyle"
You can also add global styles to the Styles.xaml file referenced in your MergedDictionaries
Please note the location of file
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
You can copy your styles directly into there instead, allowing you to keep all of your global styles in one central place. This helps keep your App.xaml nice and tidy.

XAML Preview in Visual Studio not showing title bar

I am working on designing some user interfaces for my project, and am noticing that the title bar that is displayed when I run my app is not present in the XAML Design Previewer. Not only that, the title bar apparently is throwing off the size of my elements. Is there any way to get the title bar to display in the preview so I can accurately design my UI? I am using Visual Studio 2019. Here is some code. I am also attaching screenshots of the preview vs the emulator.
I've tried explicitly setting the NavigationPage.HasNavigationBar property to true in the content page header of MainScreen.
I initiate AppShell and make it the MainPage of the app:
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
This is the XAML of AppShell:
<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:MyApp.Views"
Title="MyApp"
x:Class="MyApp.AppShell"
>
<!--
Styles and Resources
-->
<Shell.Resources>
<ResourceDictionary>
<Color x:Key="NavigationPrimary">#2196F3</Color>
<Style x:Key="BaseStyle" TargetType="Element">
<Setter Property="Shell.BackgroundColor" Value="{StaticResource NavigationPrimary}" />
<Setter Property="Shell.ForegroundColor" Value="White" />
<Setter Property="Shell.TitleColor" Value="White" />
<Setter Property="Shell.DisabledColor" Value="#B4FFFFFF" />
<Setter Property="Shell.UnselectedColor" Value="#95FFFFFF" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{StaticResource NavigationPrimary}" />
<Setter Property="Shell.TabBarForegroundColor" Value="White"/>
<Setter Property="Shell.TabBarUnselectedColor" Value="#95FFFFFF"/>
<Setter Property="Shell.TabBarTitleColor" Value="White"/>
</Style>
<Style TargetType="ShellItem" BasedOn="{StaticResource BaseStyle}" />
</ResourceDictionary>
</Shell.Resources>
<!-- Your Pages -->
<FlyoutItem Title="Home Page" x:Name="MainScreenFlyout">
<ShellContent ContentTemplate="{DataTemplate local:MainScreen }"/>
</FlyoutItem>
<FlyoutItem Title="Second Page" x:Name="SecondPage">
<ShellContent ContentTemplate="{DataTemplate local:LoadingScreen}"/>
</FlyoutItem>
</Shell>
This is the XAML of the MainScreen view.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="MyApp.Views.MainScreen"
xmlns:vm="clr-namespace:MyApp.ViewModels"
Title="MyApp">
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Primary">#2196F3</Color>
<Color x:Key="Accent">#96d1ff</Color>
<Color x:Key="LightTextColor">#999999</Color>
</ResourceDictionary>
</ContentPage.Resources>
<Grid Padding="0,25,0,25">
<Grid.RowDefinitions>
<RowDefinition Height="5*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="10*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</ContentPage>
Edit: My ultimate goal was to get the preview to show the same way that the emulator ultimately would, and I did find a work around to accomplish it. I will have to manually adjust for each new device, but that's a headache for when I am making it response.
The solution is to set a top margin in the ignorable http://xamarin.com/schemas/2014/forms/design namespace. I tried to find the heights and got some numbers but they didn't work for me so I fooled around with it until it matched up. 87 appears to be the magic number for my device.
<!--Title bars are 48 and 56 for android devices, 44 for iphone, but apparently that isn't right?-->
<Grid Padding="0,25,0,25" d:Margin="0,87,0,0" x:Name="MainScreenGrid">
Because the "Title bar" called also navigation bar is generated by Shell, while the previewer renders only MainPage.xaml file (not Application.MainPage which is Shell).
I believe Shell is still not handled properly in the previewer, as well as in emulator hot reload, future versions of VS will probably bring some enhancements. For example if you open AppShell.xaml in the previewer nothing interesting will be rendered only a blank page.
Update
xaml previewer has been completely removed in VS 16.9.0 Preview 4.0, it will be replaced with an enhanced Hot Reload.

Is there a way I can add resources to a page in the C# backend?

I have pages that look like this:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Japanese;assembly=Japanese" xmlns:template="clr-namespace:Japanese.Templates" xmlns:t="clr-namespace:Japanese.Views.HelpTab.Xaml" x:Class="Japanese.Views.HelpTab.AssignMode" Title="Assign Mode Help" BackgroundColor="{DynamicResource PageBackgroundColor}">
<ContentPage.Content>
<ScrollView>
<StackLayout Spacing="0">
<StackLayout.Resources>
<Style TargetType="Frame" CanCascade="true">
<Setter Property="Style" Value="{StaticResource FrameBorder}" />
</Style>
<Style TargetType="Label" CanCascade="true">
<Setter Property="TextColor" Value="{DynamicResource HelpTextColor}" />
<Setter Property="Style" Value="{StaticResource HD}" />
</Style>
<Style TargetType="Grid" CanCascade="true">
<Setter Property="Style" Value="{StaticResource HG}" />
</Style>
</StackLayout.Resources>
There are about twenty pages in the application that all have the same set of Resources added but not every page.
Is there a way that I can add the resources in the C# backend of my own page and then usesenter code here that for those twenty pages so that all I would need to do is something like this:
<?xml version="1.0" encoding="UTF-8"?>
<MyContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Japanese;assembly=Japanese" xmlns:template="clr-namespace:Japanese.Templates" xmlns:t="clr-namespace:Japanese.Views.HelpTab.Xaml" x:Class="Japanese.Views.HelpTab.AssignMode" Title="Assign Mode Help" BackgroundColor="{DynamicResource PageBackgroundColor}">
<ContentPage.Content>
<ScrollView>
<StackLayout Spacing="0">
Alternatively is there some way I could combine all those resources into just one and add that rather than adding the three individual styles?
Sure, there are a couple of things you can do in this area, but I think it will already help you a lot to know there are implicit and explicit styles.
Start by going to your App.xaml and add your styles there. You should see the <Application.Resources> node already there. You can add the <ResourceDictionary> in there and now define any resources or styles as you wish. Those will be available throughout the whole app.
For instance, in your case, like this:
<Application.Resources>
<ResourceDictionary>
<Style TargetType="Frame" CanCascade="true">
<Setter Property="Style" Value="{StaticResource FrameBorder}" />
</Style>
<Style TargetType="Label" CanCascade="true">
<Setter Property="TextColor" Value="{DynamicResource HelpTextColor}" />
<Setter Property="Style" Value="{StaticResource HD}" />
</Style>
<Style TargetType="Grid" CanCascade="true">
<Setter Property="Style" Value="{StaticResource HG}" />
</Style>
</ResourceDictionary>
</Application.Resources>
Because you are specifying the TargetType and not an x:Key, this is an implicit style. It will apply to all elements of that target type. There is two things you can do now; or create an inheritance of the control and apply the style only to that inheritance, or create an explicit style. You then add the x:Key="MyStyle" attribute to a style and add the Style="{StaticResource MyStyle}" to each element you want to apply the style to. More on styles can be read on the Microsoft Docs.
You can define resources in App.xaml and use in an entire project using
Application.Current.Resources["AppColor"];
If you want to use in xaml page the use like
<Label TextColor={Binding StaticResource AppColor}/>

How can I access a resource when it's declared as a Class in C# back end?

My main resources look like this:
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:converters="clr-namespace:Japanese"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Japanese.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary Source="/Resources/DetailRes.xaml" />
I have this resource file:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Japanese.DetailRes">
<Style x:Key="DetailLabel" TargetType="Label">
<Setter Property="FontSize">
<Setter.Value>
<OnPlatform x:TypeArguments="x:Double">
<On Platform="iOS" Value="35" />
<On Platform="Android" Value="35" />
</OnPlatform>
</Setter.Value>
</Setter>
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="LineBreakMode" Value="WordWrap" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
</ResourceDictionary>
In XAML I access it like this:
<t:BaseFrameButtonTemplate xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.RoundButtonText" x:Name="this" >
<Label Text="{Binding Text, Source={x:Reference this}}"
Style="{StaticResource DetailRes}"
x:Name="Label" />
</t:BaseFrameButtonTemplate>
I would like to access this in C# but do not know how to do it. I tried this but it does not find the resource:
Label.Style = (Style)Application.Current.Resources["DetailRes"];
You can use the TryGetValue to obtain a single from your ResourceDictionary:
if (Application.Current.Resources.TryGetValue("DetailLabel", out object style))
{
someLabel.Style = (Style)style;
}

Xamarin.Forms ImageButton Display Issue

I am trying to use an ImageButton in a StackLayout and attempting to set the text of the button to be set below the image. However, the text is always displayed on the right side of the image instead of below. here is the code:
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout x:Class="MyApp.Portable.Controls.MyNavigationControl"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:forms="clr-namespace:Xamarin.Forms;assembly=Xamarin.Libraries.PCL"
x:Name="MyNavControl"
BackgroundColor="White"
HorizontalOptions="FillAndExpand"
MinimumHeightRequest="75"
Orientation="Horizontal">
<StackLayout.Resources>
<ResourceDictionary>
<Style x:Key="NavButtonStyle" TargetType="Button">
<Setter Property="BackgroundColor" Value="White" />
<Setter Property="TextColor" Value="Gray" />
<Setter Property="FontSize" Value="7" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<forms:ImageButton x:Name="GpsButton"
Clicked="GpsBUttonClicked"
Image="GPS_Icon_Light_Gray_sm.png"
MinimumHeightRequest="75"
Orientation="ImageOnBottom"
Style="{StaticResource NavButtonStyle}"
Text="GPS"
VerticalOptions="Start" />
<StackLayout>
In case you haven't found it yet, here is the answer to your question. The problem is a matter of using the incorrect property to set the Image. The ImageButton.Image property will display the image however it will not adhere to any other set properties such as ImageButton.Orientation. To solve your issue you must set the ImageButton.Source property instead.
More info can be found here: https://github.com/XLabs/Xamarin-Forms-Labs/issues/235

Resources