Module 9 - FeedView none and error templates

In the previous module, you've observed how the view doesn't have a special reaction to a service-response that contains no results. In addition, you've also seen how the UI fails to indicate a connection error.
In this module, you'll learn about the power of the FeedView control, and how to use and customize its none (no data) and error templates.

Extract the item template into a separate method

  1. Open the MainPage.cs file and select and cut the entire lambda call contained in the ItemTemplate extension method of the ListView starting from youtubeVideo => ... (Ctrl+X on Windows). One way to identify the closing parenthesis is by placing the cursor on the opening parenthesis and then checking the keyboard cursor column position (on the bottom-right of the text editor), or by using the arrow to go in a straight line to identify the closing one.

  2. In the MainPage class, create a static method that returns a UIElement named VideoItemTemplate and paste the clipboard contents instead of its parameters parentheses, in the following signature:

    private static UIElement VideoItemTemplate(YoutubeVideo youtubeVideo)
    
  3. Paste the cut lambda and remove the old youtubeVideo parameter which is now duplicated.

  4. Add a closing semicolon (;) to terminate this method.

  5. Here's the final result:

    VideoItemTemplate method (collapsed for brevity).
    private static UIElement VideoItemTemplate(YoutubeVideo youtubeVideo) =>
        new CardContentControl()
            .Margin(0, 0, 0, 8)
            .Style(StaticResource.Get<Style>("ElevatedCardContentControlStyle"))
            .Content
            (
                new AutoLayout()
                    .Background(Theme.Brushes.Surface.Default)
                    .CornerRadius(12)
                    .PrimaryAxisAlignment(AutoLayoutAlignment.Center)
                    .Children
                    (
                        new AutoLayout()
                            .Background(Theme.Brushes.Surface.Default)
                            .CornerRadius(12)
                            .Padding(8, 8, 8, 0)
                            .MaxHeight(288)
                            .MaxWidth(456)
                            .AutoLayout(counterAlignment: AutoLayoutAlignment.Center)
                            .Children
                            (
                                new Border()
                                    .Height(204.75)
                                    .CornerRadius(6)
                                    .Child
                                    (
                                        new Image()
                                            .Source(() => youtubeVideo.Details.Snippet?.Thumbnails?.Medium?.Url!)
                                            .Stretch(Stretch.UniformToFill)
                                    ),
                                new AutoLayout()
                                    .Spacing(8)
                                    .Orientation(Orientation.Horizontal)
                                    .Padding(0, 8)
                                    .Children
                                    (
                                        new Border()
                                            .Width(60)
                                            .Height(60)
                                            .CornerRadius(6)
                                            .AutoLayout(counterAlignment: AutoLayoutAlignment.Center)
                                            .Child
                                            (
                                                new Image()
                                                    .Source(() => youtubeVideo.Channel.Snippet?.Thumbnails?.Medium?.Url!)
                                                    .Stretch(Stretch.UniformToFill)
                                            ),
                                        new AutoLayout()
                                            .PrimaryAxisAlignment(AutoLayoutAlignment.Center)
                                            .AutoLayout(primaryAlignment: AutoLayoutPrimaryAlignment.Stretch)
                                            .Children
                                            (
                                                new TextBlock()
                                                    .Text(() => youtubeVideo.Channel.Snippet?.Title)
                                                    .Height(22)
                                                    .Foreground(Theme.Brushes.OnSurface.Default)
                                                    .Style(Theme.TextBlock.Styles.TitleMedium),
                                                new TextBlock()
                                                    .Text(() => youtubeVideo.Details.Snippet?.Title)
                                                    .Height(16)
                                                    .Foreground(Theme.Brushes.OnSurface.Medium)
                                            ),
                                        new Button()
                                            .Foreground(Theme.Brushes.OnSurface.Variant.Default)
                                            .Style(Theme.Button.Styles.Icon)
                                            .AutoLayout(counterAlignment: AutoLayoutAlignment.Center)
                                            .Content
                                            (
                                                new PathIcon()
                                                    .Data(StaticResource.Get<Geometry>("Icon_Chevron_Right"))
                                                    .Foreground(Theme.Brushes.OnSurface.Variant.Default)
                                            )
                                    )
                            )
                    )
            );
    
  6. Replace the ListView's ItemTemplate extension method with the following:

    .ItemTemplate<YoutubeVideo>(VideoItemTemplate)
    

Add a FeedView to the UI and customize the NoneTemplate

