mercredi 9 février 2022

Propagating a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element [XAML]

In this Article we are going to see how to Propagate a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element

My need was that I needed to be able to pass down the information that an item was selected so that I could have the round blue border around the icon.  One solution would have been to add a isSelected property to my header items however I wished something simple to manage the selected state of my header.

Here is a snapshot of what I wanted:



In the end you have two options to propagate the VisualState.  First you can use this option which I found on Microsoft Docs 

Visual states for elements that aren't controls

Visual states are sometimes useful for scenarios where you want to change the state of some area of UI that's not immediately a Control subclass. You can't do this directly because the control parameter of the GoToState method requires a Control subclass, which refers to the object that the VisualStateManager acts upon. Page is a Control subclass, and it's fairly rare that you'd be showing UI in a context where you don't have a Page, or your Window.Content root isn't a Control subclass. We recommend you define a custom UserControl to either be the Window.Content root or be a container for other content you want to apply states to (such as a Panel ). Then you can call GoToState on your UserControl and apply states regardless of whether the rest of the content is a Control. For example you could apply visual states to UI that otherwise consists of just a SwapChainPanel so long as you placed that within your UserControl and declared named states that apply to the properties of the parent UserControl or of the named SwapChainPanel part of the template.

Source: https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.visualstatemanager?view=winrt-19041

However this was a total fail for me as it never worked and also seemed overkill to set a Usercontrol inside my DataTemplate, I do think that with a bit more work I could have gotten it to work but belived that a simpler solution was possible for my needs.

Binding to the ParentElement

In the end the solution that I found that worked the best for my needs was to bind the element to its parent so that when the parent knew that its item was being selected I could pass down this information to my the DataTemplate. The idea was to bind the BorderThickness of my elements in my DataTemplate to it ParentElement, in xaml this means:

BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"

Here my TemplatedParent is the ListViewItemPresenter that is located in my ListView. And with this I was able to pass the different states values Selected/UnSelected to my DataTemplate.  I do agree that If i was able to pass all the different states to my DataTemplate that would have been better but this is the next best thing for me.

Here is my DataTemplate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<DataTemplate x:Key="KeyHeaderFollowingItemTemplate" x:DataType="m:HeaderItemViewModel">
	<Grid
		x:Name="RootGrid"
		Width="235"
		Height="65"
		Margin="4"
		HorizontalAlignment="Stretch"
		VerticalAlignment="Stretch">
		<Grid.RowDefinitions>
			<RowDefinition Height="65" />
		</Grid.RowDefinitions>

		<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
			<Grid>
				<Border
					Height="58"
					Margin="0,0,12,0"
					Background="Transparent"
					BorderBrush="{StaticResource DmLightBlueBrush}"
					BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
					CornerRadius="5">
					<Grid>
						<controls:ImageEx
							x:Name="HeaderImg"
							Grid.Row="0"
							Height="51"
							Margin="0,0,0,0"
							HorizontalAlignment="Center"
							VerticalAlignment="Center"
							BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
							CornerRadius="4"
							Source="{Binding HeaderImg}"
							Stretch="UniformToFill" />
					</Grid>
				</Border>
			</Grid>

			<TextBlock
				Name="TitleTxt"
				Width="160"
				Margin="0,8,0,8"
				VerticalAlignment="Center"
				FontFamily="Retina"
				FontSize="16"
				FontWeight="Bold"
				LineHeight="16"
				Text="{Binding HeaderTitle, FallbackValue=Loading}"
				TextTrimming="WordEllipsis"
				TextWrapping="Wrap" />
		</StackPanel>
	</Grid>
</DataTemplate>

Happy Coding!


lundi 29 novembre 2021

