VisualStateManager doesn't update Grid properties when using nested Grid controls - xaml

Context: I want to use VisualStateManager to move a StackPanel to a different Grid column under certain conditions.
Problem: It seems that having nested Grid controls prevents my VisualState setter from being applied. In the example below (which has been simplified from the original code), the VisualState setter triggers if the OuterGrid is commented out, but won't fire when the OuterGrid is present.
Question: How can I trigger the VisualState setter when using nested grids?
Example:
<Grid x:Name="OuterGrid">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
>
<Button
x:Name="Button1"
HorizontalAlignment="Stretch"
Content="Button1"
/>
</StackPanel>
<StackPanel
x:Name="Button2"
Grid.Column="2"
>
<HyperlinkButton
Height="36"
HorizontalAlignment="Stretch"
>
<TextBlock
Text="Button2"
/>
</HyperlinkButton>
</StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ButtonAlignments">
<VisualState>
<!-- Move Button2 to the left column -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Button2.(Grid.Column)" Value="0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Grid>

You need to define your Visual States at the root level of your XAML tree, as per docs.
Just move the your <VisualStateManager.VisualStateGroups> into the OuterGrid. I changed your sample a bit - it's working with Visibility instead of the Grid.Column:
<Grid x:Name="OuterGrid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ButtonAlignments">
<VisualState>
<!-- Move Button2 to the left column -->
<VisualState.Setters>
<!--<Setter Target="Button2.(Grid.Column)"
Value="0" />-->
<Setter Target="Button2.(UIElement.Visibility)"
Value="Visible" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="12" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button x:Name="Button1"
HorizontalAlignment="Stretch"
Content="Button1" />
</StackPanel>
<StackPanel x:Name="Button2"
Grid.Column="2"
Visibility="Collapsed">
<HyperlinkButton Height="36"
HorizontalAlignment="Stretch">
<TextBlock Text="Button2" />
</HyperlinkButton>
</StackPanel>
</Grid>
</Grid>

Related

UWP Visual State Manager doesn't see content of DataTemplate

My page structure is shown below.
<ScrollViewer VerticalScrollBarVisibility="Auto">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="VisualStateNarrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1"/>
</VisualState.StateTriggers>
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800"/>
</VisualState.StateTriggers>
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Pivot x:Name="PivotPlatform" Margin="0" ItemsSource="{Binding PivotItems}" Grid.Row="2">
<Pivot.HeaderTemplate>
<DataTemplate>
<StackPanel Height="0" Width="0">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate>
<Grid xmlns:uwp="using:AmazingPullToRefresh.Controls">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<uwp:PullToRefreshAdorner.Extender>
<uwp:PullToRefreshExtender RefreshRequested="PullToRefreshExtender_RefreshRequested" />
</uwp:PullToRefreshAdorner.Extender>
<RelativePanel x:Name="contentPanel" Grid.Row="0" Margin="10 -30 10 10">
<TextBlock Name="titleTB" Text="{Binding Title}" FontSize="12"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignTopWithPanel="True"/>
<TextBlock Name="totalTB" Text="{Binding Total}" FontSize="18"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.Below="titleTB" />
<ProgressBar Name="progressBar" Value="{Binding ProgressValue}" Width="100" Foreground="{StaticResource currentThemeColor}"
RelativePanel.AlignLeftWithPanel="True" RelativePanel.Below="totalTB"
Margin="0 5 0 0"/>
<TextBlock Name="dateTB" Text="{Binding Date}" FontSize="16"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignTopWithPanel="True" />
</RelativePanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<Charting:Chart Grid.Row="1" x:Name="LineChart"
Margin="10" >
<Charting:LineSeries Title="" IndependentValuePath="Name" DependentValuePath="Amount"
IsSelectionEnabled="False" ItemsSource="{Binding Result}" />
</Charting:Chart>
</ScrollViewer>
</Grid>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
When I add setter for dateTB textblock into VisualState.Setters to move it to left side of Relative Panel, I get an error saying:
An animation is trying to modify an object named 'dateTB', but no such object can be found in the Page.
Code for adding the setter is:
<Setter Target="dateTB.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
Is there a way to control this textblock through Visual State Manager with this page structure?
It's a problem of name scopes, common to all XAML UI frameworks. Your VSM is in the name scope of your UserControl or Page and the TextBlock is in a DataTemplate.
Romasz's solution to your problem to put the VSM inside of the DataTemplate puts everything you need in a single name scope and is the best solution to this problem.

