Reputation: 4811
I have a binded listview in UWP and I want to show or hide some controls inside the ItemTemplate based on some properties
The XAML i am using is here for binding ObservableCollection
<ListView x:Name="lvwMovieWatchlist" ItemsSource="{x:Bind Books}" Margin="10">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:BookModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Width="156" Height="200">
<Image Source="{Binding CoverImage}" Stretch="UniformToFill"/>
</Border>
<StackPanel Grid.Column="1" Orientation="Vertical">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Margin="9.6,0"/>
<TextBlock Text="{Binding Name}" Style="{StaticResource SubtitleTextBlockStyle}" TextWrapping="NoWrap" Margin="9.6,0"/>
<StackPanel Orientation="Horizontal" Margin="10">
<Button Name="Btn_Download" Tag="{Binding}" Click="Btn_Download_Click"> DOWNLOAD</Button>
<Button Name="Btn_Read" Tag="{Binding}" Click="Btn_Read_Click"> READ</Button>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The 2 buttons Btn_Download and Btn_Read are in question here If Status property is "read" then i want to set the visibility as Visible for Btn_Read otherwise show Btn_Download button
In WPF i have Trigger for achieveing the same like
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Status}" Value="read">
<Setter TargetName="Btn_Read" Property="Visibility" Value="Visible"/>
<Setter TargetName="Btn_Download" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
But in UWP there is no Trigger only VisualStateManager.VisualStateGroups is avaiable. So how can i make it using VisualStateGroup inside the ItemTemplate
Upvotes: 1
Views: 1693
Reputation: 436
An possible idea is to use a Converter instead of the VisualStateManager:
<StackPanel Orientation="Horizontal" Margin="10">
<Button Name="Btn_Download"
Tag="{Binding}"
Click="Btn_Download_Click"
Visibility={Binding Status, Converter={StaticResource StatusToDownloadVisibilityConverter}}>DOWNLOAD</Button>
<Button Name="Btn_Read"
Tag="{Binding}"
Click="Btn_Read_Click"
Visibility={Binding Status, Converter={StaticResource StatusToReadVisibilityConverter}}>READ</Button>
</StackPanel>
Just create 2 converters (StatusToDownloadVisibilityConverter and StatusToReadVisibilityConverter) that take a Status in parameter and returns a Visibility, according to the value of the Status property.
Maybe not 100% ideal solution but that should work!
Upvotes: 1
Reputation: 167
Wrap your template in a user control and add the visual state manager.
<ListView x:Name="lvwMovieWatchlist" ItemsSource="{x:Bind ViewModel.Books}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:BookModel">
<local:BookControl />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<UserControl
x:Class="App2.BookControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ButtonsVisibility">
<VisualState x:Name="ButtonsVisible">
</VisualState>
<VisualState x:Name="ButtonsHidden">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding Status, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Btn_Download.Visibility" Value="Collapsed" />
<Setter Target="Btn_Read.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Subtitle}" />
<StackPanel Orientation="Horizontal">
<Button Name="Btn_Download" Tag="{Binding}" Click="Btn_Download_Click">DOWNLOAD</Button>
<Button Name="Btn_Read" Tag="{Binding}" Click="Btn_Read_Click">READ</Button>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
This example uses the builtin StateTrigger on a boolean property. To compare a property to some value as you want to do use the EqualsStateTrigger from the awesome WindowsStateTriggers library (https://github.com/dotMorten/WindowsStateTriggers).
Upvotes: 0
Reputation: 29792
If you want to do it completely in XAML, you can do it with Interactivity behaviors extension. A sample using visual states and datatriggeraction can look like this - XAML:
<Grid xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:ic="using:Microsoft.Xaml.Interactions.Core" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ReadOrNot">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Read">
<Storyboard BeginTime="0:0:0" Duration="0:0:1">
<FadeOutThemeAnimation TargetName="BtnDownload"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<i:Interaction.Behaviors>
<i:BehaviorCollection>
<ic:DataTriggerBehavior Binding="{Binding Status}" Value="read">
<ic:GoToStateAction StateName="Read"/>
</ic:DataTriggerBehavior>
</i:BehaviorCollection>
</i:Interaction.Behaviors>
<Button Name="BtnDownload" HorizontalAlignment="Left" Content="Download" FontSize="20" Foreground="Red"/>
<Button HorizontalAlignment="Center" Content="Change property" FontSize="20" Foreground="Orange" Click="Button_Click"/>
<Button Name="BtnRead" HorizontalAlignment="Right" Content="Read" FontSize="20" Foreground="Green"/>
</Grid>
The code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private string status = "notRead";
public string Status
{
get { return status; }
set { status = value; RaiseProperty(nameof(Status)); }
}
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e) => Status = "read";
}
Button click changes status to read and triggers visual state change via behavior. You can do something similar for your item template. As a side note, you may also use other actions, what may be little easier depending on what you want to achieve - for example ChangePropertyAction.
Of course to make it work you have to add reference to Behaviors in your project (Add reference -> Universal windows -> Extensions) or by NuGet, what like igrali has mentioned, is a better option (open source and targetting UWP).
Upvotes: 1