Easily Develop a Travel Destination Listing UI in .NET MAUI

jollenmoyani

Jollen Moyani

Posted on February 2, 2023

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"/>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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!

Related blogs

💖 💪 🙅 🚩
jollenmoyani
Jollen Moyani

Posted on February 2, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related