Author: rebelzoo

Blazor 0.8.0

Blazor 0.8.0

With the continuing improvement to Blazor, Microsoft has released a few new bits to get us close to Blazor ASP.NET Components goodness.

Visual Studio 2019 Preview 2.2

.NET Core 3.0.100-preview-010184

Blazor 0.8.0

With these three updates, you can create new or upgrade existing Blazor applications to the latest and greatest.

The following ASP.NET blog shows the steps necessary to upgrade your existing project, and also shows the changes that are getting close to the release naming conventions.

Blog: Blazor 0.8.0 experimental release now available

Notably the term Blazor is being phased out for the official branding of ASP.NET Components.

The Blazor Namespace is now Microsoft.AspNetCore.Components, rather than Microsoft.AspNetCore.Blazor

BlazorComponent is now called ComponentBase.

BlazorLayoutComponent is now just LayoutComponentBase

The simple JSON parser has been replaced by Newtonsoft’s parser, a welcome addition for those of us that need the advanced features of that parser. It was previously possible to use the Newtonsoft parser, but you needed to disable IL linking, which resulted in a much larger package size.

Visual Studio 2019 now has tooling by default.

.NET 3.0 support. Previously Blazor was limited to .NET core 2.1 (not even 2.2). With this upgrade, Blazor runs nicely in 3.0, with a performance boost.

Finally there is some client/browser side debugging. This should make for far better development experience.

Blazor debugging

More Blazor

More Blazor

To continue with our adventure into Blazor, or Razor Components, we should learn how to separate the code from the html.

The general design of Blazor Pages, is to include the page code in the @functions section of the cshtml file

@functions {
    int currentCount = 0;
 
    void IncrementCount()
    {
        currentCount++;
    }
}

But we are going to remove this section, from our Index Page, and place it in a new class called IndexBase

public class IndexBase : BlazorComponent
{
    [Inject]
    private HttpClient Http { getset; }
 
    WidgetSettingsBase[] widgets;
    protected override async Task OnInitAsync()
    {
        Console.WriteLine("OnInitAsync");
        widgets = JsonConvert.DeserializeObject<WidgetSettingsBase[]>(await Http.GetStringAsync("api/SampleData/widgets"),
            new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto });
        StateHasChanged();
    }
 
    public RenderFragment CreateDynamicComponent() => builder =>
    {
        if (widgets == nullreturn;
 
        var assm = Assembly.GetExecutingAssembly();
        var assmName = assm.GetName().Name;
 
        foreach (var widget in widgets)
        {
            var widgetType = Type.GetType($"{assmName}.Widgets.{widget.widget}");
            if (widgetType != null)
            {
                AddWidget(builder, widgetType, widget);
            }
        }
    };
 
    private static void AddWidget(RenderTreeBuilder builder, Type widget, WidgetSettingsBase settings)
    {
        var seq = 0;
        builder.OpenComponent(seq, widget);
 
        if (settings is WidgetSettings widgetSettings)
        {
            foreach (var param in widgetSettings.parameters)
            {
                seq++;
                builder.AddAttribute(seq, param.Key, param.Value);
            }
        }
        else
        {
            builder.AddAttribute(seq, "Settings", settings);
        }
        builder.CloseComponent();
    }
}

This class needs to inherit from BlazorComponent. Then we can Inherit that in the page cshtml.

@page "/"
@using System.Reflection
@using Microsoft.AspNetCore.Blazor.RenderTree
@using myDash.Shared
@using Newtonsoft.Json
@inherits IndexBase
 
<head>
    <style>
        .card {
            background-colortransparent;
            background-imagelinear-gradient(to bottom, rgba(149, 87, 29, 0.2) 0%, rgba(125, 185, 232, 0) 100%);
            background-repeatrepeat;
            border-radius10px;
            padding0px;
            margin-bottom15px;
        }
    </style>
</head>
 
<section class="container">
    <div class="row info-panel">
        @CreateDynamicComponent();
    </div>
</section>

That is all it takes. Any items that were injected using the @inject keyword, can be injected to the class by making a property and using the [Inject] attribute.

I like this separation a lot better than the code existing the cshtml. However, in some simple pages, code directly in the cshtml will be very powerful. So, I can see a use for both.

Let’s talk Blazor!

Let’s talk Blazor!

You may or may not have heard of WebAssembly, or the Mono Framework, but Microsoft is blending them to wonderful platform goodness, for the future of the web. So, you may never need to understand either to take full advantage.

Keep in mind these are all experimental bits, at the moment. You can learn more at Blazor.net

WebAssembly (Wasm) is a part of all major modern browsers. It is a binary format for execution in a browser’s controlled sand-box. It is meant to be an intermediate format and not written directly, but it can be. In general you compile from a high level language to Wasm. It serves a similar purpose to JavaScript, but is not intended to be a replacement for JavaScript. In my opinion will take a large bite out of JavaScript’s stranglehold on client code; especially with Microsoft’s help.