Add FeedView

The FeedView control is shipped as part of the MVUX and is tailored to work with feeds and states.
It reacts visually to the current state of the data and its underlying request. Here's a brief overview of the templates it currently supports:

  • ValueTemplate - used when there are ordinary data results
  • NoneTemplate - used when there was no data found
  • ErrorTemplate - used when an error occurs while requesting data
  • ProgressTemplate - used when the underlying feed or state is busy loading data
  • UndefinedTemplate - used when the control initializes, before the request is sent
Tip

To learn more about the FeedView, head over to its docs page.

Let's add a FeedView to our UI. We'll start with the ValueTemplate first.

  1. Open the file MainPage.cs and wrap the search results ListView in a FeedView:

    new FeedView()
        .AutoLayout(primaryAlignment: AutoLayoutPrimaryAlignment.Stretch)
        .VerticalAlignment(VerticalAlignment.Stretch)
        .VerticalContentAlignment(VerticalAlignment.Stretch)
        .Source(() => vm.VideoSearchResults)
        .ValueTemplate<FeedViewState>(feedViewState =>
            new ListView()
                ...
        )
    

    Feel free to touch up the code and indent it by selecting the code and pressing Tab or Shift+Tab to indent/unindent code in Visual Studio.

  2. Change the ItemsSource property of the ListView to the Data property of the FeedViewState as follows:

     ...
     new ListView()
    -    .ItemsSource(b => b.Binding("VideoSearchResults"))
    +    .ItemsSource(() => feedViewState.Data)
         ...
         .ItemTemplate(VideoItemTemplate)
     ...
    

    The FeedView serves the data via the FeedViewState wrapper class to the template, the actual data is accessed via the wrapper's Data property.

Run the app [optional]

The FeedView's error template defaults to *An error occurred' text message. If you run the app, switch flight mode on, and search YouTube, that message will display:

Default error message shown in FeedView

Customize the NoneTemplate

Add the following Shapes namespace, as well as the Path alias to the header of MainPage.cs, so that there are no ambiguations with System.IO.Path, as there are Path elements contained in the templates you are about to introduce to the project.

using Microsoft.UI.Xaml.Shapes;
using Path = Microsoft.UI.Xaml.Shapes.Path;
  1. Open Figma and select the 1.3 No search results state screen.

  2. Open the Uno Platform plugin (Ctrl+Alt+P), and navigate to the Export tab.

  3. Click the Refresh button.

    Figma Refresh button

  4. In the generated C# code, skip the navigation bar and the search box parts, then select and copy the AutoLayout that follows, with all its descendants.

  5. Add a private static method named VideoNoneTemplate returning UIElement into the MainPage class.

  6. Paste the content copied from Figma as the return value of the method.

    private static UIElement VideoNoneTemplate() =>
        /* copied content */
    
  7. Append a semicolon (;) to the end of the method to terminate it.

Add the following setting to the FeedView:

 new FeedView()
     ...
+    .NoneTemplate(VideoNoneTemplate)
     .ValueTemplate...

Run the app

When you run the app next and delete the search term, you'll see how the FeedView switches to the NoneTemplate you've just set up when there is no data:

UI showing NoneTemplate

Customize the ErrorTemplate

Import template

  1. Open Figma and select the 1.2 Error first loading error screen.

  2. Open the Uno Platform plugin (Ctrl+Alt+P), and navigate to the Export tab.

  3. Click the Refresh button.

    Figma Refresh button

  4. From the generated C#, skip the navigation bar and the search box parts, then select and copy the AutoLayout that follows, with all its descendants.

  5. Add a private static method named VideoErrorTemplate returning UIElement into the MainPage class:

  6. Paste the content copied from Figma as the return value of the method.

    private static UIElement VideoErrorTemplate() =>
        /* copied content */
    
  7. Append a semicolon (;) to the end of the method to terminate it.

Append the following property setting to the FeedView:

new FeedView()
    ...
    .ErrorTemplate(VideoErrorTemplate)
    ...

Run the app

  1. Run the app. Search results for the term Uno Platform will be loaded from YouTube.

  2. Disable the device's network (flight mode).

  3. Perform a new search by changing the search term.

  4. Observe how the error template is displayed.

  5. Restore the internet connection and return to the result.

  6. Click one of the search results, and you'll notice that the video on the video page doesn't play. In the upcoming module, you'll add a media player control to the app, which will play the videos.

    UI showing error template

Next Step

Previous | Next