Build together, debug together. Join the community on Discord.→

XAML Fundamentals for Web and Mobile: Three-level List/Detail Pattern

Opportunity instead of obsoletion

Users increasingly expect an intuitive way to drill into your data source and inspect the contents of an individual element. It’s often necessary to leverage a pattern that consistently connects the intentioned person behind the screen to the result they expect. Likewise, a nontrivial part of modernizing your .NET app not only lies in rethinking it for the web but in challenging past assumptions about the user. In a multi-platform world, significant opportunities exist to bolster engagement and familiarity with the information they seek. That’s why developers using WinUI XAML on Uno Platform are well-positioned to enhance their app’s UI to enable faster parsing through a data source, more intuitive location of data items, and a responsive layout that scales to the current device.

Understanding the pattern

Users increasingly expect an intuitive way to drill into your data source and inspect the contents of an individual element. It’s often necessary to leverage a pattern that consistently connects the intentioned person behind the screen to the result they expect. Likewise, a nontrivial part of modernizing your .NET app not only lies in rethinking it for the web but in challenging past assumptions about the user. In a multi-platform world, significant opportunities exist to bolster engagement and familiarity with the information they seek. That’s why developers using WinUI XAML on Uno Platform are well-positioned to enhance their app’s UI to enable faster parsing through a data source, more intuitive location of data items, and a responsive layout that scales to the current device.

For instance, users of an email client typically access messages sorted by the date received. While we can assign and group nearly any type of data with properties, it would make little sense for users to drill down multiple levels, such as by a sender name or priority, just to find an email. Your application scenario may not be as clear-cut, but you should weigh how specific you believe the user’s intent is against the standard behavior for your app type. Complex data sources containing related items, yet a rich set of uncommon properties should employ a three-level list/detail design instead.

Having the intermediate level allows you to display a filtered copy of your data source collection depending on what’s selected in the starting list such as the sample demonstrated:

Understanding the Underlying Data Sources

The three-level pattern requires us to delineate between the first level and second level types of items. For instance, our sample application has the `WorkRequestItem`s as being the first level, and multiple `AttachmentItem`s associated with them. Each `AttachmentItem` has more specific properties like a photo. Above, the new shower head photo is tightly related to both the `AttachmentItem` entry describing it, and the selected work request ticket which happens to contain multiple other related `AttachmentItem`s

				
					private static List<WorkRequestItem> _items = new List<WorkRequestItem>()
        {
            new WorkRequestItem()
            {
                Id = 0,
                DateAdded = DateTime.Now.AddDays(new Random().Next(1, 31)),
                Title = "My shower makes an awful loud noise",
                Description = @"My shower has been noisy since I moved in.",
                AttachmentItems = new List<AttachmentItem>()
                {
                    new AttachmentItem()
                    {
                        Title = "ORDER PRODUCT: Modern rain shower faucet",
                        DateAdded = DateTime.Now.AddDays(new Random().Next(1, 31)),
                        Photo = new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri(@"ms-appx:///Assets/showerhe.jpg"))
                    },
                    new AttachmentItem()
                    {
                        Title = "PHOTO: Damaged faucet",
                        DateAdded = DateTime.Now.AddDays(new Random().Next(1, 31)),
                        Photo = new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri(@"ms-appx:///Assets/pipesinsp.jpg"))
                    },
                    new AttachmentItem()
                    {
                        Title = "PHOTO: Water heater inspection",
                        DateAdded = DateTime.Now.AddDays(new Random().Next(1, 31)),
                        Photo = new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri(@"ms-appx:///Assets/waterheat.jpg"))
                    },
                }
            },

				
			

Representing Both Item Types in the UI

Since the purpose of our hypothetical application’s user experience is to provide technicians with simplified access to work request attachment metadata, we must represent graphically both this destination and steps to get there. To do so, we construct pages for the attachment item metadata—our destination—and the intermediate step where the user interacts with `ListView` to select the desired work request and relevant attachment. The metadata for an attachment item could consist of critical details like a photo, title, description, and date created.

				
					<Page
	x:Class="ThreeLevelListDetailsSample.DetailsPage"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="using:ThreeLevelListDetailsSample"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

	<Grid
		Margin="12,0,12,12">
		<StackPanel
			Spacing="8">
			<TextBlock
				Style="{StaticResource SubtitleTextBlockStyle}"
				Text="Attachment Details" />
			<Image
				Height="256"
				x:Name="DetailsImage"
				Stretch="UniformToFill"
				Source="{x:Bind Item.Photo, Mode=OneWay}" />
			<TextBlock
				Margin="8,0"
				FontSize="12"
				HorizontalAlignment="Left"
				TextWrapping="WrapWholeWords"
				Style="{StaticResource BodyTextBlockStyle}"
				Text="{x:Bind Item.Title, Mode=OneWay}" />

		</StackPanel>
	</Grid>
</Page>

