Assume your application needs to display a list of tourist attractions in a city on a vertical list based on the city selected from a horizontal list. Here comes the Syncfusion .NET MAUI ListView to help you easily implement this in your .NET MAUI application.
The Syncfusion .NET MAUI ListView control is a list-like interface that renders a set of data in vertical and horizontal orientation with easy customization.
By customizing the Orientation property of the .NET MAUI ListView, you can easily configure the ListView to be horizontal or vertical.
Let’s get started with the development of this application.
As we know, the .NET MAUI ListView is a data-bound control, and we must create a data model to bind to the ListView.
Create a model class to hold the data values, such as the place’s name, image, and collection, which holds data for the vertical list.
Refer to the following code example.
public class PlaceInfo : INotifyPropertyChanged { #region Fields private string? name; private string? description; private ImageSource? image; private ObservableCollection<PlaceInfo> touristPlaces; #endregion #region Constructor public PlaceInfo() { } #endregion #region Properties public string? Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } } public string? Description { get { return description; } set { description = value; OnPropertyChanged("Description"); } } public ImageSource? Image { get { return image; } set { image = value; OnPropertyChanged("Image"); } } public ObservableCollection<PlaceInfo> TouristPlaces { get { return touristPlaces; } set { touristPlaces = value; OnPropertyChanged("TouristPlaces"); } } #endregion #region Interface Member public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged(string name) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(name)); } #endregion }
Create an ObservableCollection of PlaceInfo named Places to hold the data for the horizontal ListView. Each PlaceInfo will have a collection of PlaceInfo named TouristPlaces, which holds data for the vertical list to show the travel places.
Refer to the following code.
public class ListViewOrientationViewModel : INotifyPropertyChanged { #region Fields private ObservableCollection<PlaceInfo>? places; private PlaceInfo selectedItem; #endregion #region Constructor public ListViewOrientationViewModel() { var placesRepository = new PlaceInfoRepository(); Places = placesRepository.GeneratePlaces(); SelectedItem = Places[0]; } #endregion #region Properties public PlaceInfo SelectedItem { get { return selectedItem; } set { selectedItem = value; OnPropertyChanged("SelectedItem"); } } public ObservableCollection<PlaceInfo>? Places { get { return places; } set { this.places = value; OnPropertyChanged("Places"); } } #endregion #region INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } #endregion }
Now, bind the ViewModel’s Places collection to the horizontal ListView control on the XAML page.
<ContentPage x:Class="ListViewMaui.HorizontalOrientation" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:ListViewMaui" xmlns:ListView="clr-namespace:Syncfusion.Maui.ListView;assembly=Syncfusion.Maui.ListView" BackgroundColor="White"> <ContentPage.BindingContext> <local:ListViewOrientationViewModel x:Name="viewModel"/> </ContentPage.BindingContext> <ListView:SfListView x:Name="listView" Grid.Row="1" ItemsSource="{Binding Places}" Orientation="Horizontal"> </ListView:SfListView> </ContentPage.Content> </ContentPage>
Once the ItemsSource is bound, the ListView control will display only the business objects (like PlaceInfo) as the content of the ListView items. We must update them by defining the ItemTemplate.
By defining the ItemTemplate, you can easily set custom views to display the data items.
Refer to the following code.
<ListView:SfListView x:Name="listView" Grid.Row="1" ItemsSource="{Binding Places}" ScrollBarVisibility="Never" SelectionMode="Single" Orientation="Horizontal" SelectionBackground="Transparent" ItemSize="{OnPlatform Android=120,Default=130}" Margin="8,0,0,0" HeightRequest="180" SelectedItem="{Binding SelectedItem}"> <ListView:SfListView.ItemTemplate> <DataTemplate> <Grid Margin="8,0,8,0"> <Grid.RowDefinitions> <RowDefinition Height="130"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Border Padding='{OnPlatform UWP="-5,-5,5,5"}' Stroke="#FFFDFD" StrokeThickness="6" HorizontalOptions="Center"> <Border.StrokeShape> <RoundRectangle CornerRadius="6"/> </Border.StrokeShape> <Image Grid.Row="0" Source="{Binding Image}" HeightRequest="130" WidthRequest="110" Aspect="Fill" Margin='{OnPlatform MacCatalyst=-3,iOS=-3}'/> </Border> <Label Grid.Row="1" Text="{Binding Name}" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalTextAlignment="Center" FontFamily="Roboto-Regular" VerticalOptions="Center" HeightRequest="40" WidthRequest="110" TextColor="#99000000" FontSize="14"> </Label> </Grid> </DataTemplate> </ListView:SfListView.ItemTemplate> <ListView:SfListView.SelectedItemTemplate> <DataTemplate> <Grid Margin="8,0,8,0"> <Grid.RowDefinitions> <RowDefinition Height="130"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Border Padding='{OnPlatform UWP="-5,-5,5,5"}' Stroke="#1899EF" StrokeThickness="6" HorizontalOptions="Center"> <Border.StrokeShape> <RoundRectangle CornerRadius="6"/> </Border.StrokeShape> <Image Grid.Row="0" Source="{Binding Image}" HeightRequest="130" WidthRequest="110" Aspect="Fill" Margin='{OnPlatform MacCatalyst=-3,iOS=-3}'/> </Border> <Label Grid.Row="1" Text="{Binding Name}" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalTextAlignment="Center" FontFamily="Roboto-Regular" VerticalOptions="Center" HeightRequest="40" WidthRequest="110" TextColor="#99000000" FontSize="14"> </Label> </Grid> </DataTemplate> </ListView:SfListView.SelectedItemTemplate> </ListView:SfListView>
Now the application will look like the following screenshot.
Now, selecting a city from the horizontal list will display the list of travel destinations by changing the ViewModel’s SelectedItem, which is bound to the horizontal ListView’s SelectedItem by using the bound property ItemsSource in the vertical ListView.
<ListView:SfListView x:Name="verticalListView" Grid.Row="3" ItemsSource="{Binding Path=SelectedItem.TouristPlaces, Source={x:Reference listView}}" ItemSize="60" ItemSpacing="16,8,16,8" SelectionMode="None"/>
Here, we are showing the details of travel places by using their name, description, and image in custom views defined within the ItemTemplate property.
<ListView:SfListView x:Name="verticalListView" Grid.Row="3" ItemsSource="{Binding Path=SelectedItem.TouristPlaces, Source={x:Reference listView}}" ItemSize="60" ItemSpacing="16,8,16,8" SelectionMode="None"> <ListView:SfListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="76"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Frame HorizontalOptions="Start" CornerRadius="3" IsClippedToBounds="True" Padding="0" Margin="0" HasShadow="False" HeightRequest="60" WidthRequest="60"> <Image Grid.Column="0" HorizontalOptions="Start" Source="{Binding Image}" Aspect="Fill" HeightRequest="60" WidthRequest="60"/> </Frame> <Grid Grid.Column="1" VerticalOptions="Center"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Label Grid.Row="0" Text="{Binding Name}" FontSize="14" TextColor="#666666" FontFamily="Roboto-Regular" CharacterSpacing="0.25"/> <Label Grid.Row="1" Text="{Binding Description}" FontSize="14" FontFamily="Roboto-Regular" TextColor="#DE000000" LineBreakMode="WordWrap" CharacterSpacing="0.15" Margin="0,5,0,0"/> </Grid> </Grid> </DataTemplate> </ListView:SfListView.ItemTemplate> </ListView:SfListView>
After executing this code example, we will get output like in the following GIF image. Tapping on the items in the horizontal ListView dynamically updates the content of the vertical ListView.
For more details, refer to the complete sample of Travel Place Listing UI in .NET MAUI in the GitHub repository.
Thanks for reading! I hope you now have a good idea of how to use the .NET MAUI ListView to display tourist places in a vertical list based on the city selected from a horizontal list. Try creating this sample project and share your feedback in the comment section below.
For questions, contact us through our support forum, support portal, or feedback portal. We are always happy to assist you!