Click here to Skip to main content
15,867,686 members
Articles / Web Development / Blazor
Article

Wisej.NET vs Blazor

Rate me:
Please Sign up or sign in to vote.
5.00/5 (19 votes)
15 Aug 2022CPOL26 min read 20.8K   11   11
In this article, we get up to speed with Wisej.NET and explore how it addresses the specific challenges of building Line-of-Business, Enterprise Web Applications.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

Blazor is a modern, component-based framework for building websites and applications using .NET, and for many projects, it's a great choice.

But it's not the only option.

Alongside Blazor (including Microsoft's other options like MVC and Razor Pages), we have alternatives, one of which is Wisej.NET.

Wisej.NET is a web framework designed to make it faster and easier to build business web applications. If you’re new to Wisej.NET, you can download the community version here.

It promises a flat learning curve, WYSIWYG designer, full integration with Visual Studio, and an option to bring in external components (from vendors such as SyncFusion and Telerik) so you can focus on building your app and meeting your customers’ requirements.

At first glance, you might imagine Wisej.NET is a straight-up Blazor alternative, solving similar problems and enabling you to build your web applications using .NET.

But in practice, Wisej.NET addresses a particular challenge, building large enterprise web applications, and does so in a way that merits a closer look.

So join me as I get up to speed with Wisej.NET and explore how it addresses the specific challenges of building Line-of-Business, Enterprise Web Applications.

What are we looking for?

Before we get started, it's probably worth defining what Enterprise Web Applications are in this context and what they need to be able to do.

For this comparison, I'm assuming LOB Enterprise applications manage large amounts of data, where data needs to be entered, reviewed, and visualized.

If you're thinking forms (for data entry) and screens displaying lots of data in various formats, you're probably on the right track!

The key factors we'll explore in this comparison are:

  • How the two frameworks scale (specifically, how they can handle thousands or even millions of rows of data)
  • How do the frameworks handle lost connections and browser refreshes (is state preserved?)
  • The steps involved in handling standard LOB application requirements, such as a modal dialog updating a field on the page

If you've done any work with Blazor, you'll have seen the standard 'new project' boilerplate UI (the one with the Counter and Fetch Data examples)

Let's start by attempting to recreate those examples using Wisej.NET, beginning with the Weather Data example.

Build the WeatherData demo from scratch in Wisej.NET

After running through an installation process to add Wisej.NET to Visual Studio, the first step is to create a new project.

There are a several options here, but I opted to stick with Wisej.NET 3 Web Page Application as this seemed the closest to a standard Blazor web application (with the notion of pages and the ability to add components to those pages).

Once the project has been initialized, you are presented with a rather blank project, including a file named Page1.cs.

Opening that brings up the Wisej.NET WYSIWYG designer. If you've ever worked on a WebForms or XAML application, some aspects of this are going to feel very familiar.

To build the UI, we can drag and drop controls from the Toolbox onto the page.

For this demo, I'll bring in a Navigation Bar and Flex Layout Panel.

The Navigation Bar will go on the left, and I'll put the Flex Layout Panel on the right.

Image 1

I've also added a single item to the Navigation Bar (which will link to the Fetch Data/Weather Forecasts component).

Image 2

Note the WeatherForecastPanel Tag value. We'll need that in a moment.

From here, we need a way to show different 'components' (or panels) when one of the items in the Navigation Bar is clicked.

I'll implement that by wiring up an event handler to the Navigation Bar's SelectedItemChanged event, which takes the Tag name from the selected item and uses it to locate (and activate) the relevant 'panel.'

Here's the relevant event for the designer.

Image 3

Now we need some code to locate the relevant 'panel' and activate it (so it shows up in the Flex Layout Panel I added earlier).

C#
private void navigationBar_SelectedItemChanged(object sender, System.EventArgs e)
{
    var panelName = navigationBar1.SelectedItem.Tag;

    this.flexLayoutPanel1.Controls.Clear();
    var panel = (Control)Activator.CreateInstance(Type.GetType($"Wisej.NETWebPageApplication1.Panels.{panelName}"));
    panel.Dock = DockStyle.Fill;
    panel.Parent = this.flexLayoutPanel1;

}

Although we're only just getting started, this simple demo is beginning to highlight some of the key differences between Blazor and Wisej.NET.

For comparison, the standard Blazor new project template includes an HTML div with HTML anchor elements for navigating to the various 'page' in the app.

Blazor operates as a SPA (Single Page Application), so it will intercept those clicks (when you attempt to navigate between pages), locate the component for the page you're attempting to access, then load that component and render it in the browser.

We're doing something similar with Wisej.NET, but there's a key philosophical difference.

Wisej.NET is designed for building web applications (not websites). The focus is on maintaining a consistent state and context around the user, what they're working on, what data they've entered, and which stage they've reached as they work through repeatable business processes.

Therefore the focus is less on navigating between pages on a site but rather on showing the user the suitable screens at the right time while keeping track of the context around how they ended up there.

This makes sense in the context of a web application. You probably don't want your users jumping directly to a part of the UI just because they happen to have the URL for it. In a LOB app, you likely need them to follow the correct process to arrive at that screen!

Back to our demo, we need to create that WeatherForecastPanel we referenced earlier (see earlier note about remembering the tag name).

For this we can add a User Control (via the standard Add > New Item command in Visual Studio).

Image 4

At this point we could jump off and create more 'panels' but let's have a go at implementing this Weather Data demo first.

I'll start by adding a DataGridView to the designer.

Then I can use the WeatherForecastService and WeatherForecast classes from the Blazor demo for inspiration and implement slightly modified versions for Wisej.NET.

Wisej.NET\WeatherForecastService

C#
using System;
using System.ComponentModel;
using System.Threading.Tasks;

public class WeatherForecastService
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public Task<BindingList<WeatherForecast>> GetForecastAsync(DateTime startDate, int count = 5)
    {
        var rand = new Random();
        var list = new BindingList<WeatherForecast>();
        for (var i = 0; i < count; i++)
        {
            list.Add(new WeatherForecast
                     {
                         Date = startDate.AddDays(i),
                         TemperatureC = rand.Next(-20, 55),
                         Summary = Summaries[rand.Next(Summaries.Length)]
                     });
        }

        return Task.FromResult(list);
    }
}

