Xamarin Forms Tabbed Page Trigger properties not working - xaml

I'm trying to follow this sample
by using font awesome as icon for the tabbed page and use the triggers to change font color
but when I want to apply the style using font awesome I got an error that Property="IsChecked" does not exist for the target.
I noticed in the sample they are using shells how could I replicate that example using regular tabbed pages?
on the app.XAML I have the following
<OnPlatform x:TypeArguments="x:String"
x:Key="FontAwesomeSolid">
<On Platform="Android"
Value="Font5Solid.otf#Regular" />
<On Platform="iOS"
Value="FontAwesome5Free-Solid" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String"
x:Key="FontAwesomeRegular">
<On Platform="Android"
Value="Font5Regular.otf#Regular" />
<On Platform="iOS"
Value="FontAwesome5Free-Regular" />
</OnPlatform>
For the tabbed page I have the following
<?xml version="1.0" encoding="utf-8"?>
<TabbedPage x:Name="Tab" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="pages.Views.HomeTabbedPage">
<!--Pages can be added as references or inline-->
<TabbedPage.Resources>
<ResourceDictionary>
<Style TargetType="Tab" x:Key="FollowTab">
<Style.Triggers>
<Trigger TargetType="TabbedPage"
Property="IsChecked" Value="False">
<Setter Property="Icon" >
<Setter.Value>
<FontImageSource FontFamily="{StaticResource FontAwesomeRegular}" Glyph=""/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger TargetType="Tab"
Property="IsChecked" Value="True">
<Setter Property="Icon" >
<Setter.Value>
<FontImageSource FontFamily="{StaticResource FontAwesomeSolid}" Glyph=""/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</TabbedPage.Resources>
<ContentPage Title="sample page" />
</TabbedPage>

To replicate that in a normal TabbedPage you can try these steps:
1 - Create a converter to check TabbedPage.CurrentPage type:
public class SelectedTabTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
if (!(value is Page))
throw new ArgumentException("Expected value to be of type " + nameof(Page), nameof(value));
// if value is a NavigationPage check against its RootPage
if (value is NavigationPage navPage)
return navPage.RootPage?.GetType();
return value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
2 - Add the x:Name attribute to the TabbedPage so we can reference it latter:
<TabbedPage x:Name="MyTabbedPage" ...
3 - Add the converter resource to TabbedPage.Resources:
<TabbedPage.Resources>
<ResourceDictionary>
<converters:SelectedTabTypeConverter x:Key="SelectedTabTypeConverter" />
</ResourceDictionary>
</TabbedPage.Resources>
4 - Add the pages and the DataTriggers:
<TabbedPage.Children>
<!-- Normal page tab default values -->
<views:OnePage Title="Page1">
<views:OnePage.Triggers>
<DataTrigger
TargetType="views:OnePage"
Binding="{Binding Source={x:Reference MyTabbedPage}, Path=CurrentPage, Converter={StaticResource SelectedTabTypeConverter}}"
Value="{x:Type views:OnePage}">
<Setter Property="Title" Value="Page1 Selected" />
</DataTrigger>
</views:OnePage.Triggers>
</views:OnePage>
<!-- NavigationPage tab -->
<NavigationPage Title="Page2">
<x:Arguments>
<views:TwoPage />
</x:Arguments>
<NavigationPage.Triggers>
<DataTrigger
TargetType="NavigationPage"
Binding="{Binding Source={x:Reference MyTabbedPage}, Path=CurrentPage, Converter={StaticResource SelectedTabTypeConverter}}"
Value="{x:Type views:TwoPage}">
<Setter Property="Title" Value="Page2 Selected" />
</DataTrigger>
</NavigationPage.Triggers>
</NavigationPage>
</TabbedPage.Children>

Related

How to build string for IconCode in Xamarin

How can I dynamically connect Material icon code with prefix \u in Xamarin?
This not work:
string iconCode = "e87e";
Label label = new Label
{FontFamily = "IconMaterial", Text = $"\u{iconCode}"}
And How can I do the same thing in XAML code?
Thank you!
If you want to use the Material icon, you need to set the FontFamily first.
<ContentPage.Resources>
<OnPlatform x:Key="Material" x:TypeArguments="x:String">
<On Platform="iOS" Value="Material Design Icons" />
<On Platform="Android" Value="materialdesignicons-webfont.ttf#Material Design Icons" />
</OnPlatform>
<Style x:Key="MaterialIcons" TargetType="{x:Type Label}">
<Setter Property="FontFamily" Value="{DynamicResource Material}" />
<Setter Property="FontSize" Value="400" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<Label x:Name="label" ></Label>
</StackLayout>
</ContentPage.Content>
And set the Text in code behind.
label.Text = "\ue87e";

