<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nick Alonge</title>
    <description>The latest articles on DEV Community by Nick Alonge (@nick_alonge).</description>
    <link>https://dev.to/nick_alonge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1719814%2F741f951a-da38-46fa-bd40-b548c8d02fae.jpg</url>
      <title>DEV Community: Nick Alonge</title>
      <link>https://dev.to/nick_alonge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nick_alonge"/>
    <language>en</language>
    <item>
      <title>Reusable Views in .NET MAUI</title>
      <dc:creator>Nick Alonge</dc:creator>
      <pubDate>Wed, 16 Jul 2025 01:27:33 +0000</pubDate>
      <link>https://dev.to/nick_alonge/reusable-views-in-net-maui-4n3c</link>
      <guid>https://dev.to/nick_alonge/reusable-views-in-net-maui-4n3c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog is part of the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-25/" rel="noopener noreferrer"&gt;.NET MAUI UI July 2025&lt;/a&gt; series with a new post every day of the month. See the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-25/#net-maui-ui-july-schedule" rel="noopener noreferrer"&gt;full schedule&lt;/a&gt; for more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Don’t Repeat Yourself (DRY): Why Reusable Views Matter in .NET MAUI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As developers, one of the core principles we aim to follow is DRY—“Don’t Repeat Yourself.” The idea is simple: avoid writing the same code more than once. If you’ve ever copied and pasted the same UI elements across multiple pages in your app, you’ve already bumped into the reason why DRY matters.&lt;/p&gt;

&lt;p&gt;In .NET MAUI, reusable views help us stick to DRY by letting us package UI elements into components—like custom buttons, headers, or toolbars—that we can reuse across pages. Instead of rewriting the same layout every time you need a bottom toolbar, you can define it once in a &lt;strong&gt;ContentView&lt;/strong&gt; and just drop it into your pages.&lt;/p&gt;

&lt;p&gt;This makes your code easier to maintain, more readable, and much faster to build. Need to change how your toolbar looks or behaves? Update it in one place and the change appears everywhere it’s used.&lt;/p&gt;

&lt;p&gt;In this tutorial, I’ll show you how to build a reusable bottom toolbar using a ContentView in .NET MAUI, complete with &lt;strong&gt;bindable properties&lt;/strong&gt; for &lt;em&gt;Height&lt;/em&gt; and &lt;em&gt;ActiveTab&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let's start writing code! I will be using Visual Studio 2022 for this example.  All the code will be &lt;a href="https://github.com/NickA55/Maui_UI_July_2025" rel="noopener noreferrer"&gt;available in Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the Reusable BottomToolbarView&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;
&amp;lt;ContentView 
    x:Class="MauiJuly2025.Views.BottomToolbarView"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Name="ToolbarRoot"&amp;gt;

    &amp;lt;Grid BackgroundColor="#EEEEEE"
          HeightRequest="{Binding HeightRequest, Source={x:Reference ToolbarRoot}}"&amp;gt;
        &amp;lt;Grid.ColumnDefinitions&amp;gt;
            &amp;lt;ColumnDefinition Width="*" /&amp;gt;
            &amp;lt;ColumnDefinition Width="*" /&amp;gt;
            &amp;lt;ColumnDefinition Width="*" /&amp;gt;
        &amp;lt;/Grid.ColumnDefinitions&amp;gt;

        &amp;lt;Button Text="Home"
                Grid.Column="0"
                BackgroundColor="{Binding ActiveTab, Source={x:Reference ToolbarRoot}, Converter={StaticResource TabToColorConverter}, ConverterParameter=Home}"
                Clicked="OnHomeClicked" /&amp;gt;

        &amp;lt;Button Text="Page 1"
                Grid.Column="1"
                BackgroundColor="{Binding ActiveTab, Source={x:Reference ToolbarRoot}, Converter={StaticResource TabToColorConverter}, ConverterParameter=Page1}"
                Clicked="OnPage1Clicked" /&amp;gt;

        &amp;lt;Button Text="Page 2"
                Grid.Column="2"
                BackgroundColor="{Binding ActiveTab, Source={x:Reference ToolbarRoot}, Converter={StaticResource TabToColorConverter}, ConverterParameter=Page2}"
                Clicked="OnPage2Clicked" /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/ContentView&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BottomToolbarView Code Behind&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.Maui.Controls;