However, notice that the attachment items themselves are represented by `ListViewItem`s in a similar way as top-level work request items
<Page
	x:Class="ThreeLevelListDetailsSample.ListPage"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="using:ThreeLevelListDetailsSample"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	xmlns:data="using:ThreeLevelListDetailsSample.Data"
	Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

	<Grid>
		<Grid.ColumnDefinitions>
			<ColumnDefinition
				Width="325" />
			<ColumnDefinition
				Width="*" />
		</Grid.ColumnDefinitions>
		<ListView
			x:Name="AttachmentsListView"
			SelectionMode="Single"
			SelectionChanged="ListPage_SelectionChanged"
			Grid.Column="0"
			ItemsSource="{x:Bind Items, Mode=OneWay}">
			<ListView.ItemTemplate>
				<DataTemplate
					x:DataType="data:AttachmentItem">
					<Grid
						ColumnSpacing="8"
						RowSpacing="8"
						Margin="12">
						<Grid.RowDefinitions>
							<RowDefinition
								Height="Auto" />
							<RowDefinition
								Height="Auto" />
						</Grid.RowDefinitions>

						<Grid.ColumnDefinitions>
							<ColumnDefinition
								Width="Auto" />
							<ColumnDefinition
								Width="*" />
						</Grid.ColumnDefinitions>

						<FontIcon
							Grid.RowSpan="2"
							FontSize="48"
							Glyph="&#xE723;" />

						<TextBlock
							Grid.Column="1"
							Text="{x:Bind Title, Mode=OneWay}"
							TextWrapping="WrapWholeWords" />

						<TextBlock
							Grid.Column="1"
							Text="{x:Bind DateAdded, Mode=OneWay}"
							Grid.Row="1" />
					</Grid>
				</DataTemplate>
			</ListView.ItemTemplate>
		</ListView>

		<local:DetailsPage
			x:FieldModifier="public"
			x:Name="AttachmentViewerPage"
			Grid.Column="1" />
	</Grid>
</Page>

				
			

Putting the Three-Level List/Detail Pattern Together

We use nested `Pages` to set up the multiple levels of the list/detail UI. The first `ListView` has its `ItemsSource` property set to a collection of the broad filters which we populate from code behind. In our case, we filter attachments by the maintenance work request item they’re associated with. The ListPage and DetailsPage have knowledge of what to populate their UI with because of a DependencyProperty on both that gets a value from the previous level’s selection.

				
					<Page
	x:Class="ThreeLevelListDetailsSample.MainPage"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="using:ThreeLevelListDetailsSample"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	xmlns:data="using:ThreeLevelListDetailsSample.Data"
	Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition
				Height="Auto" />
			<RowDefinition
				Height="*" />
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition
				Width="400" />
			<ColumnDefinition
				Width="*" />
		</Grid.ColumnDefinitions>
		<StackPanel
			Margin="12"
			Spacing="12"
			Grid.ColumnSpan="2"
			Orientation="Horizontal">
			<FontIcon
				FontSize="42"
				Glyph="&#xE90F;" />
			<TextBlock
				VerticalAlignment="Center"
				TextAlignment="Center"
				Text="Work Request Tracker"
				Style="{StaticResource TitleTextBlockStyle}" />
		</StackPanel>

		<ListView
			x:Name="WorkRequestItemsListView"
			SelectionMode="Single"
			SelectionChanged="MainPage_SelectionChanged"
			Grid.Column="0"
			Grid.Row="1"
			ItemsSource="{x:Bind Items, Mode=OneWay}">
			<ListView.ItemTemplate>
				<DataTemplate
					x:DataType="data:WorkRequestItem">
					<Grid
						RowSpacing="8"
						Margin="12">
						<Grid.RowDefinitions>
							<RowDefinition
								Height="Auto" />
							<RowDefinition
								Height="Auto" />
						</Grid.RowDefinitions>

						<Grid.ColumnDefinitions>
							<ColumnDefinition
								Width="*" />
							<ColumnDefinition
								Width="Auto" />
						</Grid.ColumnDefinitions>

						<TextBlock
							Text="{x:Bind Title, Mode=OneWay}"
							TextWrapping="WrapWholeWords" />

						<TextBlock
							Text="{x:Bind DateAdded, Mode=OneWay}"
							Grid.Row="1" />
					</Grid>
				</DataTemplate>
			</ListView.ItemTemplate>
		</ListView>

		<local:ListPage
			x:Name="ListViewPage"
			Grid.Row="1"
			Grid.Column="1" />
	</Grid>
</Page>

				
			

Wrapping Up

Now, when the user selects a work request item, a list of attachments relevant to this selected top level data source item are displayed in between the starting `ListView` and your content viewer control. Users can now make it to the same result faster than scrolling through a single list. This widely accepted pattern supports spatial memory which will boost repeat engagement with your application’s content.

About Uno Platform

For those new to Uno Platform – it allows for creation of pixel-perfect, single-source C# and XAML apps which run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. It offers Figma integration for design-development handoff, and a set of extensions to bootstrap your projects. Uno Platform is free and Open Source (Apache 2.0) and available on GitHub.

Next Steps

Want to start creating cross-platform applications with XAML and C#? If you are new to Uno Platform, the best way to get started is to follow our official getting started guide. (5 min to complete) To upgrade to the latest release of Uno Platform, please update your packages to 4.3 via your Visual Studio NuGet package manager! 

Tags:

Share this post:

Uno Platform 5.2 LIVE Webinar – Today at 3 PM EST – Watch