XAML VisualStateManager and RelativePanel - xaml

I am loocking to do a responsive desing on my uwp project.
The idea is on PC get my StackPanel_ListViews below the StackPanel_CommandBar.
And on mobile get my StackPanel_CommandBar below the StackPanel_ListViews.
Both StackPanels are inside of a Pivot.
This is my code:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="Mobile">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StackPanel_CommandBar.(RelativePanel.Below)" Value="StackPanel_ListViews"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pc">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StackPanel_ListViews.(RelativePanel.Below)" Value="StackPanel_CommandBar"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel>
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignTopWithPanel="True" Text="Tittle">
</controls:PageHeader>
<Pivot x:Name="MainPivot">
<PivotItem>
<RelativePanel>
<StackPanel Orientation="Vertical" x:Name="StackPanel_CommandBar" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
</StackPanel>
<StackPanel Orientation="Vertical" x:Name="StackPanel_ListViews" RelativePanel.Below="StackPanel_CommandBar" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
</StackPanel>
</RelativePanel>
</PivotItem>
</Pivot>
</StackPanel>
StackPanel_ListViews is a stackpanel contains 2 ListViews.
StackPanel_CommandBar is a stackpanel contains a CommandBar.
Using this code I have no results, and using a above and below I am geting a circular error.
What I am doing wrong, how I can get it work as I say above?
Any help is appreciated.

I think you've made a tiny mistake here by your second StackPanel "StackPanel_ListViews".
You've set RelativePanel.Below="StackPanel_CommandBar" at first, but when the layout is rendered, at the very beginning, the window's width is 0, this will cause conflicts with the Setter in your <VisualState x:Name="Mobile">, and throw an exception. You can just remove this RelativePanel.Below="StackPanel_CommandBar" code and it will works fine.

Related

VisualStateManager not working inside Button.ContentTemplate UWP

I have been struggling with this for a few days now unable to find solution after lot of effort. This is the code where I'm facing the issue. SO I have a ItemsControl where each element is Button and each Button has an Image and TextBlock. On hovering over Button I could see the Background of Button being changed to Red as expected. But I'm unable to change the Foreground of TextBlock to Green. Any help in this regard is highly appreciated
<ControlTemplate x:Key="ButtonTemplate" TargetType="ContentControl">
<Grid Background="Transparent" x:Name="Mini">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Mini.Background" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="MiniContent" />
</Grid>
</ControlTemplate>
<DataTemplate x:Key="ListItemTemplate" x:DataType="local:DataModel">
<Button
Template="{StaticResource ButtonTemplate}">
<Button.ContentTemplate>
<DataTemplate x:DataType="local:DataModel">
<UserControl>
<Grid >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Value.Foreground" Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="Value"
Foreground="Yellow"
Text="{x:Bind DisplayName, Mode=OneWay}"/>
<Image
Width="16"
Source="{x:Bind ImageBitmap, Mode=OneWay}"/>
</Grid>
</UserControl>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</DataTemplate>
<ItemsControl
ItemTemplate="{StaticResource ListItemTemplate}"
ItemsSource="{x:Bind DataModelVector, Mode=OneWay}" />
You need something to actually transition between the VisualStates, the PointerOver state isn't triggered automatically. The easiest way would be to factor the content of your DataTemplate into its own file (UserControl with markup and code-behind) and handle pointer events in code to transition between visual states using VisualStateManager.GoToState.
You could also create a reusable "StateTrigger" for transitioning any control into a "PointerOver" visual state, but often some code-behind is simpler.

Adaptive trigger doesn't work in a user control

