Module 3 - Connect UI with mock data

In this module, you will create models and services that will be used by the presentation layer to retrieve data and display it in the appropriate UI controls. You are first going to add a mock service that will simulate YouTube search results. In module 9, you will wire the service up with live search results from YouTube's API.

Create entities

In this section, you will create the entities that will be used to transfer data between the service and the presentation layers.

  1. Create another folder in the project (i.e. TubePlayer) named Services, and add another subfolder to it, named Models.

  2. Create a class named Models.cs and replace its content with the code below. If you are using Visual Studio, you can right-click the Models folder and click Add Class. When prompted, type Models.cs as the file name and press Enter.

    namespace TubePlayer.Services.Models;
    
    public record ThumbnailData(string? Url);
    
    public record ThumbnailsData(ThumbnailData? Medium, ThumbnailData? High);
    
    public record SnippetData(string? ChannelId, string? Title, string? Description, ThumbnailsData? Thumbnails, string? ChannelTitle, DateTime? PublishedAt);
    
    public record StatisticsData(string? ViewCount, string? LikeCount, string? CommentCount, string? SubscriberCount);
    
    public partial record ChannelData(
        string? Id,
        SnippetData? Snippet,
        StatisticsData? Statistics = default);
    
    public record ContentDetailsData(string? Duration);
    
    public partial record YoutubeVideoDetailsData(
        string? Id,
        SnippetData? Snippet,
        StatisticsData? Statistics = default,
        ContentDetailsData? ContentDetails = default);
    
    public record VideoDetailsResultData(ImmutableList<YoutubeVideoDetailsData>? Items);
    
    public record ChannelSearchResultData(ImmutableList<ChannelData>? Items);
    
  3. In the folder BusinessModels, add a new file called Models.cs and replace its contents with the following code.

    namespace TubePlayer.Business.Models;
    
    public partial record YoutubeVideo(ChannelData Channel, YoutubeVideoDetailsData Details)
    {
        public string? Id => Details.Id;
        public string FormattedSubscriberCount => $"{FormatLongNumber(SubscriberCount)} subscriber{(SubscriberCount > 1 ? "s" : string.Empty)}";
        public string FormattedStatistics => $"{FormattedViewCount} {Bullet} {FormattedPublishedAt}";
    
        private long ViewCount => long.TryParse(Details.Statistics?.ViewCount, out var result) ? result : default;
    
        private TimeSpan PublishedAtAgo => (Details.Snippet?.PublishedAt).GetValueOrDefault(defaultValue: DateTime.Now).Subtract(DateTime.Now);
        private long SubscriberCount => long.TryParse(Channel.Statistics?.SubscriberCount, out var result) ? result : default;
        private string FormattedViewCount => $"{FormatLongNumber(ViewCount)} view{(ViewCount > 1 ? "s" : string.Empty)}";
        private string FormattedPublishedAt => ToFriendlyString(PublishedAtAgo);
    
        private const string Bullet = "•";
        private const long Thousand = 1_000;
        private const long Million = Thousand * Thousand;
    
        private static string FormatLongNumber(long number) =>
            number switch
            {
                >= Million => (number / Million).ToString("0.##") + "M",
                >= Thousand => (number / Thousand).ToString("0.##") + "K",
                _ => number.ToString()
            };
    
        private static string ToFriendlyString(TimeSpan elapsedTime)
        {
            double minutesElapsed = elapsedTime.TotalMinutes;
    
            var result = minutesElapsed switch
            {
                < 0.75 => "less than a minute",
                < 1.5 => "about a minute",
                < 45 => $"{Math.Round(minutesElapsed)} minutes",
                < 90 => "about an hour",
                < 60 * 24 => $"about {Math.Round(Math.Abs(elapsedTime.TotalHours))} hours",
                < 60 * 48 => "a day",
                < 60 * 24 * 30 => $"{Math.Floor(Math.Abs(elapsedTime.TotalDays))} days",
                < 60 * 24 * 60 => "about a month",
                < 60 * 24 * 365 => $"{Math.Floor(Math.Abs(elapsedTime.TotalDays / 30))} months",
                < 60 * 24 * 365 * 2 => "about a year",
                _ => $"{Math.Floor(Math.Abs(elapsedTime.TotalDays / 365))} years"
            };
    
            return $"{result} ago";
        }
    }
    
    public record YoutubeVideoSet(IImmutableList<YoutubeVideo> Videos, string NextPageToken)
    {
        public static YoutubeVideoSet CreateEmpty() => new(ImmutableList.Create<YoutubeVideo>(), string.Empty);
    }
    

Add mock data service

In the Business folder, add a class named YoutubeServiceMockData.cs, and replace its content with the following:

YoutubeServiceMockData.cs code contents (collapsed for brevity)
namespace TubePlayer.Business;

