Easily Develop a Travel Destination Listing UI in .NET MAUI
Detailed Blog page Skeleton loader
Easily Develop a Travel Destination Listing UI in .NET MAUI

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.

Data population for a vertical and horizontal list

As we know, the .NET MAUI ListView is a data-bound control, and we must create a data model to bind to the ListView.

Creating a data model

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
}

Populate model collection in ViewModel

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>

Defining horizontal ListView’s ItemTemplate

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.

Defining horizontal ListView’s ItemTemplate in tourist destination UI

Data binding for vertical ListView

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"/>

Defining vertical ListView’s ItemTemplate

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.

Travel Destination Listing UI in .NET MAUI
Travel Destination Listing UI in .NET MAUI

Resources

For more details, refer to the complete sample of Travel Place Listing UI in .NET MAUI in the GitHub repository.

Conclusion

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!

Test Flight
App Center Badge
Google Play Store Badge
Microsoft Badge
Github Store Badge

Related blogs

Be the first to get updates

Jayaleshwari N

Meet the Author

Jayaleshwari N

Jayaleshwari N works for Syncfusion as a product manager. She has been a .NET developer since 2013, and has experience in the development of custom controls in Xamarin and MAUI platforms.