I'm working on a UWP app. I'm already using adaptive triggers to adapt my xaml depending on the width of the window, and it works.. only in a Page.
Now I want to do the same for the xaml of a User Control, and it's not working .. Yet I put the VisualStateManager to root grid of the user control.
Is there a difference ?
Here's the code of my user control :
<UserControl
x:Class=.....>
<UserControl.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Path="MyUserControlVM" Source="{StaticResource Locator}"/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="white">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="LastName.Fontsize" Value="10" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="LastName.Fontsize" Value="25" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Listbox>
...
</ListBox>
<TextBlock x:Name="lblNoData" Grid.ColumnSpan="2" Text="No Data" Visibility="{Binding NoDataVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ProgressRing x:Name="prLoading" Width="60" Height="60" Foreground="Blue" IsActive="{Binding InitializationNotifier.IsNotCompleted}" />
</Grid>
("LastName" and "FirstName" are textblocks in the DataTemplate of my listbox. I'm just trying to put my text in red in order to see when the triggers work)
And I call the user control in a simple page like this :
<Grid x:Name="LayoutRoot" Background="{StaticResource white}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="0,0,0,10">
...
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="10,0">
<UC:MyUserControl/>
</Grid>
</Grid>
I don't understand why this code won't work. Thank you in advance for your help !
("LastName" and "FirstName" are textblocks in the DataTemplate of my listbox. I'm just trying to put my text in red in order to see when the triggers work)
You can refer to my another answer here, as I said in that answer, when the controls are placed in the DataTemplate, they becomes the visual structure of your data objects. I think there is no clean way to do this work only in the xaml code, Data Binding here is your friend.
From your code I can see that you want to change the Foreground (but all to Red?) and the FontSize of the TextBlocks inside of the DataTemplate depending on the window's size. So you can bind these two proprieties to the window's size, or you can use ItemsControl.ItemTemplateSelector to select different template when the window's size is changed.

AdaptiveTrigger not work UWP

I have UWP app.
<GridView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="GridColections" IsItemClickEnabled="True" SelectionMode=" None " ItemsSource="{x:Bind DS.AllRem, Mode=OneWay}" ItemClick="GridColections_ItemClick" >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Small">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RemXML.Background" Value="Red" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Middle">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="400"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RemXML.Background" Value="Green"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Big">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="500"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RemXML.Background" Value="Yellow"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:GetRem">
<RelativePanel x:Name="RemXML" Width="345" Background="Cyan">
<TextBlock TextWrapping="Wrap" Text="{x:Bind ReminderName}" Margin="5,5,0,0" RelativePanel.AlignLeftWithPanel="True" FontSize="20" />
<TextBlock TextWrapping="Wrap" Text="{x:Bind ReminderDescription}" Margin="6,35,0,0" RelativePanel.AlignLeftWithPanel="True" FontSize="13.333"/>
<CheckBox RelativePanel.AlignRightWithPanel="True" Margin="100,0,-200,0" Width="220" RelativePanel.AlignVerticalCenterWithPanel="True">
<CheckBox.RenderTransform>
<CompositeTransform ScaleX="-1"/>
</CheckBox.RenderTransform>
</CheckBox>
</RelativePanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
So adaptive triggers don't work in my app. I need to change RelativePanel background. When Visual state I have in first control I have ERROR "An animation is trying to modify an object named 'RemXML', but no such object can be found in the Page." When Visual State I have in RelativePanel is don't work
Try putting the VisualStateManager inside of the DataTemplate which has a UserControl as top element.
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:GetRem">
<UserControl>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Small">
......
</VisualStateManager.VisualStateGroups>
<RelativePanel x:Name="RemXML" Width="345" Background="Cyan">

VisualState is not applied to listbox ItemTemplate

I want to use the VisualStateManager to change the appearance of my listbox items. I created a simplified example. When the listbox or listbox items got only space for width = 500 it should set for example the background to Beige otherwise to Green.
I tried the following and some other variations but neither of them worked. Has anyone a idea how to fix this?
<ListBox Grid.Column="0">
<ListBoxItem>asdfasf</ListBoxItem>
<ListBoxItem>fasf</ListBoxItem>
<ListBoxItem>fasf</ListBoxItem>
<ListBoxItem>asdsf</ListBoxItem>
<ListBoxItem>aasf</ListBoxItem>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="visualStateGroup" >
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="500" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="a" />
<Setter Target="border.Background" Value="Beige" />
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="b" />
<Setter Target="border.Background" Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl.Content>
<Border x:Name="border" >
<TextBlock x:Name="PathTextBlock" Text="{Binding RelativeSource={RelativeSource Mode=None}}" />
</Border>
</ContentControl.Content>
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I tried to adopt:
https://stackoverflow.com/a/32092547/740651
The easiest way I find to do this is to create a UserControl of your DataTemplate code. So something like this
<UserControl x:class="MyListBoxControl">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="visualStateGroup" >
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="500" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="a" />
<Setter Target="border.Background" Value="Beige" />
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="b" />
<Setter Target="border.Background" Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="border" >
<TextBlock x:Name="PathTextBlock" Text="{Binding RelativeSource={RelativeSource Mode=None}}" />
</Border>
</Grid>
</UserControl>
and then add to your ListBox ItemTemplate. The Visual States will then work accordingly. Note you will need to create a reference to where your UserControls are stored at the top of your page something like xmlns:myUserControls="[location of your controls]"
<ListBox Grid.Column="0">
<ListBoxItem>asdfasf</ListBoxItem>
<ListBoxItem>fasf</ListBoxItem>
<ListBoxItem>fasf</ListBoxItem>
<ListBoxItem>asdsf</ListBoxItem>
<ListBoxItem>aasf</ListBoxItem>
<ListBox.ItemTemplate>
<DataTemplate>
<myUserControls:MyListBoxControl />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Usually we don’t manipulate the ListBox's item directly, the common way is adding the items to the ObservableCollection and set ItemSource property of ListBox to bind to the ObservableCollection.
If you use the ItemSource, you can use the UserControl like #SWilko said. But it does not need to use the RelativeSource.
For example, I create a Person class, and it has a property Name. I use <TextBlock x:Name="PathTextBlock" Text="{Binding name}" /> replace <TextBlock x:Name="PathTextBlock" Text="{Binding RelativeSource={RelativeSource Mode=None}}" />.
If you want to add the ListBox’s item directly, and use the VisualStateManager to change the appearance of the ListBox items. You can edit the ItemContainerStyle.
To modify the template of ListBoxItem, we can select the ListBox in "Document Outline" and right click, then select "Edit Additional Templates"→ "Edit Generated Item Container (ItemContainerStyle)" → "Edit a Copy...".
You can create a VisualStateGroup into the <VisualStateManager.VisualStateGroups>.
For example:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
...
</VisualStateGroup>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="a" />
<Setter Target="border.Background" Value="Beige" />
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="PathTextBlock.Text" Value="b" />
<Setter Target="border.Background" Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
And you can edit the ContentPresenterlike:
<ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Style="{StaticResource BodyContentPresenterStyle}" TextWrapping="NoWrap" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Border Name="border">
<TextBlock Name="PathTextBlock" Text="{TemplateBinding Content}"></TextBlock>
</Border>
</ContentPresenter>
By the way, you set the MinWindowWidth property to 500 of AdaptiveTrigger, the effect will not obvious. You can set it to 600.

Drag-and-Drop UWP vs Button Style

I have a question that is me to leave upset. I want to drag and drop an item in a listview and I can not do when I have a style applied to my item. Only I can do and is working perfectly when I have no style (MyButtonStyle) applied / or do not have this image in style. When I have style (MyButtonStyle), ItemDragStarting event is not called.
Another situation: I have tapped associated event, and when I apply this style crashes. I do not understand what the problem is, can someone help me?
Thank you:
Code MainPage:
<ListView x:Name="MyListView" ItemsSource="{x:Bind _ObservableCollection}" Style="{StaticResource MyListViewStyle}" SelectionMode="None" CanDragItems="True" DragItemsStarting="MyListView_OnDragItemsStarting">
<ListView.ItemTemplate>
<DataTemplate>
<Button Tapped="Item_Tapped" Style="{StaticResource MyButtonStyle}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
XAML Style code:
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource MyColor1}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonContent" To="{StaticResource MyColor2}"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Duration="00:00:00.1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<PointerDownThemeAnimation TargetName="ButtonContent"/>
<ColorAnimation Storyboard.TargetName="ButtonContent" To="{StaticResource MyColor3}"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Duration="00:00:00.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Width="200" Height="200">
<Image.Source>
<BitmapImage UriSource="{Binding MyImage, Mode=OneTime}" />
</Image.Source>
</Image>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The best solution for you would be to not use a Button at all. Use IsItemClickEnabled property and ItemClick event on the ListView instead, then put your image in the ItemContainerStyle. It will fix your drag and drop issues, focus problems and result in better performance.