using MauiJuly2025.Pages;

namespace MauiJuly2025.Views;

public partial class BottomToolbarView : ContentView
{
    public BottomToolbarView()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty ActiveTabProperty =
        BindableProperty.Create(nameof(ActiveTab), typeof(string), typeof(BottomToolbarView), default(string));

    public string ActiveTab
    {
        get =&amp;gt; (string)GetValue(ActiveTabProperty);
        set =&amp;gt; SetValue(ActiveTabProperty, value);
    }

    public new static readonly BindableProperty HeightRequestProperty =
        BindableProperty.Create(nameof(HeightRequest), typeof(double), typeof(BottomToolbarView), 60.0);

    public new double HeightRequest
    {
        get =&amp;gt; (double)GetValue(HeightRequestProperty);
        set =&amp;gt; SetValue(HeightRequestProperty, value);
    }

    private void OnHomeClicked(object sender, EventArgs e)
    {
        ActiveTab = "Home";
        Application.Current.MainPage = new HomePage();
    }

    private void OnPage1Clicked(object sender, EventArgs e)
    {
        ActiveTab = "Page1";
        Application.Current.MainPage = new PageOne();
    }

    private void OnPage2Clicked(object sender, EventArgs e)
    {
        ActiveTab = "Page2";
        Application.Current.MainPage = new PageTwo();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add the Converter (for button highlighting)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add this converter in your Converters folder or wherever you store helper classes. A Converter is a small helper class used in data binding to transform a value before it shows up in the UI. For example, we used a converter to change the toolbar button color depending on which tab is active. Without changing the actual data, a converter lets us customize how that data appears visually.&lt;/p&gt;

&lt;p&gt;It’s like a middleman between your data and the UI: “If the active tab is this, show it in blue—otherwise, gray.”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Globalization;

namespace MauiJuly2025.Converters;

public class TabToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var activeTab = value as string;
        var thisTab = parameter as string;

        return activeTab == thisTab ? Colors.LightBlue : Colors.LightGray;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =&amp;gt;
        throw new NotImplementedException();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use the BottomToolbarView in your pages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whenever you want to add the toolbar to a page, you simply add the ContentView like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ContentPage 
    x:Class="MauiJuly2025.Pages.HomePage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:views="clr-namespace:YourApp.Views"
    Title="Home"&amp;gt;

    &amp;lt;Grid RowDefinitions="*, Auto"&amp;gt;
        &amp;lt;Label Text="Welcome to the Home Page!"
               HorizontalOptions="Center"
               VerticalOptions="Center" /&amp;gt;

        &amp;lt;views:BottomToolbarView Grid.Row="1" 
                                 HeightRequest="60"
                                 ActiveTab="Home" /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/ContentPage&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's talk about Bindable Properties... A &lt;strong&gt;BindableProperty&lt;/strong&gt; is a special type of property used in .NET MAUI that supports data binding. It lets your UI automatically update when the underlying data changes—and vice versa. You use it in custom controls (like our BottomToolbarView) when you want properties like ActiveTab or Height to be bindable from outside the control.&lt;/p&gt;

&lt;p&gt;In our example, we use a Bindable Property to set what tab is active, so we can highlight it. And another Bindable Property to set the toolbar height.&lt;/p&gt;

&lt;p&gt;That's all there is to it! As you start building your project you will create several reusable controls, and in some cases be able to use those controls in other apps.&lt;/p&gt;

&lt;p&gt;Thanks for reading, I hope you enjoyed this article and stay tuned for more.&lt;/p&gt;

</description>
      <category>mauiuijuly2025</category>
      <category>dotnetmaui</category>
    </item>
    <item>
      <title>Using Local Browser Storage in .NET MAUI Blazor Hybrid</title>
      <dc:creator>Nick Alonge</dc:creator>
      <pubDate>Thu, 18 Jul 2024 03:11:11 +0000</pubDate>
      <link>https://dev.to/nick_alonge/using-local-browser-storage-in-net-maui-blazor-hybrid-3loe</link>
      <guid>https://dev.to/nick_alonge/using-local-browser-storage-in-net-maui-blazor-hybrid-3loe</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog is part of the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/" rel="noopener noreferrer"&gt;.NET MAUI UI July 2024&lt;/a&gt; series with a new post every day of the month. See the &lt;a href="https://goforgoldman.com/posts/mauiuijuly-24/#net-maui-ui-july-schedule" rel="noopener noreferrer"&gt;full schedule&lt;/a&gt; for more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This article explores how to leverage local browser storage in a .NET MAUI Blazor Hybrid application to enhance data management and user experience. By utilizing this powerful combination, developers can create robust and efficient apps that maintain state and persist data across sessions, providing users with a smooth and responsive interface.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don't think I have written an application where I didn't need some kind of data persistence. Whether it be SQLite or a larger MS SQL database.  A lot of apps only require storing small amounts of data, and using a RDBMS can be a lot of overhead and add unnecessary libraries that inflate the size of your app.  Sometimes you have too much data for saving key/value pairs to Preferences, and not enough to warrant setting up a full blown database.&lt;/p&gt;

&lt;p&gt;Since .NET 8, I have adopted MAUI Blazor Hybrid as my tool of choice for writing mobile applications.  For the apps I write, having a consistent UI across iOS and Android is important, but also having access to the native platform is desirable as well. I have found the ease of storing data locally helps me speed up development of small to medium sized apps.&lt;/p&gt;

&lt;p&gt;There are 3 types of browser storage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session Storage&lt;/strong&gt;&lt;br&gt;
Useful for storing data until that browser session is closed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local Storage&lt;/strong&gt;&lt;br&gt;
Local storage will persist even when the user quits the app and comes back.&lt;/p&gt;

&lt;p&gt;Both Local and Session storage use key/value pairs and are limited to the amount of data (and type) you can store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IndexedDB&lt;/strong&gt;&lt;br&gt;
For more complex data, without the storage limits of local and session storage, IndexedDB should be used.  The IndexedDB API stores structured data, including files and blobs.  You can also create indexes for high speed searches. Because IndexedDB is a little moved involved, I will create a dedicated post for it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's get to the code.  I will be using VS Code on the Mac with the .NET MAUI extension for this example.  All the code will be &lt;a href="https://github.com/NickA55/MAUIHybridBrowserStorage" rel="noopener noreferrer"&gt;available in Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll start with Local storage.  Create a new MAUI-Blazor project.  We will be using the sample pages the template creates.  For local and session storage, we will be using the Blazored LocalStorage nugget.  Add this to your project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package Blazored.LocalStorage&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After the nugget installs, open up MauiProgram.cs and register the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Blazored.LocalStorage;
using Microsoft.Extensions.Logging;

namespace MAUIHybridBrowserStorage;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp&amp;lt;App&amp;gt;()
            .ConfigureFonts(fonts =&amp;gt;
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        builder.Services.AddMauiBlazorWebView();

                // Add the Blazored.LocalStorage service
        builder.Services.AddBlazoredLocalStorage();



#if DEBUG
        builder.Services.AddBlazorWebViewDeveloperTools();
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for setup. Now let's put it to use.  Open up Home.razor.  We are going to use this page to manage our shopping list.&lt;/p&gt;

&lt;p&gt;Start by injecting the Blazored.LocalStorage service into our Home.razor page:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@inject Blazored.LocalStorage.ILocalStorageService localStorage&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will use the variable localStorage to access the injected service.  Next, add the @code directive, and override OnInitializedAsync(). Our Home.razor file should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@page "/"

@inject Blazored.LocalStorage.ILocalStorageService localStorage

&amp;lt;h3 class="text-center"&amp;gt;Shopping List&amp;lt;/h3&amp;gt;

@code 
{
    protected override async Task OnInitializedAsync()
    {

    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I removed the code and HTML generated by the template, and centered the page title using Bootstrap classes.&lt;/p&gt;

&lt;p&gt;Now we will add a simple List to store our items, and read/write from that list to display the items on the page. In the OnInitializedAsync() method, we will check for any items saved to local storage, and if not NULL, set the ShoppingListItems to the value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@code 
{
    List&amp;lt;string&amp;gt;? ShoppingListItems;

    protected override async Task OnInitializedAsync()
    {
        // Check local storage for any items
        ShoppingListItems = await localStorage.GetItemAsync&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;("list-items");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also add methods for adding and deleting the local storage items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private async Task AddItems()
    {
        string[] items = {"Lettuce", "Roasted Turkey Lunch Meat", "Nutty Buddy", "Iced Tea", "Frozen Pizza", "Dog Food"};

        ShoppingListItems = items.ToList();

        // Save to local storage
        await localStorage.SetItemAsync("list-items", items);
    }

    private async Task DeleteAllItems()
    {
        // Remove the key
        await localStorage.RemoveItemAsync("list-items");

        // Reset the Shopping List
        ShoppingListItems = await localStorage.GetItemAsync&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;("list-items");
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Part 2 of this article, we will add and delete individual items, but for now we are just hard coding some values.&lt;/p&gt;

&lt;p&gt;Here is the completed Home.razor file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@page "/"

@inject Blazored.LocalStorage.ILocalStorageService localStorage

&amp;lt;h3 class="text-center"&amp;gt;Shopping List&amp;lt;/h3&amp;gt;

@if(ShoppingListItems != null)
{
    &amp;lt;div class="container"&amp;gt;
        &amp;lt;table class="table table-striped"&amp;gt;
            &amp;lt;thead&amp;gt;
                &amp;lt;tr&amp;gt;
                    &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
                &amp;lt;/tr&amp;gt;
            &amp;lt;/thead&amp;gt;
            &amp;lt;tbody&amp;gt;
                @foreach(var item in ShoppingListItems)
                {
                    &amp;lt;tr&amp;gt;
                        &amp;lt;td&amp;gt;@item&amp;lt;/td&amp;gt;
                    &amp;lt;/tr&amp;gt;
                }
            &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;
        &amp;lt;br /&amp;gt;
        &amp;lt;div class="text-center"&amp;gt;
            &amp;lt;button class="btn btn-danger" @onclick="DeleteAllItems"&amp;gt;Delete List&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
} else 
{
    &amp;lt;p class="text-center mt-3"&amp;gt;No items in the list.  Click the Add Items button&amp;lt;/p&amp;gt;
    &amp;lt;div class="text-center"&amp;gt;
        &amp;lt;button @onclick="AddItems" class="btn btn-primary"&amp;gt;Add Items&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
}

@code 
{
    List&amp;lt;string&amp;gt;? ShoppingListItems;
    protected override async Task OnInitializedAsync()
    {
        // Check local storage for any items
        ShoppingListItems = await localStorage.GetItemAsync&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;("list-items");
    }

    private async Task AddItems()
    {
        string[] items = {"Lettuce", "Roasted Turkey Lunch Meat", "Nutty Buddy", "Iced Tea", "Frozen Pizza", "Dog Fooo"};

        ShoppingListItems = items.ToList();

        // Save to local storage
        await localStorage.SetItemAsync("list-items", items);
    }

    private async Task DeleteAllItems()
    {
        // Remove the key
        await localStorage.RemoveItemAsync("list-items");

        // Reset the Shopping List
        ShoppingListItems = await localStorage.GetItemAsync&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;("list-items");
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the app add the items, they will persist until you delete them (or the app is removed).  In Part 2 we will take a look at how to use the web browser development tools in Chrome, Edge or Safari to view our local storage data.&lt;/p&gt;

&lt;p&gt;Using Local Storage is an easy way to persist data in your application without needed any knowledge of a relational database system. And .NET MAUI Blazor Hybrid gives you all the conveniences of a web application in a native app.&lt;/p&gt;

&lt;p&gt;In Part 2 we will focus on IndexedDB to store complex data types.  We will also use JS Interop to access some simple Javascript for user interaction. &lt;/p&gt;

&lt;p&gt;Thanks for reading, I hope you enjoyed this article and stay tuned for more.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetmaui</category>
      <category>blazor</category>
    </item>
  </channel>
</rss>
