This publication is part of the .NET MAUI Advent Calendar 2023, an initiative led by Héctor Pérez, Alex Rostan, Pablo Piovano, and Luis Beltrán. Check this link for more interesting articles about .NET MAUI created by the community.
The most commonly used option for storing structured data locally in a .NET MAUI app is SQLite database. However, because it is possible to create a .NET MAUI Blazor Hybrid app, a new option can be considered: IndexedDB, a database that is built into a browser,
Definition
Taken from here:
IndexedDB is a way to persistently store data inside the browser. Because it lets you create web applications with rich query abilities regardless of network availability, your applications can work both online and offline.
Before writing this post, I didn't know if it was possible or not to use IndexedDB in a Blazor Hybrid app. In theory, it is possible, so I thought it would be an interesting case to explore. I'm not sure if there are advantages or disadvantages over SQLite, but I do know that there is the typical benefit around Blazor Hybrid: If you already have a web application which stores local data using IndexedDB, then you can bring your code to a mobile app using Blazor Hybrid.
Let's demonstrate this. Moreover, I am going to use the experimental MauiHybridWebView component that was presented a few months ago and highlighted during the .NET Conf 2023. This component allows you to use JavaScript in your .NET MAUI app, it also enables communication between the code in the WebView (JavaScript) and the code that hosts the WebView (C#/.NET), so you can have a React JS app hosted in a .NET MAUI native app. It's amazing!
Step 1. Create & configure the project
First of all, create a Blazor Hybrid .NET MAUI app. You must use .NET 7 at minimum for the MauiHybridWebView component to work.
Add the EJL.MauiHybridWebView NuGet package in your application:
Using the Solution Explorer, open the Resources folder of your project. Inside the raw folder, create a new folder named hybrid_root. Then, create two new files there: index.html and dbscript.js:
Step 2. Add the .NET MAUI C#/XAML mobile code:
Open MauiProgram.cs. Add the HybridWebView support in the CreateMauiApp method, just before returning the MauiApp built instance:
...
public static MauiApp CreateMauiApp()
{
...
builder.Services.AddHybridWebView();
return builder.Build();
}
Now, open MainPage.xaml and delete the controls (just keep the top ContentPage definitions). Then, modify it according to the following instructions:
- Add a reference to the
HybridWebViewassembly. - Add a
HybridWebViewcomponent in theContentsection of theContentPage. Set the following properties and values:- Set
Nametohwv. - Set
HybridAssetRootto thehybrid_rootfolder created in Step 1. - Set
MainFileto theindex.htmlfile created in Step 1. - Set
RawMessageReceivedto anOnJsRawMessageReceivedmethod that will be created in the C# code.
- Set
This is a sample implementation code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
...
xmlns:ejl="clr-namespace:HybridWebView;assembly=HybridWebView"
...>
<ejl:HybridWebView x:Name="hwv"
HybridAssetRoot="hybrid_root"
MainFile="index.html"
RawMessageReceived="OnJsRawMessageReceived" />
</ContentPage>
Then, open MainPage.xaml.cs. Here:
- Add the
HybridWebViewnamespace. - Enable the Web Dev Tools from the
HybridWebViewcomponent in the constructor afterInitializeComponentmethod. - Create an async method called
OnJsRawMessageReceivedwhich displays a message sent by JavaScript code. It uses aDispatcherfor a safe interaction with the UI. The message is included in theHybridWebViewRawMessageReceivedEventArgsargument from theHybridWebViewcomponent.
This is the code:
using HybridWebView;
namespace NetMauiIndexedDb
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
hwv.EnableWebDevTools = true;
}
private async void OnJsRawMessageReceived(object sender, HybridWebView.HybridWebViewRawMessageReceivedEventArgs e)
{
await Dispatcher.DispatchAsync(async () =>
{
await DisplayAlert("JavaScript message", e.Message, "OK");
});
}
}
}
Step 3. Add the HTML/JS web code:
For the index.html page, you can:
- Define a basic HTML5 page that references two scripts:
HybridWebView.js(from the NuGet package) anddbscript.js(which includes the code to handle the IndexedDB database). - Add a button. When it is pressed, it will execute a
load_datamethod defined indbscript.js. - Add an ordered list. This one will dynamically show the data stored in a
studenttable from the IndexedDB database.
Please note, the dbscript.js reference is added before the body ends, because we need the HTML elements to be created first.
This is the code:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="_hwv/HybridWebView.js"></script>
</head>
<body>
<div>
<button onclick="load_data()">Load Data</button>
</div>
<br />
<div>
<h2>Students</h2>
<ol></ol>
</div>
<script src="dbscript.js"></script>
</body>
</html>
Finally, for dbscript.js, two local variables are created: students_table (for the name of the table that will exist in our database) and students (the ordered list that will display the table content).
Moreover, some methods are defined:
-
load_data: It simply invokesinit_databasemethod. -
init_database: First of all, it checks if indexedDB is supported in the browser. If so, then it tries to set up a connection to aschoolDBIndexedDB database. If it doesn't exist yet, it is created. Theonupgradeneededevent is invoked once the database is created for the first time, and here we create thestudentstable (object store). Theonsuccessevent is fired once we have successfully connected to the database, and here theinsert_studentmethod is called twice. -
insert_student: As you can expect, this method adds an entry in thestudentstable. -
show_students: This method firstly removes all the content of the ordered list. Then, it gets all entries from thestudentstable. ACursorobject iterates over each record in the table to create dynamic HTML content to display each entry.
By the way, the SendRawMessageToDotNet method (from the HybridWebView component) is used to send a message from JavaScript to .NET code.
let students_table = 'Students';
let students = document.querySelector("ol");
function load_data() {
init_database();
}
function init_database() {
if (!window.indexedDB) {
HybridWebView.SendRawMessageToDotNet("Your browser doesn't support IndexedDB");
return;
}
let db;
const request = indexedDB.open('schoolDB', 1);
request.onerror = (event) => {
HybridWebView.SendRawMessageToDotNet("Database error: " + event.target.errorCode);
};
request.onsuccess = (event) => {
db = event.target.result;
insert_student(db, {
name: 'John Doe',
faculty: 'FAI'
});
insert_student(db, {
name: 'Jane Doe',
faculty: 'FAME'
});
show_students(db);
};
request.onupgradeneeded = (event) => {
db = event.target.result;
let store = db.createObjectStore(students_table, {
autoIncrement: true,
keyPath: 'id'
});
};
}
function insert_student(db, student) {
const txn = db.transaction(students_table, 'readwrite');
const store = txn.objectStore(students_table);
let query = store.put(student);
query.onsuccess = function (event) {
console.log(event);
};
query.onerror = function (event) {
console.log(event.target.errorCode);
}
txn.oncomplete = function () {
db.close();
};
}
function show_students(db) {
while (students.firstChild) {
students.removeChild(students.firstChild);
}
const txn = db.transaction(students_table, 'readwrite');
const store = txn.objectStore(students_table);
store.openCursor().addEventListener('success', e => {
const pointer = e.target.result;
if (pointer) {
const listItem = document.createElement('li');
const h3 = document.createElement('h3');
const pg = document.createElement('p');
listItem.appendChild(h3);
listItem.appendChild(pg);
students.appendChild(listItem);
h3.textContent = pointer.value.name;
pg.textContent = pointer.value.faculty;
listItem.setAttribute('data-id', pointer.value.id);
pointer.continue();
} else {
if (!students.firstChild) {
const listItem = document.createElement('li');
listItem.textContent = 'No Students.'
students.appendChild(listItem);
}
HybridWebView.SendRawMessageToDotNet("Data has been loaded");
}
});
}
Step 4. Test the app.
Now, you should be able to build the project and test it without any issues. I will test it in a Windows app first.
Here is the initial content of our app. As you can observe, only HTML components (a button and a title) are displayed, since we didn't really add any XAML UI except the Web View that hosts the web elements:
Click on the button. The JavaScript code that interacts with an IndexedDB database in the browser is executed, and the following output is presented:
Success! Our app has created a local database with one table and two entries, which are displayed in the app. Moreover, a JavaScript message was passed to the C# part, and this communication is possible thanks to the HybridWebView component. Would it be possible to pass the data instead of a simple message so we can build a UI using XAML? I guess I've just found a new topic to write about, so I will explore this scenario soon =)
Finally, don't forget that since we enabled the Web DevTools, we can bring them to debug or, even better, to see our database:
Great! The IndexedDB storage contains the schoolDB database that we created in our app (under Application --> Storage section). Then, a Students table containing two entries is there, so everything is working as expected.
Before we go, two disclaimers:
It might happen that when you run the app, the window is empty. I'm not sure if this is a bug of the
HybridWebView(don't forget that it is an experimental component) or if it happens because theWeb DevToolswere enabled. Simply run the app again and it should work (try again if not, it will eventually show up). I noticed that when I comment the line that enables the tools, the app works without any issues, so probably this is an issue. I will do some exploration.If you run the app again, the entries will be duplicated. This is kind of obvious, since we insert them after the connection to the database is successful. You can clear the database/table by using the Web DevTools at any time, of course.
Well, that's it! You can find the source code of this project in my GitHub repo.
I hope this post was helpful for you. Remember to follow the rest of the interesting publications of the .NET MAUI Advent Calendar 2023.
Thank you for reading. Until next time!
Luis






Top comments (0)