UWP using ChangeView() on a ScrollViewer so that the focused item can be placed with a specific offset [C#]

The initial need was that I needed to be able to sync two listview together, however one of these listview had horizontal items and was not selectable which meant that it would not inform the first listview that the selected view had changed. 

The first listview is not only and indicator but can also allow to quickly access a certain row in our main listview, here is quick screenshot to better understand what I wanted to do, in red the two selected items that need to be sync and in the other colors that other ListView.

The main issue was to be able to detect when the user scrolled down on the second ListView Vertical and to also know at what index position he was at in the listview.

First we need to get the ScrollView of our ListView to detect when the user was scrolling.
We will create class ListViewBaseExtension.cs which will hold the following method
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 public static ScrollViewer GetScrollViewer(this DependencyObject element)
{
	if (element is ScrollViewer)
	{
		return (ScrollViewer)element;
	}

	for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
	{
		var child = VisualTreeHelper.GetChild(element, i);

		var result = GetScrollViewer(child);
		if (result == null)
		{
			continue;
		}
		else
		{
			return result;
		}
	}

	return null;
}
This method uses VisualTreeHelper which can allow us to easily access the UI visual tree.
Next, we will use the GetScrollViewer() method on our ListView to get our ScrollViewer:
//our second vertical listview
AppListView.Loaded += (sender, e) =>
{
	//getting scrollview
	ScrollViewer scrollViewer = AppListView.GetScrollViewer(); //Extension method
	if (scrollViewer != null)
	{
		scrollViewer.ViewChanging += ScrollViewerListView_ViewChanging;
	}
};
Now let's look at our method called ScrollViewerListView_ViewChanging that handles the ViewChanging event from our scrollViewer.
Everytime we will need to get the position of our items in the list using GetAllItemsPositions(), also we need to calculate the height that that user has scrolled down which will give us a currentVerticalPosition.  We will then use this to find an item that is on this position, which will then allow us to inform which item should be selected on the first listview:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private void ScrollViewerListView_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
        //Offset of first listview
	double AdditionOffSetToAdd = -200;

	MyItemPositions = AppListView.GetAllItemsPositions();


	if (MyItemPositions != null)
	{
		var currentVerticalPosition = e.FinalView.VerticalOffset + AdditionOffSetToAdd;

		double itemIndex = MyItemPositions.Values.Where(a => a >= currentVerticalPosition).FirstOrDefault();

		CurrentVisibleItemIndex = MyItemPositions.FirstOrDefault(x => x.Value == itemIndex).Key;

		//Debug.WriteLine($"CurrentVisibleItemIndex :{CurrentVisibleItemIndex}");
		//Debug.WriteLine($"previousItemIndex :{previousItemIndex}");

		if (previousItemIndex != CurrentVisibleItemIndex)
		{
			//update previous
			previousItemIndex = CurrentVisibleItemIndex;

			//Debug.WriteLine("VerticalOffset :{0}", e.FinalView.VerticalOffset);
			//Debug.WriteLine("possible visible item {0}", CurrentVisibleItemIndex);

			CurrentItemIndexChangedCommand();
		}
	}
}

public event EventHandler CurrentItemIndexChanged;
private void CurrentItemIndexChangedCommand()
{
	CurrentItemIndexChanged?.Invoke(this, new EventArgs());
}
By listening to the event CurrentItemIndexChanged, we can now update our selected item on on first listview.

You can find the sample application here: https://github.com/Delaire/Samples/tree/master/SyncTwoListviews

mercredi 6 octobre 2021

Adding custom Input Validation on TextBox and PasswordBox [UWP][XAML]

We are going to go over how to create a custom TextBox & PasswordBox with a new visual state.

I needed this kind on input box for my account creation view, where the user inputs his email and his password, the default controls don't allow you to set these controls in a invalide state when you check for example if the email is valid and if the password is strong enough. 
What I wanted to do is set the border of the control to red when the user had not meet the necessary steps to go to the next step in the account creation process.

While we wait for WinUI 3.0 to support input validation here is my very simple version of how I implemented one on a TextBox and PasswordBox.