This code generates random weather data (so we've got something to show in our UI).

Note that the GetForecastAsync method is modified from the original Blazor version to return an instance of BindingList, which comes from the System.ComponentModel namespace provides some 'out of the box' functionality for notifying UI controls of changes to the data (so we can easily bind controls such as data grids).

WeatherForecast itself is a plain old C# class:

C#
public class WeatherForecast
{
    [DisplayName("Date")]
    public DateTime Date { get; set; }

    [DisplayName("Temp. (C)")]
    public int TemperatureC { get; set; }

    [DisplayName("Temp. (F)")]
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

    [DisplayName("Summary")]
    public string Summary { get; set; }

    [DisplayName("Temp. (K)")]
    public double TemperatureK => TemperatureC + 273.15;
}

With these classes in place, we can set them to drive our datagrid component.

Wisej.NET\WeatherForecastPanel.cs

C#
public class WeatherForecast
{
    [DisplayName("Date")]
    public DateTime Date { get; set; }

    [DisplayName("Temp. (C)")]
    public int TemperatureC { get; set; }

    [DisplayName("Temp. (F)")]
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

    [DisplayName("Summary")]
    public string Summary { get; set; }

    [DisplayName("Temp. (K)")]
    public double TemperatureK => TemperatureC + 273.15;
}

WeatherForecastPanel_Load is wired up to this user control's Load event (so it will attempt to load this data when the WeatherForecastPanel is activated).

All that's left is to invoke WeatherForecastService.GetForecastAsync to load 25 records (for now) and bind them to the data grid's data source property.

Image 5

At this point, some differences start to emerge between the two frameworks (Wisej.NET vs Blazor).

First, we've discovered that Wisej.NET requires less manual work to create this sort of UI (a grid showing data).

Creating the equivalent UI Blazor requires us to either:

  • Handcraft the HTML and CSS to render our weather data and navigation bar
  • Use components from a third-party vendor

Choosing frameworks naturally involves comparing trade-offs (and what makes sense for your specific situation). The trade-offs here seem to be:

  • With Wisej.NET, you give up direct control over what gets rendered in the browser (because you're not handcrafting the HTML and will end up with whatever HTML Wisej.NET creates for you)
  • You gain development speed (it's quicker to bind data to the data grid than it is to write the HTML and CSS from scratch)
  • You get basic grid functionality (like sorting, selecting rows) 'out of the box' (you'd have to implement that yourself with Blazor)

Scaling up

Now, what if we need more than 25 records, say 1000 or even 1,000,000?

To test this, we can add some UI to select the number of records to load. For this, I'll add a Combobox to my Wisej.NET example, with some hardcoded values to choose from and two buttons: one to load the weather data and one to clear it.

Image 6

In code, I can read the selected value and use it to load the requested number of records when the Load Weather button is clicked.

Wisej.NET\WeatherForecastPanel.cs

C#
private async void LoadData()
{
    var count = 25;
    int.TryParse(comboBox1.Text, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out count);

    this.dataGridView1.DataSource = await new WeatherForecastService().GetForecastAsync(DateTime.Now, count);

    this.dataGridView1.Columns["Date"].DefaultCellStyle.Format = "d";
    this.dataGridView1.Columns["TemperatureK"].DefaultCellStyle.Format = "N3";
}

private void button1_Click(object sender, EventArgs e)
{
    LoadData();
}

When we run this now, Wisej.NET handles the larger numbers of records with minimal fuss.

Switching between 1,000 and 100,000 records is pretty instant, and loading in 1,000,000 records is rapid too.

Loading multiple records

The Wisej.NET DataGrid provides sort functionality (toggled by clicking the column names) and the ability to select specific rows.

You can also toggle the visibility of specific columns.

Image 8

Switching to Blazor, we can use a @foreach loop to iterate over the data, rendering table rows for each record.

Blazor\FetchData.razor

HTML
@foreach (var forecast in forecasts)
{
    <tr>
        <td>@forecast.Date.ToShortDateString()</td>
        <td>@forecast.TemperatureC</td>
        <td>@forecast.TemperatureF</td>
        <td>@forecast.Summary</td>
    </tr>
}

We can then implement UI to select the number of records using an HTML select element.

Blazor\FetchData.razor

HTML
<!-- existing markup -->

<select @onchange="CountChanged">
    <option>10</option>
    <option>10,000</option>
    <option>100,000</option>
    <option>1,000,000</option>
</select>
C#
@code {

    protected async Task CountChanged(ChangeEventArgs e)
    {
        int.TryParse(e.Value.ToString(), 
                     System.Globalization.NumberStyles.AllowThousands, 
                     CultureInfo.InvariantCulture, out var count);

        await LoadData(count);
    }

    protected async Task LoadData(int count = 0)
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now, count);
    }

}

Now, if we try to load 1,000,000 records and display them using this foreach loop, we'll run into trouble.

While testing, I found the browser eventually gives up and asks if you want to keep waiting for the page to respond.

The Blazor way around this problem is to switch to the dedicated Virtualize component instead.

Using Virtualize, we ensure that only the visible records render, which enables the page to remain responsive as we throw more records at the table.

We should probably fix the height of the table, too (so we can scroll through records in the table without scrolling the entire page).

Here's the markup and CSS we can use to employ virtualization and also fix the table’s height.

Blazor\FetchData.razor

HTML
<div class="weatherPanel">
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            <Virtualize Items="@forecasts" Context="forecast">
                 <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            </Virtualize>
        </tbody>
    </table>
</div>
CSS
.table {
    height: 100%;
}

.weatherPanel {
    height: 32em;
    display: block;
    overflow: scroll;
}

Here it is in action:

Blazor Weather Data Example

One thing to notice here is the visible 'lag' as we scroll through the records, as Blazor re-renders the new data in the existing HTML elements.

From here, and to better match the Wisej.NET version, we'd need to implement sorting, the ability to select a row by clicking it, and fix the table headers (so they don't disappear when you scroll down), and implement the ability to show and hide columns.

In effect, the Wisej.NET version is ready to go (thanks to the built-in functionality of the DataGrid).

We'd need to spend more time working on the Blazor equivalent to bring it in line (if we wanted additional functionality like sorting and highlighting selected records).

What happens when you refresh?

In any web scenario, there's always the risk of losing connection or users accidentally refreshing (or navigating away from) the current page.

This can be a significant issue, especially if your users are in the middle of a multi-step process (think multi-stage wizards or forms with multiple 'pages'), and end up losing their progress because they lost connection or inadvertently navigated away from the application.

This is one area where the two frameworks diverge quite notably.

Starting with our Wisej.NET weather data page, if I select the option to display 1 million records, let them appear in the Data Grid, then hit refresh, the page loads again exactly as it was before.

We're still on the same screen, with 1,000,000 selected in the combobox and the same one million weather records loaded in the data grid.

Before and after refreshing the page (Wisej.NET):

Image 10

Indeed, this holds true if we've sorted the grid or toggled the visibility of specific columns, with all that 'state' preserved on refresh.

While Blazor Server does attempt to preserve some application state (more on this shortly), in practice, if we hit refresh in our Blazor equivalent, we stay on the same page (with the same components), but our selections are lost, and the table lies bare.

Before Refresh

Image 11

After Refresh

Image 12

In this regard, Wisej.NET carries an advantage for LOB applications, automatically providing a stateful 'session' for your users without requiring you to implement state tracking (persistence and the ability to restore that state) yourself or via third-party libraries.

This proves particularly useful in applications where you want to provide a 'desktop like' experience, with your users opening up multiple 'windows' or forms, which can be moved around, maximized, minimized, closed, etc.

Wisej.NET makes this possible while also remembering the state of the UI as your users navigate around the application.

Here's another example: I've added a few panels to the screen at design time and marked them as movable (so the users can move them within the app itself).

After Initial Load

Image 13

With the app running in the browser it's possible to drag those windows around.

After moving the panels around, the panels keep their (new) positions when we refresh the page.

Before and After Refresh (after I moved the panels in the Browser)

Image 14

This gets more interesting when we consider common LOB tasks like filling in forms, with a partially completed form state being preserved by Wisej.NET if you refresh the app or navigate away from the form.

How Wisej.NET approaches sessions

The reason Wisej.NET can restore the UI on refresh lies in the way it handles sessions.

Not to be confused with authentication or user sessions, Wisej.NET sessions are designed to provide a desktop app-like experience but via the web.

Think of it as akin to running an instance of a desktop application. From the minute you launch the app to the moment you close it (or reboot your machine), that running instance of the application, can keep track of your actions, which windows you have visible (or hidden), which parts of the UI you're currently focusing on.

Wisej.NET sessions adopt a similar approach but run via the web. When you access the application a new session is started. At this point, you can go away, come back, refresh, and that session remains active.

The sessions keep track of the current state of the application, including the UI model (which windows are visible/open, data that's been entered via the user, which models are currently awaiting a response, etc.)

How Blazor approaches sessions

To understand how Blazor handles the 'session' state, we need to differentiate between Blazor's two hosting models.

Blazor WASM, which runs entirely in the browser, is akin to a JavaScript Single Page Application (think React or Angular). With Blazor WASM, there is no built-in session state persistence.

Say, for example, a user is part way through completing a multi-page form in a Blazor WASM application. Should their connection be interrupted (they accidentally navigate away from the page or finish work for the day and want to return to the form tomorrow), Blazor WASM won't attempt to persist in any state relating to their progress.

Blazor Server is slightly different. It uses circuits to attempt to keep track of the app's current state (for each user/connection).

Component state (which components are visible, what data is currently being displayed) is stored on the server. As you interact with a Blazor server app, those interactions are sent to the server. Blazor then sends messages to the browser (via a socket connection) instructing it to update the UI (DOM) in response to the changing component state.

In general, Blazor Server will attempt to keep a user connected to the same circuit.

The idea being, if the connection is temporarily interrupted, the user will end up reconnected to the same circuit (so they don't lose any information they've entered so far).

In practice, this is not 100% reliable; Blazor may connect you to a different circuit for a number of reasons, including:

  • The server kills the original circuit because it finds itself under memory pressure
  • Your app is served via multiple servers in a load-balanced configuration. If an individual server fails (or is removed when no longer required), the original server used for a user's connection may become unavailable when the user attempts to reconnect

If you refresh, not only will you connect to a new circuit, but you'll also lose any state held in the browser's memory (for example, if you've set values via JavaScript interop).

It is technically possible to persist state across circuits, but you are required to develop your own solution to preserve the application state somewhere other than the server's memory (a database, for example).

It's also worth noting that you aren't able to persist the UI state in this scenario.

For example, whilst you could write code to store the values a user has entered into a form, if they were halfway through a form and had been presented with a modal, you wouldn't be able to put them back on that form, with the modal open, in the event they ended up switching to another circuit.

Conversely, Wisej.NET would keep track of that state,and put you back on the same 'screen' with the same open modal.

Wisej.NET keeps the UI application model in sync

This seems like a good time to explore Wisej.NET's approach to managing the connection between client and server.

When you launch your app and access it via the browser, Wisej.NET maintains two representations of your application's UI model: one on the client (browser) and one on the server.

This is seamless from the user's perspective but opens up some interesting possibilities for development as you can write JavaScript code that interacts with your application's controls in the browser, and Wisej.NET will simultaneously keep the related server-side counterparts in sync.

The upshot is that you can use JavaScript to traverse through your application's UI model (starting with the top-level App) and interact with elements directly.

Let's take that multi-panel example from earlier.

We can traverse to the first panel (the imaginatively named 'Panel1') and invoke the hide method to make the control disappear.

Image 15

When we invoke this method (via JavaScript), Wisej.NET will hide the panel in the browser and ensure the server-side instance of the panel is updated accordingly.

To test this, I wired up an event handler to the panel's Disappear event (in the C# code):

MultiWindow.cs

C#
private void panel1_Disappear(object sender, EventArgs e)
{
    // this method was invoked
}

Then ran the following command (via the JavaScript console):

JavaScript
App.Page1.flexLayoutPanel1.MultiWindow.panel1.hide()

The panel disappeared and the corresponding panel1_Disappear event was invoked on the server:

This is a useful feature, not least because it means you can write JavaScript code without fear of the server-side model drifting out of sync, and/or inconsistent business rules being applied (any interactions triggered in the browser will be handled, correctly, on the server).

There's no direct equivalent in Blazor. The closest we have is the ability to interact with JavaScript via JS Interop. This requires manual 'plumbing' and implementing specific methods (in the Blazor components) to make them invokable via JavaScript.

Taking a step back, the humble counter

Before we tackle another common business application requirement (how to show modal dialogs), let's take a step back and consider the humble Counter demo.

Counter is likely the first demo you see when you spin up a Blazor project:

Image 16

When you click the button the counter increments.

To recreate this using Wisej.NET, we can create a simple page (or form, user control, etc.) with a button, a label, and a little code to update the value of the counter when the button is clicked.

C#
public partial class Counter : FlexLayoutPanel
{
    int value = 0;

    public Counter()
    {
        InitializeComponent();
        this.label1.Text = value.ToString();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        value++;
        this.label1.Text = value.ToString();
    }
}

In the UI, I have two labels (one to show the value and another to show the text next to the value), as well as the button.

Image 17

This demo, while simple, demonstrates a key difference between the two frameworks: Blazor tends to adopt a declarative approach, whereas Wisej.NET (in general) skews towards an imperative approach (depending on the specific use case).

Here's the Blazor counter equivalent:

HTML
<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
C#
@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Note how this relies on binding (for example, where we're displaying the current value of @currentCount in our markup). If the bound value is updated the UI element reacts accordingly (and the new value is displayed).

With Wisej.NET, we're more likely to take an imperative approach, whereby we issue commands to update the UI directly.

C#
this.label1.Text = value.ToString();

It is possible to bind certain Wisej.NET controls to data (so that, if the data source changes, the control reflects the updated data). In fact, we saw an example of that in the weather data demo. But in general, your Wisej.NET forms will take a more imperative approach.

There are trade-offs to consider with either approach:

  • Imperative code can be easier to write, especially when modeling business logic that is logically sequential (show a dialog, prompt the user for extra data, validate the provided data, etc)
  • The declarative approach makes it easier to identify which fields affect the UI when inspecting the UI (because the bindings are visible in the markup)
  • The inherent decoupling of a declarative approach (the state is changed in one place, and the UI reacts automatically) can make it harder to track down where important logic is located, especially if the changes are made in code that is located some distance away from the UI
  • You may be more used to one style over the other (in which case you're likely to be more productive using the one with which you're already familiar)

What's in a Modal?

One common requirement in LOB/Enterprise web applications is to present or request information via a Modal.

For certain business processes, it's important to block any further progress until a Modal is acknowledged and/or required information provided.

Wisej.NET Modals

Wisej.NET has built-in support for suspending execution on the server whilst awaiting a response from the user (when presented with a Modal dialog).

For example, a delete button (say, to delete a customer).

It's possible to show a message box in response to that button isclicked, which shows a confirmation dialog and prevents any other code from being executed until the user has made their choice.

C#
private void btnDelete_Click(object sender, EventArgs e)
{
    if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
    {
        this.btnDelete.Enabled = false;
        this.btnDelete.Text = "Deleted";
    }
}

When a user clicks that Delete button, they'll have to choose Yes or No to proceed.

But what if we want to request more information before proceeding?

For that. we can use a custom form and display it as a modal.

Here, EnterPassword is a form (a type of Wisej.NET container which you can add to your application).

Wisej.NET/EnterPassword (Form)

C#
public partial class EnterPassword : Form
{
    public string Password { get; set; }

    public EnterPassword()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.Password = txtPassword.Text;
        Close();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Close();
    }
}

Its job is to request a password from the user and store it in the Password property.

It's not visible in the code, but I also set the DialogResult for each button.

Image 18

This ensures that when we show this 'form' as a dialog (modal), we'll get a dialog result (which we can then use to drive the relevant business logic).

From here, we're able to show this form as a dialog (modal), take the entered password, and use it to drive business logic.

Wisej.NET/ModalPage.cs

C#
private void btnDelete_Click(object sender, EventArgs e)
{
    using (var dialog = new EnterPassword())
    {
        // show the `EnterPassword` form as a modal (dialog)
        var result = dialog.ShowDialog();

        if (result == DialogResult.OK)
        {
            // grab the entered password
            var password = dialog.Password;

            // here we could check the password,
            // and delete the user if password OK                    

            this.btnDelete.Enabled = false;
            this.btnDelete.Text = "Deleted";
        }
    }
}

In this case, we display the modal, then access the entered password (via the public Password property).

When we run this in the browser, clicking the Delete button shows the EnterPassword form as a dialog (modal), at which point we are required to close the dialog before proceeding (either by entering a password and clicking Continue or by clicking the Cancel button).

Refresh Friendly

Because Wisej.NET preserves the current UI state of the application (for any given 'session'), the state of the modal is also restored on refresh.

If we've clicked a button and a modal has appeared, server execution is paused.

We can then refresh the page, and the modal will still be open.

We're able to continue (by clicking one of the buttons), at which point execution will resume and, the relevant business logic will be executed.

Wisej.NET modal state after refresh

This 'state persistence' means we could implement complex business processes, including multiple modals (potentially showing different options based on user input), safe in the knowledge that progress through those processes would be preserved, even if the connection was temporarily lost.

Blazor Modals

Blazor doesn't ship with any built-in Modal functionality, so our options here are to:

  • Implement a Modal popup ourselves (by creating an overlay to sit in front of the page, and an HTML element to act as the modal window)
  • Use a third-party library such as Blazored Modal

If we're going to match the Wisej.NET functionality in Blazor (using Blazored.Modal) we need to:

  • Add a reference to the Blazored.Modal Nuget package
  • Reference the Blazored.Modal JavaScript and CSS files
  • Register the IModalService
  • Set up something called a Cascading Parameter to make the IModalService available to our components

Once that (one-time) setup completes, we can set about displaying modals in our Blazor components.

Here's the Blazored Modal equivalent of our Delete Confirmation example.

HTML
<button @onclick="@(()=>DeleteCustomer(customer.Id))">Delete</button>
C#
@code {

    private Customer customer = new Customer { Id = 1 };    

    [CascadingParameter] public IModalService Modal { get; set; }

    async Task DeleteCustomer(int customerId)
    {
        var modal = Modal.Show<ConfirmPassword>("Enter Password to proceed");
        var result = await modal.Result;

        if (result.Cancelled)
        {
            // Modal was cancelled
        }
        else
        {
            // Modal was closed
            var password = result.Data;
        }
    }
}

We're making use of the IModalService cascading parameter to display another component (EnterPassword) and await the result.

ConfirmPassword case is another Blazor component.

ConfirmPassword.razor

HTML
<label>
    Enter your password to continue
    <input type="password" @bind-value="password" @bind-value:event="oninput"/>
</label>

<button @onclick="Continue">Continue</button>
C#
@code {
    [CascadingParameter] BlazoredModalInstance ModalInstance { get; set; }

    string password;

    private async Task Continue()
    {
        await ModalInstance.CloseAsync(ModalResult.Ok(password));
    }
}

Note in this example, ConfirmPassword requires the BlazoredModalInstance cascading parameter to directly close the model.

Unlike the Wisej.NET example. If you refresh whilst the modal is open, any existing 'progress' will be lost, and you'll have to start over.

Some observations about the two approaches:

  • As with the data grid example, you have more direct control of the appearance of your modals with Blazor (because you are writing the HTML and CSS yourself)
  • On the flip side, with Blazor, you need to write that HTML and CSS yourself, which consumes more development time (and you're somewhat on you or your team's CSS skills)
  • Blazor depends on third-party packages to support modals (or you can implement your own)
  • Wisej.NET has built-in Modal functionality (no need to turn to a third party) and gives you the ability to style your modals as you see fit (via the WYSIWYG editor and the ability to set various properties which control appearance)
  • Wisej.NET has built-in state tracking, which can withstand lost (or 'flaky') connections
  • Wisej.NET's approach makes it possible to implement complex business logic where (multiple) Modals are required as part of the process. You can implement a multi-step process, where modals are shown at various points, and the server will remember where you are up to in that process, even on a refresh of the application

On that last point, here's an (admittedly contrived) example of a multi-stage process using Wisej.NET's ability to show message boxes with modals at various stages.

C#
private void btnDelete_Click(object sender, EventArgs e)
{
    using (var dialog = new EnterPassword())
    {
        var result = dialog.ShowDialog();

        if (result == DialogResult.OK)
        {
            // grab the entered password
            var password = dialog.Password;

            // here we could check the password,
            // and only continue if it's OK                   

            if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                if (MessageBox.Show("Are you really sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    // delete the customer then…
                    this.btnDelete.Enabled = false;
                    this.btnDelete.Text = "Deleted";
                }
            }
        }
    }
}

Clearly there's room for some refactoring here but note how you could be part way through this process, refresh, and seamlessly resume from where you left off.

Dynamic Controls

Finally, on our whistle-stop tour of Wisej.NET, let's look at implementing a dynamic UI, whereby we can add controls to the UI at runtime.

Let's say we want to implement some sort of dynamic dashboard with widgets that our users can add or remove.

Dynamic controls with Wisej.NET

With Wisej.NET, we can add any control to a container programmatically.

For example, here, I'm adding the simple Counter demo (see above) to a FlowLayoutPanel.

C#
container.Controls.Add(new Counter());

The Wisej.NET FlowLayoutPanel (and other panels in Wisej.NET) have a super handy Tools collection, whereby you can add icons to the panel's toolbar.

Here's an example:

Image 20

To test this further, I've added an 'Add' button to this FlowLayoutPanel.

From there, we can write a little code to handle a tool being clicked, then take action accordingly.

C#
private void panel1_ToolClick(object sender, ToolClickEventArgs e)
{
    var container = (Panel)sender;

    switch (e.Tool.Name)
    {
        case "Add":
            container.Controls.Add(new Counter());
            break;
        default:
            break;
    }
}

Now, when we click that button, we get new instances of Counter being added to the panel.

Each instance is independent, meaning we can increment each counter separately.

As with everything we've seen so far this state is preserved on refresh (so you can add multiple counters, increment them all separately, refresh and they'll still be there, showing their respective counts).

Image 21

We can also pass data to the control. For example, let's say we want to pass an initial count to our counters. We can use a standard C# approach here, creating a constructor which accepts an initial count:

Counter.cs

C#
public Counter(int initialCount = 0)
{
    InitializeComponent();

    this.value = initialCount;
    this.label1.Text = value.ToString();
}

Then use that constructor when we add a counter to our panel.

C#
container.Controls.Add(new Counter(100));

Dynamic controls with Blazor

Blazor (as of .NET 6) has a specialized DynamicComponent which you can use to render other components.

HTML
<DynamicComponent Type="Counter"/>

This declarative approach means we can also render a number of dynamic components by looping over a list.

C#
@code {
    readonly List<Type> _components = new List<Type>
    {
        typeof(WidgetA),
        typeof(WidgetB)
    };
}
HTML
@foreach(var component in _components){
    <DynamicComponent Type="@component"/>
}

In this example, we've created a list of component types, then looped through each, using DynamicComponent to render the relevant component.

To add a new component to the screen, we'd need to add them to the _components list, at which point Blazor would re-render and display the newly added component.

We can pass data to a dynamically rendered Blazor component using a dictionary (of string, object).

C#
private Dictionary<string, object> exampleParameters 
    = new Dictionary<string, object>
    {
        ["InitialCount"] = 100
    };

Which we can then forward to the dynamic component as Parameters:

HTML
<DynamicComponent Type="Counter" Parameters="exampleParameters"/>

Overall, there's a little more abstraction with the Blazor approach (compared to Wisej.NET, where we can more directly create controls, including parameters/data, and add them to a container).

Closing Thoughts

This was my first time exploring Wisej.NET, and my overall feeling is that it tackles some common LOB/Enterprise application requirements in a clean, scalable way.

If you're building an application where state matters (such as the current visibility of controls, partially completed form data, business processes which may run over a period of time) Wisej.NET offers a lot of functionality out of the box.

We've only really scratched the surface in this article, but for me, the notable features of Wisej.NET are:

  • Built-in state (application and UI) tracking
  • Pre-built controls (for common LOB requirements like showing data via grids)
  • Scalable architecture (can handle thousands of connections)
  • A unique client/server approach where controls are instantiated in the browser, and on the server, with interactions in either place reflected in the other
  • An accessible migration path for existing WebForms/WinForms applications

There are other factors we didn't get to, including theme management and the ability to use third-party controls from component vendors, both of which Wisej.NET provides.

Having worked on some WebForms (and indeed WinForms) web applications in the past, the approach to building 'forms' with Wisej.NET is very familiar. If you're currently maintaining a WebForms (or WinForms) application, I can see how Wisej.NET would be a very natural migration.

Switching to Blazor, I think Blazor works well for websites and applications where you want (or need) precise control over the HTML and CSS. The UI model for Blazor is based on standards-compliant HTML and CSS. Of course, this also means you have to write everything yourself or turn to third-party vendors.

Blazor is largely unopinionated (with respect to UI controls), so you'll generally need to invest time/effort into building the UI for common tasks like displaying modals or turn to a third-party solution.

This is especially true for some of the common LOB/Enterprise application requirements we've explored here. In practice, Blazor is a general-purpose web application framework with many potential use cases. As a result, you're likely to spend more time on plumbing and creating bespoke solutions (or using third-party libraries) with Blazor for these common requirements.

Ultimately any technology/framework choice comes down to trade-offs and choosing the best tool for the job.

If you're looking to build (or migrate) a LOB app, I can certainly recommend giving Wisej.NET a go, if only to establish for yourself whether it solves the key challenges you're looking to address.

Download Wisej.NET

You can download the free community version of Wisej.NET here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them.

Comments and Discussions

 
QuestionWisej vs Blazor from a dev, who using both of them Pin
Eric Ngo 20212-Sep-23 18:58
Eric Ngo 20212-Sep-23 18:58 
QuestionSource? Pin
justAcigar29-Aug-22 6:04
professionaljustAcigar29-Aug-22 6:04 
GeneralMy vote of 5 Pin
Member 1574366921-Aug-22 12:12
Member 1574366921-Aug-22 12:12 
GeneralMy vote of 1 Pin
Mauro Mandolesi19-Aug-22 12:13
Mauro Mandolesi19-Aug-22 12:13 
GeneralMy vote of 5 Pin
Tavi Butisca16-Aug-22 9:24
Tavi Butisca16-Aug-22 9:24 
QuestionWe've adopted WiseJ Pin
RedImpAway16-Aug-22 2:16
RedImpAway16-Aug-22 2:16 
AnswerRe: We've adopted WiseJ Pin
Member 1509854416-Aug-22 8:36
Member 1509854416-Aug-22 8:36 
It all sounds great...except for the price.
GeneralRe: We've adopted WiseJ Pin
RedImpAway16-Aug-22 9:18
RedImpAway16-Aug-22 9:18 
GeneralRe: We've adopted WiseJ Pin
RedImpAway27-Feb-23 3:21
RedImpAway27-Feb-23 3:21 
BugCopy-Paste error Pin
Timur Kelman15-Aug-22 23:06
Timur Kelman15-Aug-22 23:06 
GeneralMy vote of 5 Pin
Member 1344882515-Aug-22 22:27
Member 1344882515-Aug-22 22:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.