The Mono framework was started in 2004. The .NET framework and compilers were Windows only. The Mono open-source project implemented the .NET APIs, based on the same ECMA-335 open standard, that was implemented in the official .NET. This allowed compiling and running .NET code on non-Windows systems, such as Linux and MacOS.

Blazor is an experimental Single-page application (SPA) framework that combines C#, WebAssembly, and Mono to allow C# code to be compiled and run directly in the browser. The Mono framework is provided as WebAssembly. For now, at least, the browser will use Mono to interpret the client .NET dll’s; but the speculation is that the compiler will eventually minify and cross-compile the .NET dll’s directly to WebAssembly.

Blazor has a deployment mode that is coded in mostly the same way, but runs server-side. The Blazor code runs on the server, and only the view differences are sent to the browser, via a SignalR connection. This mode gives the same coding benefits, but since the Blazor client code runs on the server, delays between the browser and server can be seen. It also requires more server compute power. This deployment mode is scheduled to be released in .NET Core 3.0.

For a Blazor coding experiment we will use the client side mode, with some WebApi to get configuration data. I made a simple dashboard with widgets that are loaded via server configuration. The project is based on the Hosted Blazor template.

Widgets can be any Blazor component, but I kept my widgets all cards to keep a consistent look.

There is a simple widget that just takes it content as parameters

There is a countdown widget, that can either countdown to a date, or count from one date to another with progress

There is a life countdown, to tell you how many more days you have to an age

And for fun there is a bouncing ball demo on a canvas

This is a simple demo, so the Blazor widgets are compiled directly into the client app, using pages. The index page will request an array of WidgetSettings from the server’s WebApi

WidgetSettings[] widgets;
protected override async Task OnInitAsync()
{
    widgets = await Http.GetJsonAsync<WidgetSettings[]>("api/SampleData/widgets");
    StateHasChanged();
}

The server currently returns a fixed list of widget settings, but could easily be extended to store and load these settings by the user.

[HttpGet("[action]")]
public IEnumerable<WidgetSettings> widgets()
{
    var widgets = new[]
    {
        new WidgetSettings() {widget = "LifeCountdown", parameters =  {{"Header","Jim"},{"BirthDay""1970/1/1"}, { "EndAge"70 }}},
        new WidgetSettings() {widget = "LifeCountdown", parameters =  {{"Header","Bob"},{"BirthDay""1990/7/4"},{ "EndAge"75 }}},
        new WidgetSettings() {widget = "LifeCountdown", parameters =  {{"Header","Rick"},{"BirthDay""1985/4/27"},{ "EndAge"80 }}},
        new WidgetSettings() {widget = "SimpleCard", parameters = {{"Header""This is a simple card"}, {"SubHeader"""},
            { "Body","This is the body of my simple card.  There are other simple cards, but this is mine."},
            { "colsLarge"6}, {"colsSmall"12}}},
        new WidgetSettings() {widget = "Countdown", parameters =
        {
            {"Header","Countdown to New Year" },
            {"From",$"{DateTime.Today.Year}/1/1"},
            {"To"$"{DateTime.Today.Year+1}/1/1"}
        }},
        new WidgetSettings() {widget = "Countdown", parameters =
        {
            {"Header","Countdown to July 4th" },
            {"From",$"{DateTime.Today.Year}/7/4"},
            {"To"$"{DateTime.Today.Year+1}/7/4"}
        }},
        new WidgetSettings() {widget = "CanvasWidget"}
    };
    return widgets;
}
 

These widgets are loaded to the Index page by using the builder, at runtime.

RenderFragment CreateDynamicComponent() => builder =>
{
    if (widgets == nullreturn;
 
    var assm = Assembly.GetExecutingAssembly();
    var assmName = assm.GetName().Name;
 
    foreach (var widget in widgets)
    {
        var widgetType = Type.GetType($"{assmName}.Widgets.{widget.widget}");
        if (widgetType != null)
        {
            AddWidget(builder, widgetType, widget.parameters);
        }
    }
};
 
private static void AddWidget(RenderTreeBuilder builder, Type widget, Dictionary<string,object> parameters )
{
    var seq = 0;
    builder.OpenComponent(seq, widget);
    foreach (var param in parameters)
    {
        seq++;
        builder.AddAttribute(seq, param.Key, param.Value);
    }
    builder.CloseComponent();
}

You can download the sample code here: https://github.com/jchristman75/BlazorDash

Let’s keep it simple – SimpleCommand

Let’s keep it simple – SimpleCommand

Another important part of the MVVM (Model-View-ViewModel) pattern is the allowing the View to request actions in the ViewModel.  We will achieve this with the ICommand Interface.

You can implement many single-purpose classes that implement ICommand, but as a good friend once told me, if you want to find the easy way to do something, ask a lazy person to do it twice.

I like to create a SimpleCommand that implements ICommand, and accepts an Action in the constructor.

public class SimpleCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
 
    private bool _canExecute=true;
    private Action<object> _method = null;
 
    public SimpleCommand(Action<object> method)
    {
        _method = method;
    }
 
    public void setCanExecute(bool canExecute)
    {
        _canExecute = canExecute;
        CanExecuteChanged?.Invoke(this,null);
    }
 
    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }
 
    public void Execute(object parameter)
    {
        _method(parameter);
    }
}

