Module 5 - Creating the UI
This module is an alternative way of creating the UI from C# Markup without importing it from Figma.
It includes the C# Markup building blocks that are used in our app if you decide to skip module 4 and import it from Figma.
Import MainPage
Replace the contents of MainPage.cs with the following:
MainPage.cs code contents (collapsed for brevity)
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.UI.Xaml.Navigation;
using System;
using Uno.Extensions.Markup;
using Uno.Extensions.Navigation.UI;
using Uno.Material;
using Uno.Toolkit.UI;
namespace TubePlayer.Presentation;
public partial class MainPage : Page
{
public MainPage()
{
this.DataContext<BindableMainModel>((page, vm) => page
.Background(Theme.Brushes.Background.Default)
.NavigationCacheMode(NavigationCacheMode.Required)
.StatusBar
(
s => s
.Foreground(StatusBarForegroundTheme.Auto)
.Background(Theme.Brushes.Surface.Default)
)
.Resources
(
r => r
.Add("Icon_Chevron_Right", "F1 M 1.4099998474121094 0 L 0 1.4099998474121094 L 4.579999923706055 6 L 0 10.59000015258789 L 1.4099998474121094 12 L 7.409999847412109 6 L 1.4099998474121094 0 Z")
.Add("Icon_Search", "F1 M 12.5 11 L 11.710000038146973 11 L 11.430000305175781 10.729999542236328 C 12.410000324249268 9.589999556541443 13 8.110000014305115 13 6.5 C 13 2.9100000858306885 10.089999914169312 0 6.5 0 C 2.9100000858306885 0 0 2.9100000858306885 0 6.5 C 0 10.089999914169312 2.9100000858306885 13 6.5 13 C 8.110000014305115 13 9.589999556541443 12.410000324249268 10.729999542236328 11.430000305175781 L 11 11.710000038146973 L 11 12.5 L 16 17.489999771118164 L 17.489999771118164 16 L 12.5 11 L 12.5 11 Z M 6.5 11 C 4.009999990463257 11 2 8.990000009536743 2 6.5 C 2 4.009999990463257 4.009999990463257 2 6.5 2 C 8.990000009536743 2 11 4.009999990463257 11 6.5 C 11 8.990000009536743 8.990000009536743 11 6.5 11 Z")
)
.Content
(
new AutoLayout()
.PrimaryAxisAlignment(AutoLayoutAlignment.Center)
.VerticalAlignment(VerticalAlignment.Stretch)
.HorizontalAlignment(HorizontalAlignment.Center)
.Width(400)
.Children
(
new NavigationBar()
.Width(400)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Center)
.Content
(
new AutoLayout()
.PrimaryAxisAlignment(AutoLayoutAlignment.Center)
.Orientation(Orientation.Horizontal)
.Children
(
new Image()
.Source(new BitmapImage(new Uri("https://picsum.photos/384/40")))
.Stretch(Stretch.UniformToFill)
.AutoLayout(primaryAlignment: AutoLayoutPrimaryAlignment.Stretch)
)
),
new AutoLayout()
.Background(Theme.Brushes.Surface.Default)
.Padding(12)
.Children
(
new TextBox()
.Background(Theme.Brushes.Surface.Variant.Default)
.Text(b => b.Binding(() => vm.SearchTerm).TwoWay().UpdateSourceTrigger(UpdateSourceTrigger.PropertyChanged))
.Height(40)
.PlaceholderText("Search")
.CornerRadius(20)
.BorderThickness(0)
.Style(Theme.TextBox.Styles.Outlined)
.ControlExtensions
(
icon:
new PathIcon()
.Data(StaticResource.Get<Geometry>("Icon_Search"))
.Foreground(Theme.Brushes.OnSurface.Variant.Default)
)
),
new ListView()
.Background(Theme.Brushes.Background.Default)
.ItemsSource(() => vm.VideoSearchResults)
.Padding(12, 8)
.Navigation(request: "VideoDetails")
.AutoLayout(primaryAlignment: AutoLayoutPrimaryAlignment.Stretch)
.ItemTemplate<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)
)
)
)
)
)
)
)
))
;
}
}
Import VideoDetailsPage
Replace the contents of VideoDetailsPage.cs with the following:
VideoDetailsPage.cs code contents (collapsed for brevity)
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using Uno.Extensions.Markup;
using Uno.Toolkit.UI;
namespace TubePlayer.Presentation;
public partial class VideoDetailsPage : Page
{
public VideoDetailsPage()
{
this.DataContext<BindableVideoDetailsModel>((page, vm) => page
.Background(Theme.Brushes.Background.Default)
.NavigationCacheMode(NavigationCacheMode.Required)
.StatusBar
(
s => s
.Foreground(StatusBarForegroundTheme.Auto)
.Background(Theme.Brushes.Surface.Default)
)
.Resources
(
r => r
.Add("Icon_Arrow_Back", "F1 M 16 7 L 3.8299999237060547 7 L 9.420000076293945 1.4099998474121094 L 8 0 L 0 8 L 8 16 L 9.40999984741211 14.59000015258789 L 3.8299999237060547 9 L 16 9 L 16 7 Z")
)
.Content
(
new AutoLayout()
.Background(Theme.Brushes.Background.Default)
.Children
(
new AutoLayout()
.Width(400)
.AutoLayout
(
counterAlignment: AutoLayoutAlignment.Center,
primaryAlignment: AutoLayoutPrimaryAlignment.Stretch
)
.Children
(
new NavigationBar()
.HorizontalContentAlignment(HorizontalAlignment.Left)
.Content("Video")
.MainCommand
(
new AppBarButton()
.Icon
(
new PathIcon()
.Data(StaticResource.Get<Geometry>("Icon_Arrow_Back"))
.Foreground(Theme.Brushes.OnSurface.Default)
)
),
new MediaPlayerElement()
.AreTransportControlsEnabled(true)
.Width(400)
.Height(300)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
.TransportControls
(
new MediaTransportControls()
.IsCompact(true)
),
new ScrollViewer()
.AutoLayout(primaryAlignment: AutoLayoutPrimaryAlignment.Stretch)
.Content
(
new AutoLayout()
.Children
(
new AutoLayout()
.Spacing(6)
.Padding(16)
.Width(400)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
.Children
(
new TextBlock()
.TextWrapping(TextWrapping.Wrap)
.Text(() => vm.Video.Channel.Snippet?.Title)
.Foreground(Theme.Brushes.OnSurface.Default)
.Style(Theme.TextBlock.Styles.TitleLarge),
new TextBlock()
.Text(() => vm.Video.FormattedStatistics)
.Foreground(Theme.Brushes.OnSurface.Medium)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
),
new AutoLayout()
.Spacing(8)
.Orientation(Orientation.Horizontal)
.Padding(16, 8)
.Width(400)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
.Children
(
new Border()
.Width(40)
.Height(40)
.CornerRadius(20)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Center)
.Child
(
new Image()
.Source(() => vm.Video.Channel.Snippet?.Thumbnails?.High?.Url!)
.Stretch(Stretch.UniformToFill)
),
new AutoLayout()
.Spacing(2)
.PrimaryAxisAlignment(AutoLayoutAlignment.Center)
.Height(37)
.AutoLayout
(
counterAlignment: AutoLayoutAlignment.Center,
primaryAlignment: AutoLayoutPrimaryAlignment.Stretch
)
.Children
(
new AutoLayout()
.Orientation(Orientation.Horizontal)
.AutoLayout
(
counterAlignment: AutoLayoutAlignment.Start,
primaryAlignment: AutoLayoutPrimaryAlignment.Stretch
)
.Children
(
new TextBlock()
.Text(() => vm.Video.FormattedSubscriberCount)
.Foreground(Theme.Brushes.OnSurface.Medium)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
),
new TextBlock()
.Text(() => vm.Video.Channel.Snippet?.Title)
.Foreground(Theme.Brushes.OnSurface.Default)
.Style(Theme.TextBlock.Styles.TitleMedium)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
)
),
new TextBlock()
.TextWrapping(TextWrapping.Wrap)
.Text(() => vm.Video.Channel.Snippet?.Description)
.Margin(16)
.Foreground(Theme.Brushes.OnSurface.Variant.Default)
.Style(Theme.TextBlock.Styles.BodySmall)
.AutoLayout(counterAlignment: AutoLayoutAlignment.Start)
)
)
)
)));
}
}
Run the app
Run the app (F5 on Visual Studio) and observe the UI changes, it should look similar to the following:
If you try tapping one of the videos in the list, an exception will occur. This is because navigation has not yet been implemented. You will address that in Module 7 - Navigation.
The image above the search page is a random image. It will be replaced in Module 11 - App finalization
Next Step
In the next step, you will adjust the UI you've imported by overriding the app's color theme.