Tag Archives: Javascript

Welcome to my Blazing Pizza Store! ;)

Here’s how I built my Blazing Pizza Store with the awesome .NET Blazor framework… Oh and let me share a quick recap of the awesome knowledge I gained from it!

So recently I embarked on a journey to learn the latest awesomeness of .NET sphere, the Blazor framework, just so that I would be able to leverage some good old C# into the Web and get rid of having to code in JavaScript! :3 lol

Well I just love learning new stuff and building kickass stuff with .NET so obviously I’m loving it! I set up this repository up in my github just to encourage myself to follow up on every possible tutorial and learn of this Blazor awesomeness top to bottom.

Blazor-Playground repo:
UdaraAlwis/Blazor-Playground

That right there is where I’ll be continuously posting of my little self-learning journey into Blazor Framework.

However I came across this lengthy tutorial article through one of my colleagues, published by the .NET Foundation github, called the blazor-workshop!

BlazingPizza, the Blazor Workshop!

So this tutorial features building a Blazor Web Application from ground up for a Online Pizza Ordering shop, called Blazing Pizza! pretty catchy eh! 😉

Before this I had followed up on a few tutorials and video guides for self-learning Blazor bits, but man I gotta tell you this is the most comprehensive tutorial so far…

.NET Foundation, Blazor-workshop repo:
aka.ms/blazorworkshop

It features a step by step guidance on building a complete Blazor Web Application utilizing all the awesomeness it can offer, and it truly elevated my knowledge to another level! Hence I decided to share some of my learning experience with you guys…

Oh and I have published my finished project solution in github as well…

Udara’s Blazing Pizza Store:
UdaraAlwis/Blazor-Playground/BlazingPizzaApp

Published Web App:
https://blazingpizzaappudara.azurewebsites.net/

And I hosted the finished project in Azure as well! 😉

Feel free to try it out! 😀

Prerequisites?!

As for as some pre-requisites, of course you need some basic practice of HTML and CSS, having an idea of how these Web elements comes in together to form web pages and all, would definitely be helpful.

Its better to have a good overall knowledge of .NET programming with C#, as far as the language goes. Even though we’re going to be developing Blazor, it would some advantage if you had previous experience with ASP.NET, as I’ve seen similar elements.

I would not suggest you use this tutorial as your first ever Blazor try out, hence they provide us a pre-set up solution, with the basic Blazor Client UI and Backend API. So it could get confusing or overwhelming to figure it out at once. So its best if you first try out a few simpler demos from Microsoft docs, or a simple follow along Youtube tutorial, there are plenty out there.

Get started with ASP.NET Core Blazor
Build your first Blazor app

Then once you’re familiar with the project solution structure and all, you’re good to go!

Some Tips…

So I started off by copying their starter solution into my own repository, and from there I started following through the steps in the tutorial.

I would read through a given step, understand the objective of it and type the code all by myself. Sometimes I would even try to memorize the syntax and type it on accordingly to the understand I’ve got in that step.

It’s better not to copy paste the code to your own solution directly, I found typing the code by yourself is more effective in the learning curve, so that you get to familiarize with every little detail specially in the syntax.

Take plenty of intervals, for better productivity. This is a long tutorial, but it could be completed in a day, but its best to take some intervals in between to help clear up the exhaustion.

If a certain step is not clear, then you should complete it anyways, then move to the next step, and come back to it later until you get a clear understanding. Some times reading through a given step several times helped me understand better.

Learning journey!

This project BlazingPizza solution is a Blazor Client Side project hosted with ASP.NET, therefore it will be utilizing awesomeness of Web Assembly during the run time on client browser.

Components, Components everywhere!

Component-izing everything is the key in Blazor! Each page should be comprised of Component blocks, which has their own responsibilities separated, increasing the reusability of our code.

Layouts are also built as Components, where you inherit them component from LayoutComponentBase, such as in MainLayout.razor

@inherits LayoutComponentBase

They have a Body parameter where you will be placing the content in.

Dependency Injection!

