Reputation: 315
I would like to display the properties in a list of objects, but if the properties don't have any values I'd like to not only leave it blank but have the entire row not there at all (that's key).
Here's what I mean: let's say I have an object with four string values:
object person:
string firstname
string lastname
string favoriteMovie
string favoriteBook
Actually, I have a list of them: ObservableList<Person>
. I want to display Person
and the properties in each Person using CollectionView
, only if the property doesn't have a value I want to skip it.
It might looks something like this:
The XAML code would look something like this:
<CollectionView ItemsSource={Binding People}>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Person">
<Frame>
<Grid>
<Label Text="{Binding Name}"/>
<Label Text="{Binding FavoriteMovie}"/>
<Label Text="{Binding FavoriteBook}"/>
...
...
...
...
</CollectionView>
How do I do that?
NOTE: I'm using MVVM pattern, and I'd like to mostly do this in XAML if possible.
Upvotes: 1
Views: 1057
Reputation: 1921
Here is one way do this.
First we do the Model that is responsive using the INotifyPropertyChanged
and a PropertyChangedEventHandler
, here we also put the boolean to use if we are to present the row or not.
public class Person : INotifyPropertyChanged
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FavoriteMovie { get; set; }
public string FavoriteBook { get; set; }
public string DisplayName => FirstName + " " + LastName;
public bool HasFavoriteMovie => !string.IsNullOrEmpty(FavoriteMovie);
public bool HasFavoriteBook => !string.IsNullOrEmpty(FavoriteBook);
public event PropertyChangedEventHandler PropertyChanged;
}
The xaml code for this to mimic the layou from your image. On the label/grid we have IsVisible
binded to the model boolean value.
<ContentPage.Resources>
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource Blue100Accent}" />
<Setter Property="FontSize" Value="18" />
<Setter Property="LineHeight" Value="1.2" />
</Style>
<Style x:Key="TitleLabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource Primary}" />
<Setter Property="FontSize" Value="28" />
<Setter Property="TextTransform" Value="Uppercase" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="LineHeight" Value="1.2" />
</Style>
<DataTemplate x:Key="PeopleTemplate">
<Border
Margin="20,10,10,10"
BackgroundColor="{StaticResource LightBackgroundColor}"
Stroke="Gray"
StrokeThickness="2">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<Border.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:PeopleViewModel}}, Path=PersonClickedCommand}" CommandParameter="{Binding .}" />
</Border.GestureRecognizers>
<Grid Padding="50,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Style="{StaticResource LabelStyle}"
Text="{Binding DisplayName}" />
<Label
Grid.Row="1"
IsVisible="{Binding HasFavoriteMovie}"
Style="{StaticResource LabelStyle}"
Text="{Binding FavoriteMovie}" />
<Label
Grid.Row="2"
IsVisible="{Binding HasFavoriteBook}"
Style="{StaticResource LabelStyle}"
Text="{Binding FavoriteBook}" />
</Grid>
</Border>
</DataTemplate>
</ContentPage.Resources>
<VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Style="{StaticResource TitleLabelStyle}" Text="People" />
<CollectionView
x:Name="PeopleCollection"
ItemSizingStrategy="MeasureFirstItem"
ItemTemplate="{StaticResource PeopleTemplate}"
ItemsSource="{Binding People}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
</CollectionView>
<Button
Command="{Binding AddPersonCommand}"
Text="Add Person"
WidthRequest="200" />
</VerticalStackLayout>
And finally in your ViewModel you have
public partial class PeopleViewModel : ObservableObject
{
public ObservableCollection<Person> People { get; set; }
public PeopleViewModel()
{
People = new ObservableCollection<Person>
{
new Person { FirstName = "John", LastName = "Doe", FavoriteMovie = "Inception", FavoriteBook = "1984" },
new Person { FirstName = "Jane", LastName = "Doe", FavoriteMovie = "Avatar" },
new Person { FirstName = "Michael", LastName = "Brown" }
};
}
[RelayCommand]
private async Task PersonClicked(Person person)
{
var navigationParameter = new Dictionary<string, object>
{
{ "Person", person }
};
await Shell.Current.GoToAsync($"{nameof(PersonPage)}", navigationParameter);
}
[RelayCommand]
private void AddPerson()
{
People.Add(new Person { FirstName = "Emily", LastName = "Smith", FavoriteBook = "To Kill a Mockingbird" });
}
}
This is what it looks like. I hope this is what you want.
Upvotes: 0
Reputation: 16562
You will need a Converter that converts empty or null strings to booleans and then you need to assign it to the visibility of your item so something like the below would work,
/// <summary>
/// Inherit this for one-way converters to avoid unnecessary extra methods in each converter
/// </summary>
public abstract class BaseOneWayValueConverter : IValueConverter
{
public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException($"{GetType().Name} is a one-way converter");
}
}
public class StringIsNullToBoolConverter : BaseOneWayValueConverter
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is not string text || !string.IsNullOrWhiteSpace(text);
}
}
And then use it in your Label as shown below:
<ContentPage
.
.
.
.
xmlns:converters=*converter-namepsace-here*
>
<ContentPage.Resources>
<StringIsNullToBoolConverter x:Key="StringIsNullToBoolConverter"/>
</ContentPage.Resources>
Then use it in your respective labels:
<Label Text="{Binding Name}" IsVisible="{Binding Name,Converter={StaticResource StringIsNullToBoolConverter}}"/>
Upvotes: 1