How to change TextBlock font size using a VisualState? - xaml

I have a page that doesn't want to set the font size of a TextBlock as it resizes. I am using a VisualStateManager, but I must be doing something wrong.
<Page
x:Class="MyUWP.Pages.SplashPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:StoryFractalUWP.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="36" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="72" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="110" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<RelativePanel Background="White" x:Name="MainPanel">
<TextBlock x:Name="MainTitle" RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True"
>MyApp</TextBlock>
</RelativePanel>
</Page>
What am I doing wrong?

I think it should work if you put VisualStateManager code inside RelativePanel:
<RelativePanel Background="White" x:Name="MainPanel">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="36" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="72" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainTitle.FontSize" Value="110" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="MainTitle" RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True" FontSize="20" Foreground="Red"
>MyApp</TextBlock>
</RelativePanel>

Related

Styling depending on screen orientation in Xamarin forms

I am styling on phone and tablet but how can I add the option also for orientation? this is all for portrait but how can I add option for horizontal orientation?
<Style TargetType="Grid" x:Key="DocType">
<Setter Property="HeightRequest">
<Setter.Value>
<OnIdiom Phone="50" Tablet="80" />
</Setter.Value>
</Setter>
<Setter Property="BackgroundColor" Value="#F4F5F7"/>
</Style>
You can use OrientationStateTrigger with VisualStateManager here is an example:
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState
x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Blue" />
</VisualState.Setters>
</VisualState>
<VisualState
x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
Source
EDIT
VisualStateManager inside a style, combined with OnIdiom
<Style TargetType="Grid" x:Key="DocType">
<Setter Property="HeightRequest">
<Setter.Value>
<OnIdiom Phone="50" Tablet="80" />
</Setter.Value>
</Setter>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor">
<Setter.Value>
<OnIdiom Phone="Blue" Tablet="Green" />
</Setter.Value>
</Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor">
<Setter.Value>
<OnIdiom Phone="Yellow" Tablet="Purple" />
</Setter.Value>
</Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
in this example:
Mode
Tablet
Phone
Landscape
Green
Blue
Portrait
Purple
Yellow

Using UWP VisualStateManager with varables