UWP Adaptive UI Code not working as expected

I'm trying to resize an image and a FontSize of a textblock based on the window width. My code is as shown below:
<DataTemplate x:Name="TestItemTemplate" x:DataType="data:TestItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Default" >
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="340" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ChannelImage.Height" Value="80" />
<Setter Target="ChannelImage.Width" Value="80" />
<Setter Target="CategoryTitle.FontSize" Value="16" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<RelativePanel Margin="5,5,5,5">
<Image x:Name="ChannelImage" Source="{Binding assetName}" HorizontalAlignment="Center"/>
<TextBlock Foreground="Black" FontSize="14" RelativePanel.Below="ChannelImage" RelativePanel.AlignHorizontalCenterWith="ChannelImage"
Typography.Capitals="AllSmallCaps" x:Name="CategoryTitle" Text="{Binding itemName}" HorizontalAlignment="Center"/>
</RelativePanel>
</Grid>
</DataTemplate>
Here's the problems I'm having.
I've tried using different phone emulators with resolutions whose width is more than 340 but the resolution of ChannelImage simple doesn't scale to 80x80 epx.
I've also tried running the desktop version of the program. The image doesn't scale to 80x80 BUT as soon as I start resizing the window it gets way bigger than 80x80 and continues to grow as the window is widened further.
I would really appreciate if someone could point out what I'm doing wrong.
You need to wrap your datatemplate in UserControl like this:
<DataTemplate x:Name="TestItemTemplate" x:DataType="data:TestItem">
<UserControl>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Default" >
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="340" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ChannelImage.Height" Value="80" />
<Setter Target="ChannelImage.Width" Value="80" />
<Setter Target="CategoryTitle.FontSize" Value="16" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<RelativePanel Margin="5,5,5,5">
<Image x:Name="ChannelImage" Source="{Binding assetName}" HorizontalAlignment="Center"/>
<TextBlock Foreground="Black" FontSize="14" RelativePanel.Below="ChannelImage" RelativePanel.AlignHorizontalCenterWith="ChannelImage"
Typography.Capitals="AllSmallCaps" x:Name="CategoryTitle" Text="{Binding itemName}" HorizontalAlignment="Center"/>
</RelativePanel>
</Grid>
</UserControl>
</DataTemplate>

2 different stackpanels in a BottomAppBar Universal App

I am trying to put the elements in my BottomAppBar like that:
with this code I get the two stackpanels in the left,can you correct please my code:
<Page.BottomAppBar>
<CommandBar Background="#eff0f2">
<CommandBar.Content>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch">
<Image Source="images/1.png" Height="35" Width="35" />
<ComboBox Margin="2" BorderThickness="0" SelectedItem="Français">
<ComboBoxItem Content="Français" />
<ComboBoxItem Content="Anglais" />
</ComboBox>
<Button VerticalAlignment="Stretch" Background="#eff0f2" >
<Button.Content>
<TextBlock Text="Conditions des données" Foreground="#336699"></TextBlock>
</Button.Content>
</Button>
<Button VerticalAlignment="Stretch" Background="#eff0f2" Margin="0">
<TextBlock Text="-Mentions légales" Foreground="#336699"></TextBlock>
</Button>
<Button VerticalAlignment="Stretch" Background="#eff0f2" Margin="0">
<TextBlock Text="-Impressum" Foreground="#336699"></TextBlock>
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="1" >
<Image Source="images/marque.png" Height="35" Width="35" />
<TextBlock Text="2015 OAASE Corporation|All Right Reserved." Foreground="#666666" Margin="10" />
</StackPanel>
</Grid>
</CommandBar.Content>
</CommandBar>
</Page.BottomAppBar>
thanks for help :)
You just have to add Stretch HorizontalContentAlignment to CommandBar :
<CommandBar HorizontalContentAlignment="Stretch">
EDIT:
#user3821206, a quick fix for your problem of responsiveness is to set a minimum width to the first column, and set the width of the second column to Auto. When the screen is too small, the right part is cropped :
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="550" Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
A better and complementary fix is to use VisualStateManager and AdaptiveTrigger. For example, you can hide the right panel if the screen size is less than 720. To do that, give a name to your right Stackpanel :
<StackPanel x:Name="RightPanel"
And add the following snippet in the first container of your page (for me, it's a Grid) :
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<!-- Start of snippet -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<!-- Edit style for small screen -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RightPanel.Visibility"
Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState>
<!-- Disable previous changes for large screen -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- End of snippet -->
</Grid>

How to dynamic change ItemTemplate width and height in ListView UWP?

I have a ListView like this
<ListView Name="lvTrailers"
Grid.Row="3"
SelectionChanged="lvTrailers_SelectionChanged"
SizeChanged="lvTrailers_SizeChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Height="65" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2.5*" />
</Grid.ColumnDefinitions>
<Image HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Thumbnail}"
Stretch="UniformToFill" />
<TextBlock Grid.Column="1"
Margin="10,5"
Text="{Binding Title}"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private void lvTrailers_SizeChanged(object sender, SizeChangedEventArgs e)
{
// add some userful code
// not working
lvTrailers.ItemTemplate.SetValue(HeightProperty, e.NewSize.Height / 6);
}
In UWP apps users can resize window height and width so when it happen, I want to dynamic resize ListView ItemTemplate too. Any one could tell me how to do that?
You need to use AdaptiveTrigger. Here is an example to achieve that :
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<DataTemplate x:Key="MyCustomItemDataTemplate">
<UserControl>
<Grid x:Name="content"
Margin="8"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2.5*" />
</Grid.ColumnDefinitions>
<Image HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="ms-appx:///Assets/StoreLogo.png"
Stretch="Uniform" />
<TextBlock Grid.Column="1"
Margin="10,5"
Text="blabla"
VerticalAlignment="Center"
TextWrapping="Wrap" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Small">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="content.Height"
Value="30" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="content.Height"
Value="Auto" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>
</DataTemplate>
</Grid.Resources>
<ListView Name="items"
ItemTemplate="{StaticResource MyCustomItemDataTemplate}" />
</Grid>