public class YoutubeServiceMockData
{
    public const string ChannelData =
"""
{
  "items": [
    {
      "id": "UC8GkqD6hsSkwYof6n2Wk1hg",
      "snippet": {
        "channelId": "",
        "title": "Uno Platform",
        "description": "Welcome to the official YouTube channel of Uno Platform! The open source-platform for creating true single-source, multi-platform applications. Here we'll share best practices in Uno Platform, live coding sessions, and How-to tutorials!\n\nUno Platform is the first and only UI Platform for single-codebase applications for Windows, WebAssembly, iOS, macOS, Android and Linux. Build Mobile, Desktop and WebAssembly apps with C# and XAML. Today. Open source and professionally supported.\n\nVisit our website to learn more ➲ https://platform.uno/\nGet Started with Uno Platform ➲ https://platform.uno/get-started\nVisit our Github ➲ https://github.com/unoplatform/uno\nFollow us on Twitter ➲ https://twitter.com/UnoPlatform\n",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/iR1HhVplYsb3ba73PbXxyOqJhsB6-mSmi1T9_1disA3rp_vxvAwUMCP3YQgvuuUoUgXesdli=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/iR1HhVplYsb3ba73PbXxyOqJhsB6-mSmi1T9_1disA3rp_vxvAwUMCP3YQgvuuUoUgXesdli=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2019-06-21T19:12:22Z"
      },
      "statistics": {
        "viewCount": "163220",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "2040"
      }
    },
    {
      "id": "UCTdw38Cw6jcm0atBPA39a0Q",
      "snippet": {
        "channelId": "",
        "title": "NDC Conferences",
        "description": "After launching in Oslo 2008, NDC quickly became one of Europe’s largest conferences\nfor .NET & Agile development. Since then, the conference has evolved to encompass all technologies relevant to Software Developers. NDC speakers come from all over the world and are recognized as experts and thought leaders in their field.\n\nSee our upcoming events at ndcconferences.com",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaQ0f3RZNLI2GsTkiGPrRDSDsfPYnMt99VK0V_mu9Q=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaQ0f3RZNLI2GsTkiGPrRDSDsfPYnMt99VK0V_mu9Q=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2016-06-10T08:54:48Z"
      },
      "statistics": {
        "viewCount": "23508781",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "161000"
      }
    },
    {
      "id": "UCShMfSax7VTX3leEW9urM5w",
      "snippet": {
        "channelId": "",
        "title": "Lethcode",
        "description": "Coding has brought so much joy to my life compared to my work before. I moved on rather later in life to coding. (30s) I want to help others do the same. There is so much going on in the world of technology it is hard to keep up. But I am working on creating better tutorials that can get people into coding or get people excited.\n\nI don't know about you but most tutorials are boring. I want to raise the bar and make them more exciting and entertaining. Subscribe and  we will hopefully raise the bar on entertainment value and quality of tutorials. It will be a long journey but lets lift everyone.\n",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaRBIScn48sMYND0ZrZHwRwiMh42CmWFRwGVc27L=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaRBIScn48sMYND0ZrZHwRwiMh42CmWFRwGVc27L=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2020-02-25T03:55:08.808338Z"
      },
      "statistics": {
        "viewCount": "252067",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "1610"
      }
    },
    {
      "id": "UCBFgwtV9lIIhvoNh0xoQ7Pg",
      "snippet": {
        "channelId": "",
        "title": "SSW TV | Videos for developers, by developers",
        "description": "SSW TV is a platform for software industry members to keep up to date.\nLearn about the latest industry developments, and interact and learn from top industry professionals. \n\nSSW TV is brought to you by SSW - an industry leader in software development. \nHere, you will find interviews with the top experts, presentations from professionals, how-to and tutorial videos, webinars, and much more! Find information about your favourite technologies such as Power BI, Microsoft Teams, ASP.NET, Angular and many more. Be sure to subscribe and turn on notifications to never miss anything!\n\nFor more information about SSW's website, SSW Rules and more, please check our Linktree below.\n",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/e5enJPc8qLh6Nlr4RWBuN0D7Jn44bpm5xppZviC-O76uKaaml3zS7DDCOkFbnA9LgayN5WAtdJc=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/e5enJPc8qLh6Nlr4RWBuN0D7Jn44bpm5xppZviC-O76uKaaml3zS7DDCOkFbnA9LgayN5WAtdJc=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2009-02-04T23:28:01Z"
      },
      "statistics": {
        "viewCount": "4340045",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "37600"
      }
    },
    {
      "id": "UCENTmbKaTphpWV2R2evVz2A",
      "snippet": {
        "channelId": "",
        "title": "James Montemagno",
        "description": "Live, Love, Bike, and Code. \n\nI'm James Montemagno, a Principal Lead Program Manager for .NET Community at Microsoft. I have been a .NET developer since 2005, working in a wide range of industries including game development, printer software, and web services. Prior to becoming a Principal Program Manager, I was a professional mobile developer and has now been crafting apps since 2011 with Xamarin. In my spare time, I am most likely cycling around the PNW or guzzling gallons of coffee at a local coffee shop. \n\nFollow:\nLinkedIn: https://www.linkedin.com/in/jamesmontemagno/\nGitHub: https://github.com/jamesmontemagno\nTwitter: https://twitter.com/jamesmontemagno\nTwitch: https://twitch.tv/jamesmontemagno\nWebsite: https://www.montemagno.com\nMastodon: https://mastodon.social/@jamesmontemagno\n",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/ix5f5kkT-Iuh1op2ChTDAvc6nk75LTwGFKEOg6XonbdUWCL6_WvmixyGHsjWZejCssB4dxDei-Y=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/ix5f5kkT-Iuh1op2ChTDAvc6nk75LTwGFKEOg6XonbdUWCL6_WvmixyGHsjWZejCssB4dxDei-Y=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2013-02-06T23:24:12Z"
      },
      "statistics": {
        "viewCount": "3959231",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "55400"
      }
    },
    {
      "id": "UChqrDOwARrxdJF-ykAptc7w",
      "snippet": {
        "channelId": "",
        "title": "Microsoft Visual Studio",
        "description": "This is the official channel from Microsoft for events and videos related to Visual Studio, the amazing tools and services for you to create awesome software for yourself...or millions of people. To learn more, please visit https://www.visualstudio.com or follow us on Twitter @VisualStudio.\n\nMicrosoft YouTube Community Guidelines:\n\n1) Avoid explicit and inflammatory language \n2) Do not post repetitively nor spam our comment section \n3) Avoid posting personal information, such as: email, physical address, personal phone number, credit card information, or your site password. \n4) Please report posts that violate community guidelines using the spam button or YouTube’s Reporting and Enforcement Center.",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaTfExOvz8cgk5SY7p3xuOx8iNk99LGK03ptXD2XUYo=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaTfExOvz8cgk5SY7p3xuOx8iNk99LGK03ptXD2XUYo=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2006-08-31T12:58:52Z"
      },
      "statistics": {
        "viewCount": "18901235",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "314000"
      }
    },
    {
      "id": "UC9HZHDZQQvq-mEerHeQooog",
      "snippet": {
        "channelId": "",
        "title": "VectoArt",
        "description": "Greetings and welcome to my YouTube channel, my name is Ronak Shirodkar.\nI’m a Developer, Designer and Technology enthusiast. I love working on a new and exciting projects, sharing my knowledge with others, and I am always willing to learn more. I believe that permanent education is the key to success.\n\nI created this channel with the aim to help you to learn UI/UX Design, WinForms, WPF and Resume Design using tools like InDesign, Microsoft Word, and .NET Development.\n\nIf you are amateur or even an expert in .Net Development, UI/UX Design, WinForms, WPF and Resume Design, feel free to subscribe to my channel.\n\nFor Inquiry: artillustration391@gmail.com",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/Z5bOxiTd1gmi4S6T86J0nLruoLUTeqDYIcDg_tK3fQ-I8u3P9k0FJA0RhEDRRjUgKMJTwOxkh5g=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/Z5bOxiTd1gmi4S6T86J0nLruoLUTeqDYIcDg_tK3fQ-I8u3P9k0FJA0RhEDRRjUgKMJTwOxkh5g=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2017-06-11T08:55:05Z"
      },
      "statistics": {
        "viewCount": "1168977",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "7950"
      }
    },
    {
      "id": "UCXflBJMuMQ7INty0hZCAvLg",
      "snippet": {
        "channelId": "",
        "title": "Andrea Angella",
        "description": "Hi, I am Andrea Angella!\n\nI am a Lead Software Engineer in Redgate Software and have a strong passion for learning, growing, and sharing my wisdom and experience with the world. \n\nThis channel represents 100% who I am, and I share anything I am passionate about, from coding to mathematics, leadership, management, music, philosophy, psychology, traveling, and everything in between. \n\nI am British and Italian, so some of my videos are in English and others in Italian. \n\nIf you are one of my C# developers followers, enroll in my free course at https://join.productivecsharp.com/modern-csharp. \n\nMy peers describe me as really enthusiastic, motivated with an infinite passion for software development, a relentless desire to keep learning, and an amazing ability to involve others in my pursuit of excellence. \n",
        "thumbnails": {
          "medium": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaQv1RJV8MWBCJP3YczqScENw4PpmzWtYi2yX9Nv5w=s240-c-k-c0x00ffffff-no-rj"
          },
          "high": {
            "url": "https://yt3.ggpht.com/ytc/AOPolaQv1RJV8MWBCJP3YczqScENw4PpmzWtYi2yX9Nv5w=s800-c-k-c0x00ffffff-no-rj"
          }
        },
        "channelTitle": null,
        "publishedAt": "2008-10-28T00:51:41Z"
      },
      "statistics": {
        "viewCount": "309043",
        "likeCount": "",
        "commentCount": "",
        "subscriberCount": "2890"
      }
    }
  ]
}
""";

