BlazorBindings.Maui v1.0
Tags: dotnetmaui, dotnet, maui, blazor
Published: 2023-03-07 22:33:16

This release is one of the biggest ones so far, and I have decided that [BlazorBindings.Maui](https://github.com/Dreamescaper/BlazorBindings.Maui) is stable enough for a 1.0 release! Take a look at [Get Started](https://dreamescaper.github.io/MobileBlazorBindingsDocs/maui-blazor-bindings/get-started.html) page to check it out!

  * [About this project](#about-this-project)
  * [New controls supported](#new-controls-supported)
    + [SearchBar](#searchbar)
    + [IndicatorView](#indicatorview)
    + [SwipeView](#swipeview)
    + [TableView](#tableview)
    + [ListView](#listview)
  * [Behaviors](#behaviors)
  * [BlazorBindingsApplication](#blazorbindingsapplication)
  * [Root Wrapper](#root-wrapper)
  * [Error Boundaries](#error-boundaries)
  * [Entry is generic](#entry-is-generic)
  * [Other changes](#other-changes)
  * [Breaking changes](#breaking-changes)
  * [What's next?](#whats-next)


## About this project

[This project](https://github.com/Dreamescaper/BlazorBindings.Maui) is a fork of MobileBlazorBindings - experimental project by Microsoft to allow to use Blazor syntax for native controls instead of XAML with MAUI. Here is a sample Counter component that renders native UI, which may look familiar to Blazor developers, that increments a value on each button press:
```xml
<StackLayout>
    <Label FontSize="30">You pressed @count times </Label>
    <Button Text="+1" OnClick="@HandleClick" />
</StackLayout>

@code {
    int count;

    void HandleClick()
    {
        count++;
    }
}
```
![Android emulator](https://camo.githubusercontent.com/554d00d915708b2059da912c31662b22f3ea93d72431c30e278c3d39d3d02ed9/68747470733a2f2f646576626c6f67732e6d6963726f736f66742e636f6d2f6173706e65742f77702d636f6e74656e742f75706c6f6164732f73697465732f31362f323032302f30312f626c617a6f722d616e64726f69642d636f756e7465722d322e676966)



## New controls supported

This update brings the support to five more controls. That alse means that all Maui built-in controls are supported now (however, not all the functionality is supported for some controls yet).
You can take a look at [ControlGallery](https://github.com/Dreamescaper/BlazorBindings.Maui/tree/main/samples/ControlGallery) sample to find the examples of usage for those (and other) controls.

### SearchBar

SearchBar is a user input control used to initiating a search.

```xml
<SearchBar @bind-Text="_searchQuery" @bind-Text:after="UpdateSearchResults" />
<CollectionView ItemsSource="_displayedItems" />

@code {
    string _searchQuery;
    string[] _displayedItems;

    async Task UpdateSearchResults()
    {
        _displayedItems = await /* retrieve items */;
    }
}
```
![SearchBar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rx8mex5r9vtc2ie0j9b.gif)

### IndicatorView

IndicatorView is a control that displays indicators that represent the number of items, and current position, in a CarouselView.

```xml
<CarouselView IndicatorView="() => indicatorView">
    <ItemTemplate>
        ...
    </ItemTemplate>
</CarouselView>

<IndicatorView @ref="indicatorView"
               HorizontalOptions="LayoutOptions.Center"
               IndicatorColor="Colors.DarkGray"
               SelectedIndicatorColor="Colors.LightGray" />

@code {
    IndicatorView indicatorView;
}

```
![IndicatorView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w900i6cedpdbzq3cxww2.gif)

### SwipeView

SwipeView is a container control that wraps around an item of content, and provides context menu items that are revealed by a swipe gesture.

```xml
@foreach (var item in _items)
{
    <SwipeView>
        <RightItems>
            <SwipeItems>
                <SwipeItem BackgroundColor="Colors.Yellow"
                       Text="Favorite"
                       OnInvoked="() => MakeFavorite(item)" />

                <SwipeItem BackgroundColor="Colors.Red"
                       Text="Remove"
                       IsDestructive="true"
                       OnInvoked="() => Remove(item)" />
            </SwipeItems>
        </RightItems>

        <ChildContent>
            <ContentView BackgroundColor="Colors.Green" Padding="8">
                <Label>@item</Label>
            </ContentView>
        </ChildContent>
    </SwipeView>
}
```
![SwipeView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wsl32b15ieioinrufhqw.gif)

### TableView

TableView displays a table of scrollable items that can be grouped into sections. A TableView is typically used for displaying items where each row has a different appearance, such as presenting a table of settings.

```xml
    <TableView>
        <TableRoot>
            <TableSection Title="Cells examples">
                <TextCell Text="Click me" Detail="Tapping makes something happen." OnTapped="OnCellTapped" />
                <EntryCell Label="Enter your name:" @bind-Text="entryText" />
                <SwitchCell Text="Click to toggle:" @bind-On="switchOn" />
            </TableSection>
        </TableRoot>
    </TableView>
```
![TableView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xntp1y3zo6gxwi64kap0.png)

### ListView

ListView displays a scrollable vertical list of selectable data items. While ListView manages the appearance of the list, the appearance of each item in the list is defined by a DataTemplate that uses a Cell to display items.

```xml
<ListView ItemsSource="items" SelectionMode="ListViewSelectionMode.None">
    <ItemTemplate>
        <ImageCell Text="@context.Name" Detail="@context.Type" ImageSource="@context.ImageUrl">
            <ContextActions>
                <MenuItem Text="Remove" IsDestructive="true" OnClick="() => RemoveItem(context)" />
            </ContextActions>
        </ImageCell>
    </ItemTemplate>
</ListView>
```
![ListView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6qb76er7rksx8uol5gch.png)


## Behaviors

While Behaviors concept is a bit foreign with Blazor, it is an important part of the Maui ecosystem, therefore this update brings the support for Behaviors. While there are no Behavior components provided out of the box, you can create bindings for the behaviors that you need using [component generator](../advanced/custom-components.md#wrapping-maui-components).
Here's the example for [StatusBarBehavior](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/behaviors/statusbar-behavior) from the MAUI Community Toolkit (you can take a look at the full sample [here](https://github.com/Dreamescaper/BlazorBindings.Maui/tree/main/samples/ThirdPartyControlsSample)).

```xml
<ContentPage Title="CommunityToolkit.Behaviors">
    <Behaviors>
        <StatusBarBehavior StatusBarColor="Colors.Green" />
    </Behaviors>

    ...
</ContentPage>
```
![Behavior](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpr3czz2pkncju00nbqw.png)



## BlazorBindingsApplication

Previously, you had to use `MauiBlazorBindingsRenderer` instance to add BlazorBindings component to your application. However, `MauiBlazorBindingsRenderer` is not recommended to be used from the application code, as it is a low level implementation detail.
This release brings an easy to use `BlazorBindingsApplication` type, which accept your root component type as a generic argument.

You can inherit your App type from BlazorBindingsApplication, if you need to customize the Application in any way:
```csharp    
public class App : BlazorBindingsApplication<AppShell>
    {
        public App(IServiceProvider services) : base(services)
        {
            // Do whatever you need (e.g. add resources).
        }
    }
```

Or you can use this type directly in simple cases:
```csharp
builder.UseMauiApp<BlazorBindingsApplication<AppShell>>();
``` 

## Root Wrapper

Generally, BlazorBindings' renderer creates separate render trees for each page - that includes MainPage and navigations (both regular and modals). However, in some cases it might be useful to have a component, which wraps any rendered subtree - e.g. to define global cascading parameters, or to update all the pages on some theme changed.

Now it is possible to do that via `BlazorBindingsApplication`'s `WrapperComponentType` property. 

For instance, here's an example of a wrapping component `Root.razor`, which adds a global cascading value, and updates the UI based on some event: 
```xml
@inject AppState AppState

<CascadingValue Value="123" Name="test">
    @ChildContent
</CascadingValue>

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

    protected override void OnInitialized() 
    {
        AppState.ThemeChanged += InvokeAsync(() => StateHasChanged());
    }
}
```

Now, it is set as a wrapper component by overriding `WrapperComponentType` property:
```csharp
public class App : BlazorBindingsApplication<AppShell>
{
    public App(IServiceProvider services) : base(services) { }

    public override Type WrapperComponentType => typeof(Root);
}
```

## Error Boundaries

One of use cases for wrapper components is to allow to set a global [error boundary](https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-7.0#error-boundaries). It allows to handle any exception, display a nice error page, and return back to the application (instead of simply crashing the application).

> [!NOTE]
> Error boundaries do not allow you to _ignore_ exceptions based on some conditions, and leave the user on a current page. Error boundary content is _always_ replaced with an error content if any exception occurs.

Here's an example of such wrapper component:
```xml
<ErrorBoundary @ref="_errorBoundary">
    <ChildContent>
        @ChildContent
    </ChildContent>

    <ErrorContent>
        <ContentPage Title="Error">
            <VerticalStackLayout Margin="8" Spacing="8">
                <Label Text="Oops!" HorizontalOptions="LayoutOptions.Center" FontSize="20" />
                <Label Text="Unexpected error has occured:" />
                <Label>@context.Message</Label>

                <Button Text="Return" OnClick="Recover" />
            </VerticalStackLayout>
        </ContentPage>
    </ErrorContent>

</ErrorBoundary>

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

    ErrorBoundary _errorBoundary;

    void Recover() {
        _errorBoundary.Recover();
    }
}
```

![Error Boundary](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tf2i5py7i94dpeage63a.gif)



> [!NOTE]
> You'll need to create your ErrorBoundary type, implementing ErrorBoundaryBase. You can take a look [here](https://github.com/Dreamescaper/BlazorBindings.Maui/blob/main/samples/ControlGallery/Views/ErrorBoundaries/ErrorBoundary.cs) for an example.

## Entry is generic

Entry component is generic now, which allows binding the value to non-string properties, like `int` or `decimal`.
```xml
<Label>Int entry: @_int</Label>
<Entry @bind-Text="_int" Keyboard="Keyboard.Numeric" />

@code{
    int _int;
}
```

> [!NOTE]
> While this change should not cause any troubles in most cases (since Blazor is able to infer generic argument from the `@bind` directive),
> it still might cause compilation errors in some cases. You can add `T="string"` attribute value to resolve that.

## Other changes

- Component Generator adds XML documentation now.

## Breaking changes

- Due to some internal changes, components created via Component Generator have to be regenerated.
- `NativeControlComponentBase.ElementHandler` is protected now.
- Control components do not inherit `ComponentBase` anymore.
- Default error page is removed. Application will crash in case of unhandled exceptions now.
- `MauiBlazorBindingsRenderer` does not support rendering the component to non-Application parent.
- Some methods, which are not stable yet, have [[RequiresPreviewFeatures]](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2252) attribute now.
- Entry component is generic now (see above).


## What's next?

- Shell URL navigation improvements.
- Supporting DataTemplateSelectors.
- Supporting Maps controls, which were added in MAUI for .NET 7.
- Improving the documentation.