@inject directive let’s your inject objects into your page that are registered in the DI container, such as,

@page "/"
@inject HttpClient HttpClient

Also you need to make sure those objects are properly registered in the DI, which is located in Program.cs

builder.Services.AddTransient(...);
...
builder.Services.AddScoped(...);

Parameterizing Components…

@code {
    [Parameter] public Pizza Pizza { get; set; }
}

[Parameter] attribute defines a parameters of a Component

Handle HTML Events..

You can specify which HTML event you want to handle using the corresponding HTML attribute in that and then specify the C# delegate you want called..

<li 
    @onclick="@(() => Console.WriteLine(special.Name))" .... >
</li>

Two way binding…

@bind directive lets you two-way bind a value between C# and HTML elements, also with @bind:event you can specify which element triggers the value change as follows…

<input type="range" min="@Pizza.MinimumSize" 
max="@Pizza.MaximumSize" step="1" 
@bind="Pizza.Size" @bind:event="oninput" />

Here we bind the value of <input> element to Pizza.Size, but it will change the value on the oninput event.

Component Event Handling!

You can define Events in a Component by creating EventCallback properties. Then Parent Component can subscribe to those events of a Child Component.

[Parameter] 
public EventCallback OnCancel { get; set; }

Then this Child Component’s elements can trigger it,

<button class="btn btn-secondary mr-auto" 
@onclick="OnCancel">Cancel</button>

And parents who are subscribed can get notified on up on it.

<ConfigurePizzaDialog Pizza="configuringPizza" 
OnCancel="CancelConfigurePizzaDialog" />

CancelConfigurePizzaDialog will be pointing to a method in C#.

Url Parameters into Components!

You can pass parameters into Components from the routing Url, as you see here in the @page directive we define the route along with the [Parameter] with the same name.

@page "/myorders/{orderId:int}"

@code {
    [Parameter] public int OrderId { get; set; }
}

Other parameter types such as string, bool, datetime and guid are supported.

Force Update UI!

Forcefully re-render a component by calling StateHasChanged() method which is built in function in Blazor.

This is handy when you don’t know exactly if the data has changed but wants to make sure its reflected in the UI.

Programmatic Navigation..

NavigationManager allows you to programmatically Navigate in your Blazor app.

@inject NavigationManager NavigationManager
...
...
NavigationManager.NavigateTo($"myorders/{newOrderId}");

AppState pattern…

Save the state of an object in the DI container,

builder.Services.AddScoped<OrderState>();

Saving the state will help you retain data during page navigation across components.

Data Validation!

Using Annotations on the data model you can easily implement Server side data validation. You can start by adding DataAnnotations validation rules onto the model classes.

public class Address
{
	public int Id { get; set; }

	[Required, MaxLength(100)]
	public string Name { get; set; }
	
	[Required(ErrorMessage = "Yo we need for sure!"), MaxLength(50)]
	public string City { get; set; }

	...	
}

This will be picked up by [ApiController] endpoints for validation in Server side.

Then for Client side validation, using EditForm component you can easily implement form validation in Blazor.

<div class="main">
    <EditForm Model="OrderState.Order.DeliveryAddress"  OnValidSubmit="PlaceOrder">
        
		... 

        <button class="checkout-button btn btn-warning" @onclick="PlaceOrder">
            Place order
        </button>
		
		<DataAnnotationsValidator />
		<ValidationSummary />
    </EditForm>
</div>

The above ValidationSummary, displays a basic list of errors, if you need something nicer then its better to decorate those <input> data fields with ValidationMessage instead.

<div class="form-field">
    <label>Name:</label>
    <div>
        <input @bind="Address.Name" />
        <ValidationMessage For="@(() => Address.Name)" />
    </div>
</div>

For better UX use Blazor built in components, such as InputText, InputCheckbox, InputDate, InputSelect, etc…

<div class="form-field">
    <label>Name:</label>
    <div>
        <InputText @bind-Value="Address.Name" />
        <ValidationMessage For="@(() => Address.Name)" />
    </div>
</div>

Authorization!