    public const string DetailsData =
"""
{
  "items": [
    {
      "id": "cY51JedRtHA",
      "snippet": {
        "channelId": "UCENTmbKaTphpWV2R2evVz2A",
        "title": "Which C# UI Desktop Technology to Pick? | Stream Highlight | .NET MAUI, WinUI 3, WPF, Avalonia, Uno",
        "description": "On a recent live stream, I was asked what UI desktop technology should be used if re-writing a complete desktop app with extremely specific requirements but also using Xamarin.Forms for the mobile apps. Tune in.\n\nLinks:\n* My Stream Timer - https://github.com/jamesmontemagno/MyStreamTimer\n* .NET MAUI - https://github.com/dotnet/maui\n* Xamarin.Forms - https://dotnet.microsoft.com/apps/xamarin\n* .NET - https://dot.net\n* Avalonia - http://avaloniaui.net/\n* Uno - https://platform.uno/\n* Blazor - https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\n\nWhat is on my hat? It is the CLE clothing logo because I am from Cleveland! Checkout their awesome CLE merch: https://cleclothingco.myshopify.com/\n\nWhat is that art on my wall? It is an original piece from the French street artist Gregos of La Butte Montmartre: https://www.instagram.com/p/BceZ1oNHiQx/\n\nFollow:\n👨‍💻 GitHub: https://github.com/jamesmontemagno\n🦜 Twitter: https://twitter.com/jamesmontemagno\n🔴 Twitch: https://twitch.tv/jamesmontemagno\n📄 Website: https://www.montemagno.com\n📰 Newsletter: https://newsletter.montemagno.com/\n\nDisclaimer: This channel, videos, and streams are created in my spare time and are a product of me... James Montemagno! They are NOT officially affiliated or endorsed by Microsoft (my employer) in any way. Opinions and views are my own.\n\nMy Setup:\n* Blue Spark Microphone - https://amzn.to/3qgtYkq\n* Blue Pop Filter - https://amzn.to/3jEWM3r\n* Rode Microphone Arm - https://amzn.to/2Z68AlE\n* Sony MDR7306 Headphones - https://amzn.to/372jxta\n* Stream Deck - https://amzn.to/373Uk1n\n* Elgato Cam Link - https://amzn.to/3a9eGbh\n* GoPro Hero - https://amzn.to/374lm90\n* MX Master 2S Mouse - https://amzn.to/3d7J2gj\n* Tecware Phantom Keyboard - https://amzn.to/3aUP4y9\n\n#UWP #WPF #WinUI #DotNetMAUI #XamarinForms",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/cY51JedRtHA/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/cY51JedRtHA/hqdefault.jpg"
          }
        },
        "channelTitle": "James Montemagno",
        "publishedAt": "2021-08-26T13:00:30Z"
      },
      "statistics": {
        "viewCount": "34049",
        "likeCount": "552",
        "commentCount": "57",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT12M7S"
      }
    },
    {
      "id": "fyo2BI4rn0g",
      "snippet": {
        "channelId": "UChqrDOwARrxdJF-ykAptc7w",
        "title": "Uno Platform Part 1",
        "description": "In this episode, Robert is joined by Martin Zikmund, who introduces us to the Uno Platform (https://platform.uno/). Uno is a Universal Windows Platform Bridge that enables UWP-based code (C# and XAML) to run on iOS, Android and WebAssembly. Martin shows how you can take a UWP app and compile it to run not only on Windows 10, but also on mobile devices and the Web. \n\nThis episode is an introduction. Next week, Martin shows some more advanced demos. \n\nResources:\n\nUno Platform docs (https://platform.uno/docs/articles/intro.html)",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/fyo2BI4rn0g/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/fyo2BI4rn0g/hqdefault.jpg"
          }
        },
        "channelTitle": "Microsoft Visual Studio",
        "publishedAt": "2019-12-12T18:31:00Z"
      },
      "statistics": {
        "viewCount": "30609",
        "likeCount": "622",
        "commentCount": "103",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT20M12S"
      }
    },
    {
      "id": "0zvzfCGO0gI",
      "snippet": {
        "channelId": "UCBFgwtV9lIIhvoNh0xoQ7Pg",
        "title": "Build Multi-Platform Applications for Mobile, Desktop and Web in .NET with the Uno Platform",
        "description": "Uno Platform ↴\nhttps://platform.uno/\n\nDummyJSON ↴\nhttps://dummyjson.com/\n\nJuly .NET User Group: Build Multi-Platform Applications for Mobile, Desktop and Web in .NET with the Uno Platform.\n\nThe open-source Uno Platform extends pixel-perfect WinUI experiences built with C# and XAML to all platforms where WinUI doesn't natively run on – Web/WebAssembly, macOS, Linux, iOS, Android.\n\nThe ability to deploy the same UI codebase across all platforms is a great productivity booster. But the application lifecycle starts much earlier – at design time. Uno Platform provides a Figma plugin, which eliminates the timely designer-developer handoff. In addition, the platform now provides a set of non-UI extensions to help jump-start your apps. Lastly, the VS Code extensions allows C# and XAML to use VS Code with IntelliSense-like experience, C# and XAML Hot Reload, and more.\n\nIn this session, you'll learn how you can create applications spanning across multiple platforms and device form factors, all while reusing your existing XAML and C# skills.\n\n15:21 |  Introduction\n15:58 |  What is WhinUIEverywhere\n20:19 |  #WinUIEverywhere\n22:25 |  Every Pixel\n25:11 |  XAML\n25:39 |  Live demo UNO app\n31:54 |  History of \"Universal app development\"\n33:33 |  Licensing\n34:24 |  Sauce code and Library support\n35:58 |  Infragistics Controls for UNO Platform\n36:08 |  Productivity: Toolkit VS Code, Figmia\n36:58 |  Figma\n38:58 |  UNO for Figma\n40:18 |  Figma Demo\n43:01 |  Themes\n43:52 |  Toolkit\n45:16 |  Extensions: Hit the ground running\n47:11 |  Extensions: Navigation\n48:05 |  Extensions:Reactive (MVU-X)\n50:04 |  VS Code tooling integration\n56:22 |  Making it look like the Figma demo app\n07:20 |  Summary\n10:57 |  Q & A\n\n|| Subscribe for more content from SSW TV ||\n\n|| Press like and leave a comment below to let us know how we're doing ||\n\nTwitter ↴\nhttps://twitter.com/ssw_tv\n\nFacebook ↴\nhttps://www.facebook.com/SSW.page\n\n------------------------------------------------------------------------------\n\nNick Randolph\n\nNick currently runs Built to Roam which focuses on building rich mobile applications. Nick has been identified as a Microsoft MVP in recognition of his work and expertise with the Microsoft application platforms. \n\nHe is still an active contributor in the device application development space via his blog at http://nicksnettravels.builttoroam.com. Nick has been invited to present at a variety of events including Tech Ed and Ignite Australia & NZ, DDD, NDC and local user groups. \n\nHe has also authored multiple books on Visual Studio and Windows development, and helped judge multiple world finals for the Imagine Cup. \n\nNick has worked on numerous mobile applications and has helped hundreds of developers build their own mobile applications. Nick has been involved with applications for well-known brands such as Domain.com.au, ninemsn, AFL, NRL, Qantas, JB Hi-Fi, NAB, Stan and Boost Juice\n\nTwitter ↴\nhttps://twitter.com/thenickrandolph\n------------------------------------------------------------------------------\n\nSee more videos at https://tv.ssw.com\n\nFor more information about SSW's web application consulting services, please visit https://ssw.com.au/ssw/Consulting/Web-Applications.aspx\n\nCreated by SSW TV | Videos for developers, by developers\n\nV3 - Greentree",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/0zvzfCGO0gI/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/0zvzfCGO0gI/hqdefault.jpg"
          }
        },
        "channelTitle": "SSW TV | Videos for developers, by developers",
        "publishedAt": "2022-08-03T05:50:42Z"
      },
      "statistics": {
        "viewCount": "2882",
        "likeCount": "56",
        "commentCount": "1",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT1H5M14S"
      }
    },
    {
      "id": "uPSAdGhMUHE",
      "snippet": {
        "channelId": "UC9HZHDZQQvq-mEerHeQooog",
        "title": "Windows UI 3 : Uno Platform | Get Started",
        "description": "Windows UI 3 : Uno Platform | Get Started\n\nUno Platform allows you to create single-codebase, cross-platform applications which run on iOS, Android, Web, macOS, Linux and Windows. You'll be creating cross-platform .NET applications with XAML and C# in no time.\n\nThis getting started will guide you through the creation of an Uno Platform App using C# and .NET, based in the WinUI 3 XAML.\n\nCommands\ndotnet tool install -g uno.check\ndotnet tool update -g uno.check\nuno-check\n\nOther Videos:\nWindows UI 3 : To Do App using Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-video_hub-ffiumb\n\nWindows UI 3 : Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-videos_2021-pswuqy\n\nWindows UI 3 : Uno Platform | Get Started\nhttps://linkopener.co/youtube-video-home\n\nWindows UI 3: Login App using Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-videos-lwrjvy\n\nPlease see Common Issues \nhttps://platform.uno/docs/articles/get-started-wizard.html?tabs=windows\n\nGitHub Discussions or Discord\nhttps://github.com/unoplatform/uno/discussions\nhttps://discord.com/invite/eBHZSKG\n\n#uno-platform #winui  #fluentui  #uidesign  #uxdesign  #visualstudio  #xaml  #vectoart",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/uPSAdGhMUHE/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/uPSAdGhMUHE/hqdefault.jpg"
          }
        },
        "channelTitle": "VectoArt",
        "publishedAt": "2023-03-18T04:00:08Z"
      },
      "statistics": {
        "viewCount": "2127",
        "likeCount": "32",
        "commentCount": "1",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT9M15S"
      }
    },
    {
      "id": "ep7wA3QSyuM",
      "snippet": {
        "channelId": "UC8GkqD6hsSkwYof6n2Wk1hg",
        "title": "Getting Started with Uno Platform and Visual Studio Code",
        "description": "Video takes you through the initial setup and getting your first Uno Platform application running in Visual Studio Code.\n\nCommands\nInstall Templates: dotnet new install Uno.Templates\nCreate a New Project : dotnet new unoapp -o MyApp -vscode\nUninstall Old Templates: dotnet new uninstall Uno.ProjectTemplates.Dotnet\n\nResources\nDocs: https://platform.uno/docs/articles/get-started-vscode.html?tabs=windows%2Candroiddebug\nUno VS Code Page: https://platform.uno/vs-code/\nUno Platform VS Code Extension: https://marketplace.visualstudio.com/items?itemName=unoplatform.vscode\nLatest VS Code Extension Announcement: https://platform.uno/blog/announcing-net-mobile-debugging-in-vs-code-mobile-development-in-vs-code-with-uno-platform-or-net-maui/",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/ep7wA3QSyuM/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/ep7wA3QSyuM/hqdefault.jpg"
          }
        },
        "channelTitle": "Uno Platform",
        "publishedAt": "2023-05-05T16:51:13Z"
      },
      "statistics": {
        "viewCount": "502",
        "likeCount": "14",
        "commentCount": "1",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT2M2S"
      }
    },
    {
      "id": "hQcKsoxaAHo",
      "snippet": {
        "channelId": "UC8GkqD6hsSkwYof6n2Wk1hg",
        "title": "Getting Started with Uno Platform and Visual Studio",
        "description": "Video takes you through initial setup in Visual Studio and getting the first Uno Platform application running. Video complements the Getting Started Tutorial at https://platform.uno/docs/articles/getting-started-tutorial-1.html\n\nNext Steps Resources:\nToDo Sample App - https://platform.uno/GetStarted \nCh9 Sample & Real App - https://github.com/unoplatform/Uno.Ch9\nDiscord - #uno-platform channel https://discord.gg/eBHZSKG",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/hQcKsoxaAHo/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/hQcKsoxaAHo/hqdefault.jpg"
          }
        },
        "channelTitle": "Uno Platform",
        "publishedAt": "2020-08-19T17:51:37Z"
      },
      "statistics": {
        "viewCount": "10770",
        "likeCount": "96",
        "commentCount": "9",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT2M41S"
      }
    },
    {
      "id": "6xLDK8Dnfl8",
      "snippet": {
        "channelId": "UC9HZHDZQQvq-mEerHeQooog",
        "title": "Windows UI 3 : Uno Platform | Figma to XAML",
        "description": "Windows UI 3 : Uno Platform | Figma to XAML\n\nUno Platform allows you to create single-codebase, cross-platform applications which run on iOS, Android, Web, macOS, Linux and Windows. You'll be creating cross-platform .NET applications with XAML and C# in no time.\n\nIn this video we will go through \n1. Get Figma Assets\nInstall the Uno Platform Plugin for Figma and Download (“Duplicate”) Uno Platform Material Toolkit from Figma community. The file will contain the sample Commerce App.\n\n2. Export XAML\nInside Figma, navigate to “Example App” inside Uno Platform Material Toolkit. Select any one screen, right-click and select “Uno / Figma” plugin, then generate XAML.\n\n3. See XAML in your App\nCopy XAML generated in step 2 and paste inside your Uno Platform App in Visual Studio. Or if you want to skip steps 1 and 2, you can use XAML we generated. \n\nOther Videos:\nWindows UI 3 : To Do App using Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-video_hub-ffiumb\n\nWindows UI 3 : Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-videos_2021-pswuqy\n\nWindows UI 3 : Uno Platform | Get Started\nhttps://linkopener.co/youtube-video-home\n\nWindows UI 3: Login App using Uno Platform | Figma to XAML\nhttps://linkopener.co/youtube-videos-lwrjvy\n\n\n#uno-platform #winui  #figma #fluentui  #uidesign   #visualstudio  #xaml  #vectoart",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/6xLDK8Dnfl8/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/6xLDK8Dnfl8/hqdefault.jpg"
          }
        },
        "channelTitle": "VectoArt",
        "publishedAt": "2023-03-23T12:00:10Z"
      },
      "statistics": {
        "viewCount": "2472",
        "likeCount": "53",
        "commentCount": "9",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT10M56S"
      }
    },
    {
      "id": "YPfOsbDzamI",
      "snippet": {
        "channelId": "UChqrDOwARrxdJF-ykAptc7w",
        "title": "Visual Studio Toolbox Live - Build Your First App with Uno Platform",
        "description": "Join the Uno Platform team to see how you can create single-codebase apps for Windows, WebAssembly, iOS, macOS, Android and Linux. \n\nFeaturing: Robert Green (@rogreen_ms) & Leslie Richardson (@lyrichardson01)\n\n#Uno #VisualStudio #WebAssembly #Windows #iOS #macOS #Android #Linux",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/YPfOsbDzamI/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/YPfOsbDzamI/hqdefault.jpg"
          }
        },
        "channelTitle": "Microsoft Visual Studio",
        "publishedAt": "2021-02-23T20:34:32Z"
      },
      "statistics": {
        "viewCount": "14108",
        "likeCount": "188",
        "commentCount": "10",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT1H20S"
      }
    },
    {
      "id": "IcnRWuGIsHE",
      "snippet": {
        "channelId": "UCXflBJMuMQ7INty0hZCAvLg",
        "title": "Uno Platform 4.0 by Martin Zikmund",
        "description": "So far you would have known of Uno Platform for its UI unique capabilities – enabling C# and XAML single-codebase apps to run on Web, iOS, Android, macOS, Windows and Linux.\n\nBut now the open-source Uno Platform also brings plethora of new capabilities such as:\n\n• Figma plugin – for one-click XAML generation from Figma designs\n• Extensions – for bootstrapping your apps faster\n• Toolkit – for common cross-platform controls such as Navigation Bar or TabBar\n• VS Code Plugin – for Intelisense-like experience for XAML and C# completion, Hot Reload and more\n\nIn this session we will cover some of the Uno Platform fundamentals and then provide a deep dive on all additional Uno Platform capabilities that came in 4.0 wave.\n\nhttps://www.meetup.com/dotnetcambridge/events/281988862/",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/IcnRWuGIsHE/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/IcnRWuGIsHE/hqdefault.jpg"
          }
        },
        "channelTitle": "Andrea Angella",
        "publishedAt": "2022-06-21T19:08:14Z"
      },
      "statistics": {
        "viewCount": "1766",
        "likeCount": "30",
        "commentCount": "3",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT1H9M6S"
      }
    },
    {
      "id": "BTgp9VyZSs8",
      "snippet": {
        "channelId": "UCTdw38Cw6jcm0atBPA39a0Q",
        "title": "Uno Platform: Your Apps Everywhere - Martin Zikmund - NDC London 2023",
        "description": "Developing cross-platform applications is complex. Some frameworks allow us to share the business logic, some go further and allow us to share the UI layer, often limited to the lowest common denominator among target platforms.\nBut even then – working with device hardware still requires not only error-prone platform-specific code but also developers familiar with each of the target platforms to maintain it. What if there was a framework, which would allow you write all your code in C# and XAML, with unlimited styling and templating capabilities, and a single API to access device hardware and APIs? And what if you could use this framework to target all mobile and desktop devices and even the web? Meet the Uno Platform - your C#/XAML apps on Windows, Android, iOS, macOS, Linux, and WebAssembly!\n\n\nCheck out our new channel: \nNDC Clips:\n@ndcclips \n\nCheck out more of our featured speakers and talks at\nhttps://ndcconferences.com/\nhttps://ndclondon.com/",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/BTgp9VyZSs8/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/BTgp9VyZSs8/hqdefault.jpg"
          }
        },
        "channelTitle": "NDC Conferences",
        "publishedAt": "2023-06-16T10:00:08Z"
      },
      "statistics": {
        "viewCount": "834",
        "likeCount": "27",
        "commentCount": "1",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT52M2S"
      }
    },
    {
      "id": "FrZULFjc8uI",
      "snippet": {
        "channelId": "UChqrDOwARrxdJF-ykAptc7w",
        "title": "Visual Studio Toolbox Live - Deep Dive into the Uno Platform",
        "description": "The Uno Platform enables C# and XAML single-codebase apps to run on multiple platforms. Today the Uno Platform team will go deeper than the UI layer and cover two hidden gems of the Uno Platform. Extensions, to get even more code reuse for commonly implemented functions like navigation, reactive and more. Also, Uno Platform for Figma which produces XAML code from Figma designs.\r\n\r\nFeaturing: Robert Green (@rogreen_ms), Francois Tanguay (@francoistanguay), Jerome Laban (@jeromelaban), Nick Randolph (@thenickrandolph)",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/FrZULFjc8uI/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/FrZULFjc8uI/hqdefault.jpg"
          }
        },
        "channelTitle": "Microsoft Visual Studio",
        "publishedAt": "2022-10-11T19:33:59Z"
      },
      "statistics": {
        "viewCount": "2309",
        "likeCount": "63",
        "commentCount": "19",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT1H17S"
      }
    },
    {
      "id": "RYkznjvVx1o",
      "snippet": {
        "channelId": "UCShMfSax7VTX3leEW9urM5w",
        "title": "How to create cross platform apps with C# - Uno Platform Android, IOS, MacOS, Windows, WASM",
        "description": "Here we explore the Uno Platform for creating a single app that we can deploy across multiple operating systems. Web Assembly, IOS, Android, MacOS and a Windows Native app.\n\nWe walk through live debugging and Real Time Updates of the UI as we change the code. We will walk through a basic app and some real basics of XAML and events in UWP. We also talk about the difference between WPF and UWP and what some of the limitations of this technology will be.\n\nUWP vs WPF  - https://github.com/jbe2277/waf/wiki/UWP-vs.-WPF\nUno Platform Main Page - https://platform.uno/\nGetting Started (PreReqs) - https://platform.uno/docs/articles/get-started-vs.html\nWhat controls are allowed on each target - https://platform.uno/docs/articles/implemented-views.html\nDebugging Web Assembly - https://platform.uno/blog/debugging-uno-platform-webassembly-apps-in-visual-studio-2019/\nMaterial and Fluent Controls - https://gallery.platform.uno/\nWinUI 3 Preview - https://platform.uno/blog/announcing-uno-platform-3-0-support-for-winui-3-0-preview-1/\n\n\nTimecodes\n0:00 Intro\n0:35 WPF vs UWP\n3:33 Introducing Uno Platform\n5:08 Uno Platform Prereqs\n6:17 Building a Uno App\n13:55 Building for WASM\n15:27 Building for Android\n19:30 Debugging a Android app in Uno\n20:02 Debugging a WASM app in Uno\n22:29 What controls are avaliable\n23:39 Material and Fluent Controls\n25:59 WinUI 3 Support Coming",
        "thumbnails": {
          "medium": {
            "url": "https://i.ytimg.com/vi/RYkznjvVx1o/mqdefault.jpg"
          },
          "high": {
            "url": "https://i.ytimg.com/vi/RYkznjvVx1o/hqdefault.jpg"
          }
        },
        "channelTitle": "Lethcode",
        "publishedAt": "2020-10-13T01:54:24Z"
      },
      "statistics": {
        "viewCount": "21576",
        "likeCount": "318",
        "commentCount": "22",
        "subscriberCount": ""
      },
      "contentDetails": {
        "duration": "PT27M16S"
      }
    }
  ]
}
""";
}