XAML: align Ellipse with Uniform stretch to the center

I'm trying to align an Ellipse with Uniform Stretch to the center (both vertical and horizontal). But when I add HorizontalAlignment="Center" and/or VerticalAlignment="Center" to the Ellipse, it becomes invisible.
This is my XAML code:
<Grid Grid.Row="1" Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse x:Name="Ellipse_AccountImage" Grid.Row="0" Grid.Column="0" Stretch="Uniform">
<Ellipse.Fill>
<ImageBrush x:Name="ImageBrush_AccountImage"/>
</Ellipse.Fill>
</Ellipse>
<Grid x:Name="Grid_AccountInfo">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Text="Name:" Margin="0,0,12,0"/>
<TextBlock Grid.Row="0" Grid.Column="1" Style="{ThemeResource BodyTextBlockStyle}" x:Name="TextBlock_Name"/>
<TextBlock Grid.Row="1" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Text="Email:" Margin="0,0,12,0"/>
<TextBlock Grid.Row="1" Grid.Column="1" Style="{ThemeResource BodyTextBlockStyle}" x:Name="TextBlock_Email"/>
<TextBlock Grid.Row="2" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Text="Created:" Margin="0,0,12,0"/>
<TextBlock Grid.Row="2" Grid.Column="1" Style="{ThemeResource BodyTextBlockStyle}" x:Name="TextBlock_Created"/>
</Grid>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Ellipse_AccountImage.(Grid.RowSpan)" Value="2"/>
<Setter Target="Ellipse_AccountImage.Margin" Value="0,0,24,0"/>
<Setter Target="Grid_AccountInfo.(Grid.Row)" Value="0"/>
<Setter Target="Grid_AccountInfo.(Grid.Column)" Value="1"/>
<Setter Target="Grid_AccountInfo.(Grid.RowSpan)" Value="2"/>
<Setter Target="Grid_AccountInfo.VerticalAlignment" Value="Center"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Ellipse_AccountImage.(Grid.ColumnSpan)" Value="2"/>
<Setter Target="Ellipse_AccountImage.Margin" Value="0,0,0,24"/>
<Setter Target="Grid_AccountInfo.(Grid.Row)" Value="1"/>
<Setter Target="Grid_AccountInfo.(Grid.Column)" Value="0"/>
<Setter Target="Grid_AccountInfo.(Grid.ColumnSpan)" Value="2"/>
<Setter Target="Grid_AccountInfo.HorizontalAlignment" Value="Center"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
I already tried putting the Ellipse element in a ViewBox, Canvas or Grid but that doesn't solve the problem.
From the comments above it seemed that it isn't possible to solve this with XAML only. Instead I wrote a function that updates the width and height manually every time the page is resized (like igrali said). Thank your for the help!