xaml usercontrol multidatatrigger from parent control and itself (usercontrol)

So I'm trying to learn how to dynamically apply style changes to controls. I have not been able to get a user control to change its borderbrush and background based off a radio button in the main window and the usercontrol's text property. Basing it just off the usercontrol's text property does seem to work. So it appears that I'm doing something wrong with getting the radio button's isCheck property.
I've simplified from the original code but this still shows the issue.
MainWindow.xaml
<Window x:Class="UserControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<RadioButton x:Name="calcPace" TabIndex="1" Content="Pace" HorizontalAlignment="Left" Margin="34,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="41"/>
<RadioButton x:Name="calcDistance" TabIndex="2" Content="Distance" HorizontalAlignment="Left" Margin="80,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="61"/>
<RadioButton x:Name="calcTime" TabIndex="3" Content="Time" HorizontalAlignment="Left" Margin="146,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="42"/>
<local:TextBoxTime/>
</Grid>
</Window>
TextBoxTime.xaml (usercontrol):
<UserControl x:Class="UserControlTest.TextBoxTime"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="timeString" TabIndex="4" HorizontalAlignment="Left" Height="23" Margin="68,130,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=calcTime, Path=IsChecked}" Value="False"/>
<Condition Binding="{Binding ElementName=timeString, Path=Text}" Value=""/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
Currently I've added no code behind for either.
Thanks
Here's how I might do it:
public partial class RequireableTextBox : UserControl
{
public RequireableTextBox()
{
InitializeComponent();
}
#region IsRequired Property
public bool IsRequired
{
get { return (bool)GetValue(IsRequiredProperty); }
set { SetValue(IsRequiredProperty, value); }
}
public static readonly DependencyProperty IsRequiredProperty =
DependencyProperty.Register(nameof(IsRequired), typeof(bool), typeof(RequireableTextBox),
new PropertyMetadata(false));
#endregion IsRequired Property
#region Text Property
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(String), typeof(RequireableTextBox),
// Default must be "" not null, for the trigger to understand
new PropertyMetadata(""));
#endregion Text Property
}
XAML
<UserControl
x:Class="UserControlTest.RequireableTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
IsTabStop="False"
>
<Grid>
<TextBox
x:Name="timeString"
HorizontalAlignment="Left"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<!--
Seemed right to disable when unneeded; delete this trigger
if you'd rather not.
-->
<DataTrigger
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="False"
>
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="True"
/>
<Condition
Binding="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value=""
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</UserControl>
Usage:
<StackPanel>
<RadioButton x:Name="calcTime" GroupName="CalculationType">Calculate Time</RadioButton>
<RadioButton x:Name="calcDistance" GroupName="CalculationType">Calculate Distance</RadioButton>
<local:RequireableTextBox
IsRequired="{Binding IsChecked, ElementName=calcTime}"
/>
<local:RequireableTextBox
x:Name="DistanceValue"
IsRequired="{Binding IsChecked, ElementName=calcDistance}"
/>
<!-- Just tossed this in to demonstrate the Text property -->
<Label Content="{Binding Text, ElementName=DistanceValue}" Foreground="Gray" />
</StackPanel>

FontFamily defined in XAML per platform

In my Xamarin Forms project I would like to define the Form family onect per platform and the use that in the application.
So far I've hardcoded the FontFamily per controltype
<Style x:Key="TahomaBase_Label" TargetType="Label" BaseResourceKey="SubtitleStyle">
<Setter Property="FontFamily" Value="Tahoma" />
...
</Style>
Is it posible to set Fotnfamily globally in my XAML code preferable itn a OnPlatform tag?
Define a style in the App.xaml and then reference that style throughout the app. That way you can set the font once in the App.xaml using the OnPlatform tag and never have to worry about OnPlatform in all your other XAML files.
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="PlatformFontSample.App">
<Application.Resources>
<ResourceDictionary>
<OnPlatform x:Key="FontFamilyName" x:TypeArguments="x:String" iOS="MarkerFelt-Thin" Android="OpenSans" WinPhone="Segoe UI" />
<Style x:Key="FontLabel" TargetType="Label">
<Setter Property="FontFamily" Value="{DynamicResource FontFamilyName}" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
And then:
<Label Text="{Binding Name}" Style="{DynamicResource FontLabel}" FontSize="Medium" FontAttributes="Bold" LineBreakMode="NoWrap"/>