This class contains two constant variables that are assigned to partial JSON output results taken from a real YouTube search query of the term 'Uno Platform'.

Create service interface, mock data, mock service, and register services

  1. In the same folder (Business), create a new interface file named IYoutubeService.cs, that will represent our data service. Ignore errors you might see while editing the following files, these will addressed soon by adding the missing namespaces globally.

    namespace TubePlayer.Business;
    
    public interface IYoutubeService
    {
        Task<YoutubeVideoSet> SearchVideos(string searchQuery, string nextPageToken, uint maxResult, CancellationToken ct);
    }
    
  2. Add another class named YoutubeServiceMock.cs to the Business folder and replace its content with the following:

    YoutubeServiceMock.cs code contents (collapsed for brevity)
    using System.Diagnostics.CodeAnalysis;
    
    namespace TubePlayer.Business;
    
    public class YoutubeServiceMock : IYoutubeService
    {
        private readonly VideoDetailsResultData _details;
        private readonly IDictionary<string, ChannelData> _channels;
    
        public YoutubeServiceMock(ISerializer serializer)
        {
            _details = serializer.FromString<VideoDetailsResultData>(YoutubeServiceMockData.DetailsData)!;
    
            var channelsData = serializer.FromString<ChannelSearchResultData>(YoutubeServiceMockData.ChannelData)!;
            _channels = channelsData.Items!.ToDictionary(channel => channel.Id!, StringComparer.OrdinalIgnoreCase);
        }
    
        public Task<YoutubeVideoSet> SearchVideos(string searchQuery, string nextPageToken, uint maxResult, CancellationToken ct)
        {
            var filtered = _details
                .Items!
                .Where(detail =>
                    detail.Snippet!.Title!.Contains(searchQuery, StringComparison.OrdinalIgnoreCase));
    
            var videos = filtered
                        .Select(detail => new YoutubeVideo(_channels[detail.Snippet!.ChannelId!], detail))
                        .ToImmutableList();
    
            var result = new YoutubeVideoSet(videos, NextPageToken: string.Empty);
    
            return Task.FromResult(result);
        }
    }
    