For enforcing Authorization in Server side, add the [Authorize] attribute on the API Controllers, which is provided by Microsoft.AspNetCore.Authorization namespace.

[Route("orders")]
[ApiController]
[Authorize]
public class OrdersController : Controller
{
    ...
}

This will make sure all incoming calls should be Authorized.

For the Client side we use AuthenticationStateProvider, which is provided by Microsoft.AspNetCore.Components.WebAssembly.Authentication package. Then to enable the authentication services for your Blazor Client, add a call to AddApiAuthorization in Program.cs

public static async Task Main(string[] args)
{
    ....
    // Add auth services
    builder.Services.AddApiAuthorization();
    ...
}

RemoteAuthenticatorView component orchestrates the authentication flow in the Blazor Client app. So to enable this make sure to add the Authentication.razor component.

@page "/authentication/{action}"

<RemoteAuthenticatorView Action="@Action" />

@code{
    [Parameter]
    public string Action { get; set; }
}

It handles all authentication actions such as register, login, profile, and logout.

We need to maintain the Auth state across the client app and inherit into child components, so let’s use CascadingAuthenticationState that wraps the Router of app.razor

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Program).Assembly" Context="routeData">
        ...
    </Router>
</CascadingAuthenticationState>

AuthorizeView will let you control displaying of elements based on the Auth state.

<AuthorizeView>
	<Authorizing>
		...
	</Authorizing>
	<Authorized>
		...
	</Authorized>
	<NotAuthorized>
		...
	</NotAuthorized>
</AuthorizeView>

Use the IAccessTokenProvider injected to the pages for acquiring an AccessTokens whenever you need to call the API.

var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var accessToken))
{
	...
}
else
{
	NavigationManager.NavigateTo(tokenResult.RedirectUrl);
}

AuthenticationStateTask lets you check programmatically if the user is logged in or not. You can use it as a [CascadingParameter] in any page, thanks to the CascadingAuthenticationState that we added earlier.

[CascadingParameter] 
public Task<AuthenticationState> AuthenticationStateTask { get; set; }

Use it to access User.Identity.IsAuthenticated for checking the User’s Auth state.

protected override async Task OnInitializedAsync()
{
	var authState = await AuthenticationStateTask;
	if (!authState.User.Identity.IsAuthenticated)
	{
		NavigationManager.NavigateTo("authentication/login?redirectUri=/checkout", true);
	}
}

AuthorizeRouteView controls the direct access to routes based on Auth state

<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Program).Assembly" Context="routeData">
        <Found>
            <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
                <NotAuthorized>
                    ...
                </NotAuthorized>
                <Authorizing>
                    ...
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            ...
        </NotFound>
    </Router>
</CascadingAuthenticationState>

You will lose state during redirection, specially in authentication flow, but you can preserve it by implementing RemoteAuthenticationState.

public class PizzaAuthenticationState : RemoteAuthenticationState
{
    public Order Order { get; set; }
}

You need to register it in the DI Services with AddApiAuthorization<PizzaAuthenticationState>() and update the Authentication.razor accordingly by replace the RemoteAuthenticatorView with RemoteAuthenticatorViewCore set up.

@page "/authentication/{action}"

<RemoteAuthenticatorViewCore TAuthenticationState="PizzaAuthenticationState"
    AuthenticationState="RemoteAuthenticationState"
    OnLogInSucceeded="RestorePizza"
    Action="@Action" />

@code{
    ...

    public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState();

    ...
}

You can use the LogOutSucceeded in Authentication component to set the page to redirect to once the user logs out.

builder.Services.AddApiAuthorization<PizzaAuthenticationState>(options =>
{
    options.AuthenticationPaths.LogOutSucceededPath = "";
});

Javascript Interop!

Use IJSRuntime to invoke JavaScript functions from C# code.

await JSRuntime.InvokeVoidAsync
("javascript.functionname", param1, param2);

var result = await JSRuntime.InvokeAsync<bool>
("javascript.functionname", param1);

You can call void methods and even await for results as shown above.

Templated Components…