The idea was to add a property called HasError to my new control and bind it to my ViewModel, when the HasError property is changed we can use VisualStateManager to change the visual of our control. What I learned was that you can create a custom TextBox but not a PasswordBox as the PasswordBox is a sealed class.

TextBox custom control

Here is my simple control with HasError property added to a TextBox my controler is called  LoginValidatingTextBox.cs

 public class LoginValidatingTextBox : TextBox
    {
        public LoginValidatingTextBox()
        {
             this.DefaultStyleKey = typeof(LoginValidatingTextBox);
        }

        public bool HasError
        {
            get { return (bool)GetValue(HasErrorProperty); }
            set { SetValue(HasErrorProperty, value); }
        }

        public static readonly DependencyProperty HasErrorProperty =
            DependencyProperty.Register("HasError", typeof(bool), typeof(LoginValidatingTextBox), new PropertyMetadata(false, HasErrorUpdated));


        // This method will update the Validation visual state which will be defined later in the Style
        private static void HasErrorUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LoginValidatingTextBox textBox = d as LoginValidatingTextBox;

            if (textBox != null)
            {
                if (textBox.HasError)
                    VisualStateManager.GoToState(textBox, "InvalidState", false);
                else
                    VisualStateManager.GoToState(textBox, "ValidState", false);
            }
        }
    }


Next, we need to add this VisualStateGroup to the default style of my TextBox so that when we have the InvalideState activated we can update our TextBox UI as we wish.

<VisualStateGroup x:Name="ValidationState">
 <VisualState x:Name="InvalidState">
  <Storyboard>
   <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource DmRedBrush}" />
   </ObjectAnimationUsingKeyFrames>
  </Storyboard>
 </VisualState>
 <VisualState x:Name="ValidState">
  <Storyboard />
 </VisualState>
</VisualStateGroup>

Here is how we can implement this control in our UWP app

<controls:LoginValidatingTextBox
 x:Name="AddressTextbox"
 HasError="{Binding InvalidEmailErrorVisible}"
 PlaceholderText="Email address"
 Text="{Binding UserEmailAddress}" />

And, now when InvalidEmailErrorVisible is true we will update our TextBox as needed.

PasswordBox custom control

Next we will try to and do the same thing for the password box is not as pretty as you cant inherit from the base control you have to do it in CS of your view.

First off you need to add the same VisualStateGroup to the style of your PasswordBox same as the TextBox, int he code behind of your view you will need to listen to when your PasswordErrorVisible property has changed and call a method that will call:

VisualStateManager.GoToState(UI ELEMENT, STATE YOU WISH, false)

Here is the full code:

private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
 Debug.WriteLine(e.PropertyName);

 switch (e.PropertyName)
 {
  case "PasswordErrorVisible":
   PasswordErrorChanged();
   break;
 }
}

private void PasswordErrorChanged()
{
 if (ViewModel.PasswordErrorVisible)
  VisualStateManager.GoToState(password, "InvalidState", false);
 else
  VisualStateManager.GoToState(password, "ValidState", false);
}

and there you have it fr both TextBox and PasswordBox we now have an invalid state.

Hope this helps!
Happy coding.





jeudi 1 juillet 2021