With this SimpleCommand, the CanExecute state is manually set via the setCanExecute method, but you can easily pass a predicate in the constructor to validate CanExecute as well.  Keep in mind, this value should be very inexpensive or better yet cached, as some MVVM frameworks will exhaustively call the CanExecute method to see if the Command has changed states.

You can also extend the constructor to accept a default state for CanExecute, if that is desirable.

Add one of these commands to your ViewModel.

private SimpleCommand _simpleMessageCommand = null;
public SimpleCommand SimpleMessageCommand
{
    get
    {
        if(_simpleMessageCommand ==null)
        {
            _simpleMessageCommand = new SimpleCommand((x) => Message = x as string);
        }
    return _simpleMessageCommand;
    }
}

In this example Message sets a message to the Simple view

private string _message = "Hello World!";
public string Message
{
    get => _message;
    set => setBackingField(ref _message, value);
}

Most of the UI elements you are used to using, with Events, will have Dependency Properties that accept commands to be used in-place of the events.

For instance a button has a Command property that can replace the click event.

Bind to the Command. If you need to pass a parameter you can pass that in the Command’s associated Parameter property; or for advanced data bind to the Parameter.  The button will be enabled or disabled based on the CanExecute state of the Command.

<Button Content="Say 'Button Pressed'" Command="{Binding SimpleMessageCommand}" CommandParameter="Button Pressed"/>

Here is a simple view at startup.


Pressing the Say ‘Button Pressed’ Button, which is connected to our command, will execute the command.  The command will update the message, with the message passed through the parameter.

Let’s start simple – SimpleViewModelBase

Let’s start simple – SimpleViewModelBase

To implement the MVVM (Model-View-ViewModel) pattern, you need ViewModels that implement the INotifyPropertyChanged Interface.

I normally handle this by having a base class for all ViewModels to inherit.

public abstract class SimpleViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    public void OnPropertyChanged([CallerMemberNamestring propertyName = "")
    {
        PropertyChanged?.Invoke(this,
                        new PropertyChangedEventArgs(propertyName));
    }
 
    public bool setBackingField<T>(ref T storage, 
                                   T value,
                                   [CallerMemberNamestring propertyName = "")
    {
        var oldValue = storage;
 
        if(object.Equals(storage, value))
        {
            return false;
        }
 
        storage = value;
 
        OnPropertyChanged(propertyName);
 
        return true;
    }
}

OnPropertyChanged() will either allow for a property to announce that it has changed

public string MyProperty
{
    get { return _myProperty; }
    set
    {
        _myProperty = value;
        OnPropertyChanged();
    }
}

..or that another property has changed, by passing the name of that property.

public string LastName
{
    set
    {
        ...
        OnPropertyChanged(nameof(FullName));
    }
}

setBackingField<T>() is a helper method to allow property setters to perform all of the aspect necessary for setting their backing field and calling OnPropertyChanged to reflect that change.

public string MyProperty
{
    get { return _myProperty; }
    set
    {
        setBackingField(ref _myProperty, value);
    }
}

setBackingField<T>() will return a boolean informing if there was change.

ViewModels will inherit this and take advantage of the helper methods

public class PersonViewModel : SimpleViewModelBase
{
    private string _firstName;
    public string FirstName
    {
        get => _firstName;
        set
        {
            if (setBackingField(ref _firstName, value))
            {
                //Also inform Fullname
                OnPropertyChanged(nameof(FullName));
            }
        }
    }
 
    private string _lastName;
    public string LastName
    {
        get => _lastName;
        set
        {
            if (setBackingField(ref _lastName, value))
            {
                //Also inform Fullname
                OnPropertyChanged(nameof(FullName));
            }
        }
    }
 
    public string FullName
    {
        get => $"{FirstName} {LastName}";
    }
}

console.writeline(“Hello world!”);

console.writeline(“Hello world!”);

Welcome to my blog experiment.  I have been a software developer for over 20 years, developing C, C++, VB, ASP, PHP.  In the past 14 years I have focused on .NET for desktop. 

The world is growing rapidly around me.  This blog will encourage me to grow my abilities, and if it helps someone else along the way, we both benefit