Xaml Style Child Control in Universal Windows Platform (UWP)

I would like to know how to set style targetting child controls on the UWP within a style definition.
WPF seems to have 'Style.Resources' to define sub-styles but this doesn't seem the case for UWP
example in wpf : WPF - How to create a style that applies styles to child types
If you want the styles in separate sheets ( which you should. I showed the Styles in the control itself because I misread and thought that's what you wanted ) you can create a Resources folder and add different ResourceDictionaries. Personally I usually create a separate dictionary for Brushes, Styles, Templates, and Converters. Then you declare them in the ResourceDictionary.MergedDictionaries in App.xaml
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Brushes.xaml" />
<ResourceDictionary Source="/Resources/Styles.xaml" />
<ResourceDictionary Source="/Resources/Converters.xaml" />
<ResourceDictionary Source="/Resources/Templates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
You can define Styles in the ResourceDictionary of the parent control. The Style defined in Window.Resources applies to all Rectangles because it doesn't specify a Key, so the Rectangle in the first StackPanel is yellow and small. The second StackPanel defines it's own Resources, which its children use, and they end up different colors and a larger size. There's also Style inheritance in there using BasedOn
<Window x:Class="GBTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-GBTest"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<!--Default style defined in the Window Resources-->
<Window.Resources>
<Style TargetType="Rectangle">
<Setter Property="Width"
Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Fill"
Value="Yellow" />
</Style>
</Window.Resources>
<StackPanel>
<Rectangle />
<StackPanel>
<!--The styles defined in the resources of this StckPanel will apply to its children, overriding the default style defined in the Window Resources-->
<StackPanel.Resources>
<Style TargetType="Rectangle"
x:Key="BigStyle">
<Setter Property="Height"
Value="200" />
<Setter Property="Width"
Value="200" />
</Style>
<Style TargetType="Rectangle"
x:Key="RedStyle"
BasedOn="{StaticResource BigStyle}">
<Setter Property="Fill"
Value="Red" />
</Style>
<Style TargetType="Rectangle"
BasedOn="{StaticResource BigStyle}"
x:Key="BlueStyle">
<Setter Property="Fill"
Value="Blue" />
</Style>
</StackPanel.Resources>
<Rectangle Style="{StaticResource RedStyle}" />
<Rectangle Style="{StaticResource BlueStyle}" />
</StackPanel>
</StackPanel>

XAML UserControl - Set Background based on Trigger

I have a UserControl that contains an Expander :
<UserControl x:Class="Client.DevicesExpander"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="devicesExpander" >
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="{Binding ElementName=devicesExpander, Path=Background}">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="{Binding ElementName=devicesExpander, Path=Background}" />
</StackPanel>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
Ans basically all I'd like to do is change the Expander and StackPanel's Background color based on the IsExpanded of the UserControl (or Expander).
I've added three Dependancy Properties to the control :
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(DevicesExpander));
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(System.Windows.Media.Brush), typeof(DevicesExpander));
But my code does not work. The IsExpanded property from the usercontrol does work as the property changes accordingly (when the expander expands) when checked from within the window where the usercontrol is placed.
How can I change the background color of the Expander based on the UserControl.IsExpanded property ?
Thanks !
EDIT:
I have in the meanwhile done the following:
<UserControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="Transparent">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="Transparent" />
</StackPanel>
</Expander>
and removed the BackgroundProperty dependancy property. I actually thought this could work, but alas ...
I have managed to solve my problem. Herewith the solution (showing only the essential code) ...
My Dependancy Property is created as follows:
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
and my XAML is :
<UserControl x:Class="TestApp.DevicesExpander"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="devicesExpander">
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
The solution in the end was to remove the Backgroud property from the element and specify a DataTrigger for IsExpanded=True and IsExpanded=False. It thus seems like the Background property when specified in the element properties overrides anything that the triggers tried to set.
Hope this helps someone !
Herewith another solution to the same problem, this time using a IValueConverter ...
My XAML:
<UserControl.Resources>
<local:BackgroundConverter x:Key="backgroundConvertor" />
</UserControl.Resources>
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Background" Value="{Binding ElementName=devicesEntry, Path=IsExpanded, Converter={StaticResource backgroundConvertor}}" />
</Style>
</Expander.Style>
</Expander>
and my code for the value convertor :
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "White" : "Transparent";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string color = value as string;
if (color == "White")
return true;
return false;
}
}
I think though that I prefer the XAML only solution, although the IValueConverter option does make for slightly better reading of the XAML ...
(The DependancyProperty is still created exactly the same way)