Using Razor Class Library you can create your own components library for your project solution.

Templated Components can be defined as those components that accepts body content as a parameter. You can create these to increase the reusability and build truly decoupled components.

Use a parameter of type RenderFragment which is a delegate type that the runtime has special handling for, to place content inside a Templated Component.

[Parameter] 
public RenderFragment ChildContent { get; set; }

You can also add multiple RederFragment parameters in a component. We can create a generic-typed component using the @typeparam directive, to support any type of data as follows, @typeparam TItem

[Parameter] 
public RenderFragment Loading { get; set; }
[Parameter] 
public RenderFragment<TItem> Item { get; set; }

and you can set the type and pass parameter content to the generic template by,

<div class="main">
    <TemplatedList TItem="OrderWithStatus" Loader="@LoadOrders">
        <Loading>Loading...</Loading>
        <Item Context="item">
            Status: @item.StatusText
        </Item>
    </TemplatedList>
</div>

PWA bits!

Easily set up PWA features (installable, offline, push notifications) for Blazor, with service-worker.js configuration.

Simply create a service-worker.js file in your Blazor Client’s wwwroot directory, and add the ‘install’ and ‘fetch’ event listeners inside. Then to enable it add a reference of it to the index.html file, beneath the other script elements..

<script>navigator.serviceWorker.register('service-worker.js');</script>

To make your Web app installable, add the manifest.json file in wwwroot directory, which will include app set up details. Then add the reference of it inside the <head> element in index.html

<link rel="manifest" href="manifest.json" />

Setting up Push Notification, first execute the Push Notification permission request from the user through JS script and acquire the NotificationSubscription object, which will provide you the following object filled with Url, P256dh and Auth properties.

You will be passing it over to the API endpoint to save it in the DB with the UserId attached in it. Then when you need to send push notification to your User from your API backend, retrieve the NotificationSubscription object from the DB and use WebPush to execute the broadcast.

var payload = JsonSerializer.Serialize(new
{
	message,
	url = $"myorders/{order.OrderId}",
});
await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails);

Then to display that push notification you need to set up ‘push’ event listener in your service-worker.js and additionally to handle the user click on notification, you can set up ‘notificationclick’ event listener in there as well.