Microsoft Login for UWP on Windows 10 apps and Xbox One using Microsoft Graph [2020] [C#]

We are going to go over how to implement Microsoft login inside your application on Windows 10 apps and Xbox One apps.


We used to use the old login endpoint login.microsoft.com to login our users and today we have update our app to use the new graph endpoint from Microsoft you can download the nuget package called Microsoft.Graph and also Microsoft.Identity.Client.


Microsoft has greatly improved how you can login to its API compared to how I used to login or it might have been me that was not doing the best way.

Next we will try to login a user using the MS documentation.  We are using simple scopes for our login "user.read" and our ClientId was registered in our Azure portal.

The login code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private async void CallMsGraphLogin()
{
	try
	{
		// Sign-in user using MSAL and obtain an access token for MS Graph
		GraphServiceClient graphClient = await SignInAndInitializeGraphServiceClient(scopes);

		// Call the /me endpoint of Graph
		Microsoft.Graph.User graphUser = await graphClient.Me.Request().GetAsync();

		string token = await SignInUserAndGetTokenUsingMSAL(scopes);
		 
		MsTokenReceived?.Invoke(token);
	}
	catch (MsalException msalEx)
	{
		MsErrorLoginReceived?.Invoke($"Error Acquiring Token:{System.Environment.NewLine}{msalEx}");
		//await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalEx}");
	}
	catch (Exception ex)
	{
		MsErrorLoginReceived?.Invoke($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}");
		return;
	}
}


The app will register to the MS Graph API and will ask the user to login, which will generate the Microsoft login window.





We will not go over the methods call SignInUserAndGetTokenUsingMSAL and SignInAndInitializeGraphServiceClient as I would be saying the same thing as the Microsoft documentation which can be found here.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private static async Task<string> SignInUserAndGetTokenUsingMSAL(string[] scopes)
{
	// Initialize the MSAL library by building a public client application
	PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
		.WithAuthority(Authority)
		.WithUseCorporateNetwork(false)
		.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
		 .WithLogging((level, message, containsPii) =>
		 {
			 Debug.WriteLine($"MSAL: {level} {message} ");
		 }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
		.Build();

	// It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.
	IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
	IAccount firstAccount = accounts.FirstOrDefault();

	try
	{
		authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
										  .ExecuteAsync();
	}
	catch (MsalUiRequiredException ex)
	{
		// A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
		Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

		authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
										  .ExecuteAsync()
										  .ConfigureAwait(false);

	}
	return authResult.AccessToken;
}

/// <summary>
/// Sign in user using MSAL and obtain a token for MS Graph
/// </summary>
/// <returns>GraphServiceClient</returns>
private async static Task<GraphServiceClient> SignInAndInitializeGraphServiceClient(string[] scopes)
{
	GraphServiceClient graphClient = new GraphServiceClient(MSGraphURL,
		new DelegateAuthenticationProvider(async (requestMessage) =>
		{
			requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", await SignInUserAndGetTokenUsingMSAL(scopes));
		}));

	return await Task.FromResult(graphClient);
}








mercredi 2 décembre 2020

UWP Entity framework update your DB schema with a new column [UWP][C#][Database]

The point of this article is to go over the different steps that were needed to add a new column to one of my existing table.  This table was already used by my users using my application called My Stocks Alerts & Charts.

To add a new column or table to your existing DB you are going to need to create 3 new files.  The first new class will be called MyAlertQuotesModelSnapshot which inherit from ModelSnapshot and have the annotation of DbContext, this will hold the new table schema model of our updated table.

Next, we will create a class called MyFirstMigration this will also hold you new table schema model, this class will not inherit from anything but will have the annotations of DbContext and Migration.

Lastly the most important part we are going to create the class that will add the column to our table this will be called MyFirstMigration will inherit from Migration and will allow us to add our new column to our table by using migrationBuilder.AddColumn as follows:

migrationBuilder.AddColumn<string>(
                            name: "ExtraColumn",
                            table: "MyAlertQuotes",
                            nullable: true);

If you had wanted to add a new table that was called MyAlertQuotes we would have used migrationBuilder.CreateTable as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
protected override void Up(MigrationBuilder migrationBuilder)
{         

	migrationBuilder.CreateTable(
		name: "MyAlertQuotes",
		columns: table => new
		{
			Id = table.Column<Guid>(nullable: false),
			SymbolId = table.Column<string>(nullable: false),
			FullName = table.Column<string>(nullable: false),
			Currency = table.Column<string>(nullable: false),
			ExtraColumn = table.Column<string>(nullable: false),
			 
		},
		constraints: table =>
		{
			table.PrimaryKey("PK_MyAlertQuotes", x => x.Id);
		});
}

Which would have added a new table to our DbSchema. 

One last step, you will also have a class that inherits from DbContext, make you that you add check on initialization to make sure your users have migrated to the new Db schema

1
2
3
4
5
6
7
 public static void CheckMigrations()
{
	using (var db = new LocalStorageContext())
	{
		db.Database.Migrate();
	}
}

Personally in my App.xaml.cs I check to make sure that users have updated to my new Db Schema.

Here are my full classes:

20201101_MyFirstMigration.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using MyPersonalStocks.Helpers;
using System;

namespace MyPersonalStocks.Common.DataBase.Migrations
{
    public partial class MyFirstMigration : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {

            migrationBuilder.AddColumn<string>(
                            name: "ExtraColumn",
                            table: "MyAlertQuotes",
                            nullable: true);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            //migrationBuilder.DropColumn(
            //               name: "ExtraColumn",
            //               table: "MyAlertQuotes"
            //               );
        }
    }
}


20201101_MyFirstMigration.design.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using MyPersonalStocks.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyPersonalStocks.Common.DataBase.Migrations
{
    [DbContext(typeof(LocalStorageContext))]
    [Migration("20201101_MyFirstMigration")]
    partial class MyFirstMigration
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.4");

            modelBuilder.Entity("MyPersonalStocks.Model.DbModel.AlertQuotesDb", b =>
            {
                b.Property<Guid>("Id");

                b.Property<string>("SymbolId");
                b.Property<string>("FullName");
                b.Property<string>("Currency");
                b.Property<string>("ExtraColumn");

                b.HasKey("Id");

                b.ToTable("MyAlertQuotes");
            });
        }
    }
}