I can use VisualStateManager to change individual properties of controls. Something like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--small window-->
<AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Control1.FontSize" Value="13"/>
<Setter Target="Control2.FontSize" Value="13"/>
<Setter Target="Control3.FontSize" Value="13"/>
<Setter Target="Control4.FontSize" Value="13"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<!--large window-->
<AdaptiveTrigger MinWindowHeight="665" MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Control1.FontSize" Value="24"/>
<Setter Target="Control2.FontSize" Value="24"/>
<Setter Target="Control3.FontSize" Value="24"/>
<Setter Target="Control4.FontSize" Value="24"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
This works but is soooo much typing!
Is it possible to use VisualStateManager to set a value for the font and than refer to this variable in XAML?
Something like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--small window-->
<AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="#MyFontSize" Value="13"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<!--large window-->
<AdaptiveTrigger MinWindowHeight="665" MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="#MyFontSize" Value="24"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
I could then use #MyFontSize variable in XAML when designing controls and I could change it in one place.
<TextBlock x:Name="Control1" FontSize="#MyFontSize"/>
<TextBlock x:Name="Control2" FontSize="#MyFontSize"/>
<TextBlock x:Name="Control3" FontSize="#MyFontSize"/>
Is it possible to do something like this with UWP VisualStateManager?
Is it possible to use VisualStateManager to set a value for the font and than refer to this variable in XAML
I'm afraid you can't set variable within VisualStateManager, But for your scenario, we have a workaround that use Setting class as medium and effect other TextControl with MVVM bind.
For example
public class Setting : INotifyPropertyChanged
{
private double _fontSize = 10;
public double CFontSize
{
get { return _fontSize; }
set { _fontSize = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Usage
<Page.Resources>
<local:Setting x:Key="Setting" />
</Page.Resources>
<StackPanel>
<TextBlock
x:Name="BaseControl"
VerticalAlignment="Center"
FontSize="{Binding CFontSize, Source={StaticResource Setting}, Mode=TwoWay}"
Text="Hello" />
<TextBlock
x:Name="Control1"
VerticalAlignment="Center"
FontSize="{Binding CFontSize, Source={StaticResource Setting}, Mode=TwoWay}"
Text="How are you" />
<TextBlock
x:Name="Control2"
VerticalAlignment="Center"
FontSize="{Binding CFontSize, Source={StaticResource Setting}, Mode=TwoWay}"
Text="Fine thank you, and you?" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!-- small window -->
<AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BaseControl.FontSize" Value="13" />
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<!-- large window -->
<AdaptiveTrigger MinWindowHeight="665" MinWindowWidth="1000" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BaseControl.FontSize" Value="24" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</StackPanel>
Found a similar but simpler way to do this inspired by #Nico Zhu's approach. Sharing here in case someone else finds it useful.
My approach is to use one control as a template and bind all other controls of that type to it.
The VisualStateManager sets the property for the "master" controls:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--VisualState to be triggered when window width is <665 effective pixels.-->
<AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="txtHeader.FontSize" Value="13"/>
<Setter Target="txtRegular.FontSize" Value="10"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowHeight="665" MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="txtHeader.FontSize" Value="20"/>
<Setter Target="txtRegular.FontSize" Value="16"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
All other controls can bind to "master" controls.
<TextBlock Text="My Header 1" FontSize="{Binding ElementName=txtHeader, Path=FontSize}" />
<TextBlock Text="My Header 2" FontSize="{Binding ElementName=txtHeader, Path=FontSize}" />
<TextBlock Text="My regular text 1" FontSize="{Binding ElementName=txtRegular, Path=FontSize}" />
<TextBlock Text="My regular text 2" FontSize="{Binding ElementName=txtRegular, Path=FontSize}" />
When a user resizes page, VisualStateManager will change the master controls, all others will get it through binding.
If you want to, you can create hidden controls just to server as templates.

How to create Resource Style in XAML of VisualStateManager?

Hi I have created an ImagenButton as below:
<ImageButton Source="articulos.png" Clicked="ImageButton_Clicked"/>
And this is the code for animation:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale" Value="1"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale" Value="0.8"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
but I want create many buttons with same style, I do not want code repetition.
This is the complete code:
<ImageButton Source="articulos.png" Clicked="ImageButton_Clicked">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale" Value="1"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale" Value="0.8"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ImageButton>
Please help.
You can define styles in your page, and then apply them to your Controls:
<StackLayout.Resources>
<Style TargetType="ImageButton">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale" Value="1"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale" Value="0.8"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</StackLayout.Resources
Check the official documentation, it has there an example

How to make CompositeStateTrigger work with AdaptiveTrigger and DeviceFamilyStateTrigger UWP

I am trying to make AdaptiveTrigger and DeviceFamilyStateTrigger work with CompositeStateTrigger.
Found this useful link which guided me but still unable to make it to work
I've created a sample project at GitHub to reproduce my issue here.
For reference, here's my XAML. What am I missing?
<Grid Background="AliceBlue" x:Name="gridRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup >
<VisualState x:Name="narrow">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MinWindowWidth="0" MaxWindowHeight="520" />
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is narrow" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="medium">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MinWindowWidth="521" MaxWindowHeight="1199" />
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is medium width" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="wide">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MinWindowWidth="1200"/>
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is wide" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="gridText">
<TextBlock x:Name="textBlock" FontSize="40" Text="My Text Block" Style="{StaticResource HeaderTextBlockStyle}" FontWeight="ExtraBold"/>
</Grid>
</Grid>
Your code is almost right. The problem here is that you've set wrong property in your AdaptiveTrigger. In the AdaptiveTrigger, we should set MinWindowWidth and MaxWindowWidth but not MaxWindowHeight.
So you can change you code like following and it should be able to work.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="narrow">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MaxWindowWidth="520" MinWindowWidth="0" />
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is narrow" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="medium">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MaxWindowWidth="1199" MinWindowWidth="521" />
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is medium width" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="wide">
<VisualState.StateTriggers>
<triggers:CompositeStateTrigger Operator="And">
<mytrigger:AdaptiveTrigger MinWindowWidth="1200" />
<triggers:DeviceFamilyStateTrigger DeviceFamily="Desktop" />
</triggers:CompositeStateTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="textBlock.Text" Value="Desktop is wide" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

windows 10 templated control and AdaptiveTrigger

How can I use AdaptiveTrigger in Templated Control in Windows 10 (I use Windows 10 Pro Insider Preview Build 10074). Window.Current.SizeChanged event do not fire up when window size change. What is proper way to make fluid control? Here is what I try to do, but nothing happens when change size of screen:
XAML template:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1">
<Style TargetType="local:CustomControl1" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomControl1">
<Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualSizeStates">
<VisualState x:Name="Small">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Rect.Fill" Value="Green"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Big">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowHeight="1000" MinWindowWidth="1000" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Rect.Fill" Value="Blue"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="Rect" Fill="Red" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
The trick is that you have to put VisualStateManager with AdaptiveTrigger-s into root control of ControlTemplate otherwise it will not work.
Here is example:
Generic.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdaptiveLayoutExample">
<Style TargetType="local:CustomControl1" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomControl1">
<Grid x:Name="RootGrid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="Yellow"/>
<Setter Target="MyGrid.Background" Value="White"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="Gray"></Setter>
<Setter Target="MyGrid.Background" Value="Red"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="MyGrid" Width="50" Height="50" Background="Black"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainPage.xaml -->
<Page
x:Class="AdaptiveLayoutExample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdaptiveLayoutExample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<local:CustomControl1 Width="100" Height="100"/>
</Grid>
</Page>
I don't think AdaptiveTriggers works in a style like that. The only place I've got it to work is directly in a UserControl or a Grid inside a Page. I know for sure they don't work in a DataTemplate. The VisualStateManager must be before the controls content too I believe. Try a different approach like this:
<!--in app.xaml or something-->
<ControlTemplate x:Key="controlTemplate1" TargetType="MyControl">
<Border Background="Green"/>
</ControlTemplate>
<ControlTemplate x:Key="controlTemplate2" TargetType="MyControl">
<Border Background="Blue"/>
</ControlTemplate>
<!--in your page-->
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="visualStateGroup" >
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="control.Template" Value="{StaticResource controlTemplate1}"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="control.Template" Value="{StaticResource controlTemplate2}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<MyControl x:Name="control" Template="{StaticResource controlTemplate1}"/>
</Grid>
Not tested but let me know how that works out.
Please note that when you don't have a "Big" VisualState to trigger the default settings you will keep having the overwritten ones from the other VisualStates. Might be obvious, but it took some time for me to grasp it.