self.addEventListener('push', event => {
...

self.addEventListener('notificationclick', event => {
...

You can generate your cryptographic keys for the push notification set up at https://tools.reactpwa.com/vapid and make sure to update the publicKey in both client and server, then privateKey in server along with the subject, mailto:bingo@bingomail.bingo

Publish to Azure!

To Publish to Azure, you need to create an App Service and a Hosting Plan of Basic tier or higher. Right click on Blazor Server project node -> Publish and follow through the wizard to create your Publish profile.

Before deploying you need to set up a signing key with Azure Key Vault for the IdentityServer. So create a new Key Vault -> create new Certificate (copy the Subject value for later use) -> go to your App Service -> TLS/SSL Settings -> Private Key Certificates (.pfx) -> Import Key Vault Certificate -> Select the imported certificate and copy its thumbprint -> go to Configuration in same App Service -> Add the new key WEBSITE_LOAD_CERTIFICATES and use the thumbprint as the value -> Ok

Then update the BlazingPizza.Server appsettings.json with the Certificate Subject name you configured, “Name”: “CN=BlazingPizzaCertificateUdara”

Publish the App! you’re all done! 😉

It’s go time!

So at the end of all that awesome learning experience, I had built myself a beautiful Blazing Pizza Store web app! 😀

Woot! woot!

Update to the Tutorial…

By the time I almost finished writing this post, they seemed to have updated the tutorial a little bit for the better, such as using the BaseAddressAuthorizationMessageHandler for acquiring access tokens and moving the whole logic to a separate OrdersClient, which will increase the reusability of the code for sure! 😀

Conclusion!

This BlazingPizza workshop tutorial was one of the most comprehensive step by step guide I ever followed for learning Blazor given my last few weeks into this awesome journey. I’m not much of a Web dev fella given my extensive Mobile dev background, but I gotta say I picked up a lot about .NET Web development bits in this tutorial, not only that a complete picture of the awesomeness of Blazor framework.

I hope my humble effort of sharing the new knowledge I gained in Blazor development helps out any fellow devs out there striving to learn new stuff!

Share the love! 😀 Cheers yol!

Pushing the limits of Hybrid WebView in Xamarin.Forms!

Let’s push the possible limits of Hybrid WebView and build something awesome in Xamarin.Forms! 😀

Remember my last blog post, Building a bi-directional interop bridge with WebView in Xamarin.Forms! which is where I shared about building a bi-directional invoke bridge between Javascript and C# dotnet run time using Hybrid WebView.

So based on that I went on a little journey to push the limits of this Hybrid WebView and built some cool demos with it. So here I am sharing it all with yol! 😉

Pushing the limits!

BTW: This is going to be a continuation of my previous blog post, so better take a look at it first if you haven’t: Building a bi-directional interop bridge with WebView in Xamarin.Forms!

To put it into fancy words,

We built a bi-directional communication bridge, between completely different run times, with possibility of two way execution invoke on demand! Hybrid WebView…

So now, the question is, what can we do to push the limits of our Hybrid WebView? Sounds like something for the imagination to limit!

Behold the awesomeness!

Imagine, being able to load a Web page into you Xamarin.Forms WebView, and being able to access Device features such as Camera, GPS Location, Accelerometer, etc..

Basically we’re going access Device Native features through the WebView and retrieve data directly from C# .NET run time in our Xamarin.Forms app! You will be able to load any HTML content, either from a Web Hosted source or locally generated HTML source, it would work like a charm thanks to the Hybrid WebView!

In order to demonstrate this awesomeness, let’s try out the following feature for the demo implementation…

  • Capture Photos using Device Camera
  • Pick Photos from Device Gallery
  • Get Device Native information
  • Get Device GPS Location data

All those device features and data will be accessed from the Javascript running inside the Hybrid WebView! 😉

Like I mentioned before, we will have two HTML sources for this demo, a web hosted HTML source and a locally generated HTML source to make sure it works for either cases! 😀

Now here’s a bit about what will help us build this awesome demo..

– Bi directional bridge!
Being able to invoke executions directly between the Javascript environment and C# .NET Xamarin.Forms run time, is what is going to help us build this awesomeness!

Javascript <~> Xamarin.Forms (C# .NET)

So you can pass data from the Javascript that’s running inside the Hybrid WebView out into the C# .NET run time, as well as you can pass data directly into the Javascript that’s running inside the Hybrid WebView.

– Multiple parameters…
In my previous blog post I mentioned this at the end where you can extend the same implementation we did for our Hybrid WebView, into supporting multiple parameters, so here I will use this to demonstrate what we’re going to build!

Sneak Peak!

Now here’s some action on the go with this awesomeness… 😉

As usual I’ve hosted this whole demo project up on my github repo:

Code on github: https://github.com/UdaraAlwis/XFHybridWebViewAdvDemo

This is how we do it!

So let’s get right into it, but first let’s make sure some prerequisites.

– Basic Hybrid WebView implementation
Refer to my previous post and check out the implementation of the Hybrid WebView. Building a bi-directional interop bridge with WebView in Xamarin.Forms!

– Xamarin.Essentials set up to access the device native feature
You need to implement Xamarin.Essentials and set up all the device native features and their configuration for the features you wish to use. https://docs.microsoft.com/en-us/xamarin/essentials/

Multiple Parameters support!

Let’s begin with adding support for multiple parameters in our Hybrid WebView as follows…

public class HybridWebView : WebView
{
	private Action<string, string> _action;

	public void RegisterAction(Action<string, string> callback)
	{
		_action = callback;
	}

	public void Cleanup()
	{
		_action = null;
	}

	public void InvokeAction(string param1, string param2)
	{
		if (_action == null || (param1 == null && param2 == null))
		{
			return;
		}

		if (MainThread.IsMainThread)
			_action.Invoke(param1, param2);
		else
			MainThread.BeginInvokeOnMainThread(() => _action.Invoke(param1, param2));
	}
}

Code on github: /XFHybridWebViewAdvDemo/Controls/HybridWebView.cs

As you can see, I have defined Action property with two parameters, along side the setter method of it, RegisterAction() accepts Action instances with two parameters.

InvokeAction() which gets called from each native renderer level now accepts two parameters accordingly. As an addition I have added an enforced UI Thread execution using Xamarin.Essentials.MainThread feature, since we’re going to access IO heavy device features.

Following the same pattern you can add as many number of parameters as you wish! 😉

We need to add support for this in our Javascript implementation as well, so we create a pre defined separator (a pipe separator preferably “|”) that separates parameters in the data object we’re sending from javascript to Hybrid WebView’s Renderer’s script handler.

..
invokexamarinforms('PHOTO|CAMERA')
..

When our Hybrid WebView renderers receive the invoke from Javascript, we need to handle the incoming data object as follows by splitting it up using the separator we defined…

var dataBody = data;
if (dataBody.Contains("|"))
{
	var paramArray = dataBody.Split("|");
	var param1 = paramArray[0];
	var param2 = paramArray[1];
	((HybridWebView)hybridRenderer.Element).InvokeAction(param1, param2);
}
else
{
	((HybridWebView)hybridRenderer.Element).InvokeAction(dataBody, null);
}

Then we pass it on to the InvokeAction() event, for Xamarin.Forms level to handle whichever the Action has subscribed to it. This way you can handle as many parameters as you wish!

Launch invoke from HTML Javascript to C# .NET!

So here’s how we set up the simple implementation to call up the invokeXamarinFormsAction() method that we have defined in our Hybrid WebView platform Renderers. Well there’s not much different from what we implemented in my previous demo, but here we are passing multiple parameters into the javascript method upon the button click.

...

<script>
...
function invokexamarinforms(param){
    try{
        invokeXamarinFormsAction(param);
    }
    catch(err){
        alert(err);
    }
}
</script>

...

<button type="button" 
	onclick="invokexamarinforms('PHOTO|CAMERA')">
	Get from Xamarin.Forms</button>

...

This is something I’ve explained step by step in my previous blog post so I wouldn’t go into details in here. You can define as many onclick actions as you wish with the set of predefined parameters like “PHOTO|CAMERA” and “PHOTO|GALLERY”, even single parameters, “GPS” and “DEVICEINFO”, etc…

Next you need to handle those parameters in C# code to execute the specific action we’re targeting, as simple as a if-else block as follows, or even a switch statement would suffice.

...

private async void ExecuteActionFromJavascript(string param1, string param2)
{
	...	
	if (param1 != null && param1.Equals("PHOTO") && param2.Equals("CAMERA"))
	{
		var result = await _deviceFeaturesHelper.TakePhoto(this);
		if (result != null)
		{
			...
		}
	}
        ...
	else if (param1 != null && param1.Equals("DEVICEINFO"))
	{
		var result = await _deviceFeaturesHelper.GetDeviceData();
		if (result != null)
		{
			...
		}
	}	
	...
}

Based on the requested action we execute it in C#, in this case accessing Camera and Capturing a photo _deviceFeaturesHelper.TakePhoto() or even getting Device Native information _deviceFeaturesHelper.GetDeviceData() as shown above.

Let’s move to the next step of this chain…

Ping back from C# .NET to HTML Javascript!?

Now that we established pathway to call the C# .NET run time from Javascript, we’re able to invoke any action, but how do we get back the result data into the Javascript?

So in my previous blog article Talking to your WebView in Xamarin.Forms! which explains how easy it is to pass data into the Javascript rendered inside the WebView at run time, since our Hybrid WebView is an extension of the default WebView, we can use the same method here…

...
        var result = await _deviceFeaturesHelper.GetDeviceData();
        if (result != null)
        {
             await webViewElement
                   .EvaluateJavaScriptAsync($"setresult('{result}')");
        }
...

So we’re calling up on EvaluateJavaScriptAsync() with the Javascript function name that’s accepting to the results for this specific action. This function needs to be created inside the Javascript before hand, that is rendered inside the Hybrid WebView as follows…

...
<script>
...
	function setresult(value) {
		// - display the value in HTML
		// - send the data to server
	}
</script>
...

Once the data is passed into your Javascript function, You can do whaever you want with the data, be it display in the HTML, or send it up to a web server, your choice! 😉

Calling the Device Native!

Well this is quite simple if you know how to use Xamarin.Essentials to access device native features and other 3rd party plugins to access various features from Xamarin.Forms! But I’ll quickly walk through the code that I’m using in this demo.

I’ve basically created like a little Facade layer which handles all the device native features required as follows, and each method handles a given specific feature such as Camera and GPS features using their respective services of plugin calls…

public class DeviceFeaturesHelper
{
	public async Task<string> TakePhoto(ContentPage pageContext)
	{
		// launch Media Plugin to capture photos from camera
	
		...

		return imageAsBase64String;
	}
	
	public async Task<string> GetDeviceData() 
	{
		// launch Xamarin.Essentials to load device info

		return $"{nameof(DeviceInfo.Model)}: {device}<br />" +
			$"{nameof(DeviceInfo.Manufacturer)}: {manufacturer}<br />" + 
			$"{nameof(DeviceInfo.Name)}: {deviceName}<br />";
	}
}

Code on github: /XFHybridWebViewAdvDemo/DeviceFeaturesHelper.cs

So what you need to keep in mind is that we need to return a value that’s compatible to be displayed inside our WebView, based on HTML, that’s why you see I have modified some of those returned values accordingly, such as Image objects are returned as base64 strings and device information formatted as a text block with <br /> inline.

These methods are called from the Hybrid WebView’s Invoke action that we created before and results are returned to be pushed back into Javascript.

Handling Media Image objects…

Using Media Plugin for Xamarin.Forms, provides you with a MediaFile object which contains the Image object that you acquired either from Camera or Gallery, but how do we convert that into something that’s compatible to be pushed into Javascript-HTML environment?

The solution is,

MediaFile -> byte[] array -> Base64 string

We’re going to convert our MediaFile object into a byte[] array, then convert again into a base64 string, which makes it so much easier to transfer the data into the javascript run time and use that object for any purposes inside javascript itself. Here’s my code snippet for this…

public async Task<string> TakePhoto(ContentPage pageContext)
{
        ...
	var file = await CrossMedia.Current.TakePhotoAsync(...);

	// Convert bytes to base64 content
	var imageAsBase64String = Convert.ToBase64String(ConvertFileToByteArray(file));

	return imageAsBase64String;
}

private byte[] ConvertFileToByteArray(MediaFile imageFile)
{
	// Convert Image to bytes
	byte[] imageAsBytes;
	using (var memoryStream = new MemoryStream())
	{
		imageFile.GetStream().CopyTo(memoryStream);
		imageFile.Dispose();
		imageAsBytes = memoryStream.ToArray();
	}

	return imageAsBytes;
}

This is what you saw in the previous section which I have used in my /DeviceFeaturesHelper.cs
You can use this in the javascript functions as easy as below,

function setresult_takephoto(value) 
{
	document.getElementById("photoCamera_ResultElement").src 
                                             = "data:image/png;base64," + value;
}

Well that’s the entire set up of this awesome demo, so then let’s see it in action…

Fire it up!

Here’s the web page we’re loading: https://testwebpage.htmlsave.com/

Look at em on fire, Android, UWP and iOS side by side…

Code on github: https://github.com/UdaraAlwis/XFHybridWebViewAdvDemo

Conclusion…

This was just me pushing the limits of the Hybrid WebView to build awesome stuff with Xamarin.Forms! This will definitely come in handy whenever you get a scenario you need to implement an existing Web App into a Xamarin.Forms app, and you need to let the user use it as a Mobile App, with being able to access device native features.

Well that concludes it. Hope you find it useful!

Share the love! 😀 Cheers!