MyAlertQuotesModelSnapshot.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using MyPersonalStocks.Helpers;
using MyPersonalStocks.Model.DbModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyPersonalStocks.Common.DataBase.Migrations
{


    [DbContext(typeof(LocalStorageContext))]
    partial class MyAlertQuotesModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.4");

            modelBuilder.Entity("MyPersonalStocks.Model.DbModel.AlertQuotesDb", b =>
            {
                b.Property<Guid>("Id");

                b.Property<string>("SymbolId");
                b.Property<string>("FullName");
                b.Property<string>("Currency");
                b.Property<string>("ExtraColumn");
                b.HasKey("Id");

                b.ToTable("MyAlertQuotes");
            });
        }
    }

}


jeudi 10 octobre 2019

Building a SMARTER Placeholder Loading UI 2.0 using only C# for your UWP application [C#,Storyboard]

Previously to build my Placeholder UI I would be using a UserControl, that contained Grid, Storyboard, adding a gradient styling and a ContentControl.
However this could be quite resource intensive which is not great and also I was not happy with my initial implementation.

After a quick refactoring it was decided that a blinking rectangle would better fit the need and we would not need to have to animate something going to left to right.





To make things simple it was decided to create a new Class that would inherited from Grid:

public class PlaceholderSkeletonView : Grid

Next we just needed to add a Storyboard animation that will make this element "blink"

var blinkAnimation = new DoubleAnimation()
    {
         From = 1,
         To = 0.4,
         AutoReverse = true,
         //blink every 1sec
         Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1)),
         RepeatBehavior = RepeatBehavior.Forever
    };

And lastly all we need is to add this blinking animation to our Storyboard:

 myLoadingStoryboard.Children.Add(blinkAnimation);

 Storyboard.SetTarget(myLoadingStoryboard, this);
 Storyboard.SetTargetProperty(blinkAnimation, "Opacity");

 Loaded += (a, e) => myLoadingStoryboard.Begin();

All in all it was


Full class here:


public class PlaceholderSkeletonView : Grid
{
        Storyboard myLoadingStoryboard { get; set; }
        public PlaceholderSkeletonView()
        {            
            myLoadingStoryboard = new Storyboard();
            var blinkAnimation = new DoubleAnimation()
            {
                From = 1,
                To = 0.4,
                AutoReverse = true,
                //blink every 1sec
                Duration = new Windows.UI.Xaml.Duration(TimeSpan.FromSeconds(1)),
                RepeatBehavior = RepeatBehavior.Forever
            };

            myLoadingStoryboard.Children.Add(blinkAnimation);

            Storyboard.SetTarget(myLoadingStoryboard, this);
            Storyboard.SetTargetProperty(blinkAnimation, "Opacity");

            Loaded += (a, e) => myLoadingStoryboard.Begin();
        }
}


In your XAML it would look as follows for example:


<StackPanel Orientation="Horizontal">
     <local:PlaceholderSkeletonView
                Width="120"
                Height="120"
                Margin="20"
                Background="#ebebeb" />
          <StackPanel Margin="20" Orientation="Vertical">
                <local:PlaceholderSkeletonView
                       Width="190"
                       Height="20"
                       Margin="10"
                       Background="#ebebeb" />
                <local:PlaceholderSkeletonView
                       Width="150"
                       Height="20"
                       Margin="10"
                       Background="#ebebeb" />
                <local:PlaceholderSkeletonView
                       Width="120"
                       Height="20"
                       Margin="10"
                       Background="#ebebeb" />
           </StackPanel>
</StackPanel>


Happy Coding!

Github repo:  https://github.com/Delaire/UIPlaceholder







dimanche 1 septembre 2019

XAMARIN Tizen for Tvs and .NET Standard 2.0 [9-2019][XAML, C#]

My return of Experience on XAMARIN for TIZEN TVs the 1st of September 2019.

Here is documentation where you can find about Xamarin Forms for Tizen:

After a long summer of slowly migrating all of my app libraries to .NET Standard 2 I could finally really test Tizen to see how much code I could share between my Xbox One UWP app and a new Tizen TV app.

Framework versions:
  • Tizen.NET (5.0.0.14562)
  • Tizen.TV.UIControls (1.0.0)
  • Tizen.NET.Sdk (1.0.1) (this nuget would not update to the latest version 1.0.3)
  • Xamarin.Forms(4.1.0.709249) (forms 4.2.0.709249 was generating an error when building the app)

I discovered a new library called Tizen.TV.UIControls  which greatly improve the UI layout for the TV.  This library contains awesome features like:

MediaPlayer












RecycleItemsView


Horizontal lists


 
 
You can find the github repo here.

Tizen TV Xamarin C#


To get started I wanted to do a simple HTTP call to get data and set this data into a horizontal list, well I never got passed this step...

As for all applications you need to tell the app that it will have special privileges.  First you will need to start by adding  http://tizen.org/privilege/internet which is located in the file tizen-manifest.xml and then the tab Privileges. 

Next here is the code I am using to get weather information:


HttpClient client;
        private async System.Threading.Tasks.Task GetWeatherServiceAsync()
        {             
            var uri = new Uri("https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22");

            try
            {
                client = new HttpClient();

                var response = await client.GetAsync(uri);

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = await response.Content.ReadAsStringAsync();

                }
            }
            catch (Exception ex)
            {
                var error = ex;
            }

        }


And the issue is that no matter what I tried client would never be initialized:



So as of today I cant do an HTTP call on my Tizen .NET Xamarin application, ill keep you posted as soon as I find a solution.

After looking more closely at all of the GitHub Sample for Tizen TV app they all generate their sample data by hand which probably means that their is an issue somewhere for TV apps, I do hope Samsung fix this ASAP as a TV app without being able to do HTTP calls is not very practical...


Happy coding

- UPDATE 5 Septembre 2019 - 
After discussion with Samsung it was found that I was using an old build of the Tizen emulator.
BuildInfo: "tizen_20181210.1228946"
BuildDate : "20181210_063435"

When I should have been using a 2019 Build, once I updated to the latest build everything was working.  IMO inside Visual Studio I wish I have had a warning telling me that I was using an old build.