The SearchVideos method reads the JSON result you added in the previous instruction and deserializes it into ChannelSearchResultData and VideoDetailsResultData objects respectively.

Register service

As we've selected Dependency Injection when generating the app, it includes full DI support on all platforms.
You will now register the YoutubeServiceMock to be provided every time an IYoutubeService is requested.

  1. Open GlobalUsings.cs and append the following lines to it:

    global using System.Text.Json;
    global using TubePlayer.Business;
    global using TubePlayer.Services.Models;
    

    In Visual Studio, you can sort the usings, go to the menu and click EditIntelliSenseSort Usings (Alt+E,I,S).

  2. Open the App.xaml.cs file (in the project folder), head to the ConfigureServices section, remove the comments in it, and add the following registrations instead:

    services.AddSingleton<IYoutubeService, YoutubeServiceMock>();
    
  3. Before the line .ConfigureServices add the following service registration:

    .UseSerialization(services =>
    {
        services.AddSingleton(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
    })
    

    This sets up Uno Platform's serialization extension.

Setup the presentation layer with the service

Use mock service in the model

  1. Open the file MainModel.cs and replace its content with the following:

    namespace TubePlayer.Presentation;
    
    public partial record MainModel(IYoutubeService YoutubeService)
    {
        public IState<string> SearchTerm => State<string>.Value(this, () => "Uno Platform");
    }
    

    The SearchTerm is an MVUX state, with "Uno Platform" set as its default value. Feeds and states are part of the MVUX framework developed by Uno Platform.
    Both feeds and states are used as an async data connection point between the model and its underlying services. The MVUX also offers controls that can interact with feeds and states.
    The difference between states and feeds is that a state holds the state of the data and can be updated manually, whereas feeds only act as a bridge to the data coming from the service.

    To learn more about MVUX, refer to the MVUX docs.

  2. After the SearchTerm property, add another property. Contrary to the SearchTerm which is a state to support two-way binding to and from the search TextBox, the VideoSearchResults property we are now adding will be a list-feed (a feed of a collection of items) that requests and obtains the search results we just set up in the mock service.

    public IListFeed<YoutubeVideo> VideoSearchResults => SearchTerm
        .Where(searchTerm => searchTerm is { Length: > 0 })
        .SelectAsync(async (searchTerm, ct) =>
            await YoutubeService.SearchVideos(searchTerm, nextPageToken: string.Empty, maxResult: 30, ct))
        .Select(result => result.Videos)
        .AsListFeed();
    

Bind UI

  1. Go back to MainPage.cs and set the Page's DataContext to the generated bindable proxy of MainModel, by adding the following line to the beginning of the UI tree (after this.).

    +this.DataContext<BindableMainModel>((page, vm) => page
         .NavigationCacheMode(NavigationCacheMode.Required)
          ...
    
  2. Add a closing parenthesis at the end of the C# Markup tree of MainModel to close that (you can just add another closing parenthesis before the terminating semicolon (;).

  3. Bind the TextBox to the SearchTerm state property, and expand the ListView's template like the following:

    namespace TubePlayer.Presentation;
    
    public partial class MainPage : Page
    {
        public MainPage()
        {
            this.DataContext<BindableMainModel>((page, vm) => page
                .NavigationCacheMode(NavigationCacheMode.Required)
                .Background(Theme.Brushes.Background.Default)
                .Content(new Grid()
                    .SafeArea(SafeArea.InsetMask.All)
                    .RowDefinitions("Auto, *")
                    .Children(
                        new TextBox()
                            .Text(x => x
                                .Binding(() => vm.SearchTerm)
                                .Mode(BindingMode.TwoWay)
                                .UpdateSourceTrigger(UpdateSourceTrigger.PropertyChanged))
                            .PlaceholderText("Search term"),
                        new ListView()
                            .Grid(row: 1)
                            .ItemsSource(() => vm.VideoSearchResults)
                            .ItemTemplate<YoutubeVideo>(ytv =>
                                new StackPanel()
                                    .Children(
                                        new TextBlock()
                                            .FontWeight(FontWeights.Bold)
                                            .Text(() =>
                                                ytv.Details.Snippet?.ChannelTitle),
                                        new TextBlock()
                                            .Text(() =>
                                                ytv.Details.Snippet?.Title))))));
        }
    }
    

    The DataTemplate is set for a YoutubeVideo object. It consists of a StackPanel containing two text fields for the video title (styled in bold text), and the title of the YouTube channel that published the video.

Update other models

  1. Open the VideoDetailsModel.cs file, add a YoutubeVideo property instead of Entity, as well as the VideoSource property. Here are the updated contents of the file:

    using Windows.Media.Core;
    
    namespace TubePlayer.Presentation;
    
    public partial record VideoDetailsModel(YoutubeVideo Video)
    {
        public IFeed<MediaSource> VideoSource => Feed.Async((ct) => new ValueTask<MediaSource>());
    }
    

    These properties are currently defined as placeholders to enable bindings to work properly but will be implemented to play YouTube videos in module 10.

  2. In the VideoDetailsPage.cs, remove the Text extension method for now, as its binding depends on the Entity property we've just removed.

     new TextBlock()
    -    .Text(() => vm.Entity.Name)
         .HorizontalAlignment(HorizontalAlignment.Center)
        ...
    
  3. You can now close all open tabs. In Visual Studio, this can be achieved by right-clicking any tab and clicking on Close All Tabs.

    Visual Studio - Close All Tabs command

Run the app

  1. Run the app.
    The ListView will bind with the list-feed which will retrieve the JSON results from the service, based on the search term, which defaults to 'Uno Platform'.

    App displaying JSON search results for the term 'Uno Platform'

  2. Try changing the search term. Let's see what we have for XAML (note that this is based on our limited JSON data):

    App reacting to change of search term to 'XAML'

Next Step

In the next step, you're going to build the UI.
The UI for the Tube Player app has been designed using Figma - an app design tool, along with its Uno Platform plugin for Figma.

Previous | Next

Note

The next module exports the design from Figma. If you don't want to use Figma, you can skip the next module and go directly to Module 5, which provides the final C# Markup exported from Figma with some minor adjustments.