Only this pageAll pages
Powered by GitBook
1 of 51

WebMap for Blazor

WebMap Blazor

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Assessment Tool

Loading...

Getting Started

Modals and Dialogs

Loading...

Loading...

Loading...

Loading...

DCP: Desktop Compatibility Library

API Documentation

Loading...

Components Information

This is the list of components that currently Blazor tool support and his properties.

Loading...

Loading...

Loading...

Loading...

Post Conversion

How To?

Create a new WebMap Window?

Create a new WebMap Component?

Create a native Blazor Window in a WebMap app?

Create a native Blazor Component in a WebMap Window?

Change the default WebMap visual layout?

WebMap: Angular vs Blazor

Loading...

Binaries size

Chatiness

Performance

Extensibility

Maintainability

Debugging

Project Structure

Loading...

Loading...

FAQ

Errors and Troubleshooting

License

Modernization

Our solution

LiveView - Streaming Rendering patterns

To successfully convert a Windows Forms application to a Web application without needing to decouple the ViewModel or extract an API, we combine the LiveView and Streaming Rendering patterns.

  • LiveView Pattern: Enables real-time, interactive UIs by maintaining a persistent connection between client and server. The server handles rendering and logic, sending updates as the application's state changes, allowing dynamic content updates without full page reloads or extensive client-side JavaScript implementations.

  • Streaming Rendering: UI content is sent from the server to the client in small chunks as it’s generated, allowing users to view and interact with parts of the page immediately. This approach enables dynamic updates to specific portions of the page in response to user commands and asynchronously to internal parallel processes or external events.

By applying such patterns, we switch the Application architecture from this:

To this:

Our modernization process replaces the Windows Forms native OS rendering system with an HTML server-side renderer. The modernized architecture includes the following components:

  • Client Proxy: Relays raw HTML events to the server and updates the DOM based on the visual deltas sent by the server.

  • Full-Duplex Connection: Asynchronously streams commands and visual updates in near real-time.

  • Original Application Logic: HTML events are mapped to Windows Forms events and programmatically triggered in the original application logic. This initiates one or more business logic processes that modify the application state.

  • HTML Renderer: Directly bound to the application state, it reacts to its changes by generating new HTML code and computing the difference from the current visual state. Only this difference is sent back to the client.

Deployment

It is important to note that Blazor Server operates as a stateful service, where all session data is maintained in the server's memory. In scenarios with multiple users, horizontal scaling may become necessary. Since Blazor utilizes persistent WebSocket connections, the scaling infrastructure must be capable of maintaining these connections and correctly routing messages to the appropriate instances that manage the user sessions. Additionally, the system must handle reconnection logic to ensure session continuity when disruptions occur.

Session Affinity

Session affinity, or "sticky sessions," is a crucial routing mechanism in Stateful server-side applications that ensures all requests from a specific user are consistently directed to the same server instance, particularly when scaling out.

Session affinity guarantees that all SignalR messages are routed to the same instance. This approach also automatically manages reconnections when necessary.

The following diagram outlines the services that offer WebSockets session affinity, along with the deployment options for scalable back-end infrastructure across the major cloud providers (AWS, Azure, GCP). Service selection should be driven by your specific technical requirements:

What is Blazor?

Blazor is a powerful framework from Microsoft that enables the creation of interactive web applications using C# and .NET technologies, providing an alternative to JavaScript. It seamlessly integrates with existing .NET libraries and tools, offering a rich selection of visual controls to support modernization and extension of your legacy Windows Forms applications.

One of its standout features is the implementation of LiveView and streaming rendering patterns, making it an ideal choice for modernizing legacy applications.

The main advantages of using Blazor for modernization are:

  • Rich interactivity in C#: Handle arbitrary UI events and implement component logic entirely in C#, a language that is very likely to be familiar to your current development team.

  • Single Tech Stack: Your application will be implemented using a single tech stack, enabling you to build both the frontend and backend using a single codebase.

  • Efficient UI-Delta based Rendering: Blazor optimizes UI updates by carefully tracking changes in the DOM as components render, ensuring that updates are both rapid and efficient.

  • Ease of Debugging: The step-by-step debugging and Hot Reload features significantly enhance development efficiency.

Overview

Migrate Legacy Desktop Windows Forms Applications to Web.

The modernization process converts a legacy Windows Forms application into a Web-based application, stored on a remote server and delivered over the Internet through a browser interface.

This has the following advantages:

  • No installation required.

  • Not limited to Windows OS.

  • The resulting App uses Cutting-edge web frameworks and patterns.

  • Can be distributed and billed using a SaaS model.

  • [more?]

Our Modernization process has the following goals:

  • Business Logic retention: Most legacy software is a 'black box' in terms of the inner workings of its business logic. Our modernization process does not require any major analysis, refactoring, or reimplementation of existing Business logic.

  • Maintainability: The output of the Modernization process is a set of software projects that can be considered software products on their own. Unlike many other transpilers, the resulting code is intended to be human readable and modifiable.

  • Extensibility: The modernized codebase is intended to be easily extended by seamlessly interfacing with third-party Web Component libraries and new business logic implementations.

  • [more?]

How does Blazor Work?

Although any major UI design pattern, such as MVC or MVP, can be implemented in Blazor, it is especially well-suited for the MVVM pattern:

  1. DOM event: The user triggers an event (e.g., 'onclick').

  2. Event relayed to View Model: Using a WebSocket connection, the event is sent to the server and then to the session View Model.

  3. State change: The View Model executes business logic and updates the model.

  4. View Model update: The View Model reacts to model changes, updating its state and triggering the HTML renderer.

  5. HTML rendering: The HTML renderer uses the bound parameters from the Model to generate new HTML and compute the difference with the current state.

  6. Visual delta transport: HTML differences are streamed asynchronously to the server, reducing data transfer and improving performance by updating only the necessary elements.

  7. DOM update: The view is refreshed by directly modifying the DOM with the received data.

Windows Forms in Blazor

To illustrate how we use Blazor to modernize Windows Forms applications, we will use this simple example:

In this scenario, when the button is clicked, the model (Counter) is updated, and the UI automatically reflects the changes.

Upon the successful completion of our Modernization process, the expected outcome will be as follows (the code has been significantly simplified for clarity):

  • The original Windows Forms code remains essentially unchanged, with only minor modifications to accommodate the distinct characteristics of a desktop application to a server-side application.

  • Razor components, along with their associated bindings and observers, are automatically generated as part of the modernization process. There's no need for you to create or modify this code manually.

  • Bindings and observers trigger Razor components to generate HTML dynamically as needed. This HTML is then transmitted to the client, where the DOM is updated in real-time via Streaming Rendering, aiming to provide an experience as close as possible to that of a desktop application.

The following diagram details the execution flow triggered when the button is pressed:

Conversion Tool

Interfacing with hardware devices

Modern web browsers have strict security restrictions preventing direct access to hardware. Even with physical access, browsers block such connections. In some cases, hardware may be in a remote facility, unable to connect directly to the user’s machine.

Hardware devices must connect to an Agent running on a local computer or embedded device, acting as a bridge to the Blazor app. The device can be remotely controlled and send real-time telemetry to the user. This follows standard IoT principles, enabling remote monitoring and control over a network.

Scaling Blazor apps with multiple hardware device connections across servers presents routing challenges, especially with many-to-one or one-to-one user-device mappings. Load balancers with sticky sessions ensure the session stays on the same server, while message routing mechanisms ensure control commands and telemetry reach the correct user-device pair.

Different cloud providers, such as AWS, Azure, and Google Cloud, offer various solutions for achieving scalable, reliable communication between users and remote hardware devices. These solutions often involve message brokers, event routing services, and IoT platforms:

Static Service Management

Alternatives to Async Properties in C#

Current issue situation:

  • It is not allowed to have async properties in c#, it produces syntax errors, that’s why we need to look for alternatives.

  • If the property has code that could potentially be asynchronous because it has a call to, for example, a Gap.Blazor.MessageBox.Show which is an async method, the conversion tool generates an await inside the property code generating the compilation error.

Approach #1: Transform property to async methods (set and get).

This approach implies that any use of the "getter" requires an await so it can take the actual returned value instead of the task.

This approach could lead to poor legibility of the code, because of the massive amount of await sentences that could potentially be present in the code.

Example #1

Source code instance:

Migrated code instance:

The execution chain should add async and await keywords as required event for event handlers

The awaited task cannot be the entry point of the app because of some errors when it runs:

Result: It could lead to code hard to read and with slightly performance issues.

Approach #2: Changing source code, remove async modals as needed from methods that are consumed from properties

This could be the easiest solution, but could lead to problems when refactoring the customer code.

Finding

Description

Recommendation

Performance Overhead of Async Properties

Observed a slight performance overhead when using async properties due to the state machine generation.

Explore alternatives for performance-critical sections.

Complexity in Error Handling

Error handling in async properties can be complex, especially with multiple awaited operations.

Use helper methods or dedicated error handling strategies.

Testing Challenges

Unit testing async properties requires mocking and asynchronous testing patterns, adding complexity.

Develop clear testing strategies and use mocking frameworks effectively.

Alternative 1: Async Methods with Naming Convention

Using async methods (e.g., `GetAsyncValue()`) instead of async properties.

Adopt a consistent naming convention for async data retrieval.

Alternative 2: Lazy Initialization with Async Factory

Lazy initialization with an async factory method to populate the value.

Use `Lazy<Task<T>>` for delayed asynchronous initialization.

Alternative 3: Event-Based Approach

Using events to notify when the asynchronous operation completes and the value is available.

Implement event handlers and manage event subscriptions carefully.

Issue with using "MessageBox.Show" in Blazor

Issue to be solve

In Winforms you can have a constructor for a Form, and call a MessageBox.Show or a method that calls the MessageBox.Show

In Blazor, we need to wait for the MessageBox response(in most cases) to continue execution. Why most cases?

As an example, if we have a MessageBox.Show(“Message”), this is just an Expression Statement. We don’t need to wait for a response because it is just a notification, this means that the application does not need to wait for a response.

Proposed solution

This allow us to just show the notification without having to wait for the result or response.

So to achieve this, it is needed to have an event or service to notify TelerikNotification to show and use the correct message. And, by migration, we need to replace the MessageBox.Show by a different statement. The approach is to have a TelerikNotification component in the WMApp component, and this control will be listening for a subscription to show the message

Blazor Desktop Compativility Platform

In the Application.cs, we are building an event to invoke the ShowNotification.

In the migrated code, we can replace a simple MessageBox.Show by a Application.CurrentApplication.ShowNotification()

By doing this, we don’t need to change anything else (initially) in the migrated razor files. Just a replace during migration (and using the DCP changes)

This should be the result when showing the notification in an web environment application:

What if we just show a notification in a web oriented way? Well, we can use the component.

TelerikNotification

Solution and Project Structure

Research for Blazor

Modernization Challenges

[Introduction: Write a short introduction for this.]

High Coupling

A typical legacy application is usually highly coupled. The design of Windows Forms components favors the proliferation of several antipatterns, such as business logic being directly written in button events, models being used as view models, and the lack of clear architectural layer separation.

Standard Approach for Modernizing Web Applications

Modernizing a legacy Windows Forms app to a standard Web application normally involves two expensive and time-consuming steps:

  1. View Model decoupling: Modern web frameworks divide business logic (server) from presentation logic (client). From a Windows Forms perspective, this requires extensive analysis and the rewriting of most of the graphical logic.

  2. API Extraction: A high-level API must be designed to serve the client application. This requires a deep understanding of the business semantics and full access to a domain expert.

Static Service Management

Technical explanation of service implementation.

Problem

One important challenge in the upgrade from Desktop to Web application is the handle of the static variables, in this scenario, the Desktop application only have one scope, if you execute multiple instances of the same application each one will have its own context, in Web application there are different scenarios.

For example, in Blazor WebAssembly deploy it will create one scope per tab or SignalR connection, so it won’t require any change given is a similar execution mode.

In Blazor Server we only have a server-client architecture so, It'll be the same server handling different connections, the scope of the static variables are global, so if you as a user open different tabs, they’ll share the same static variables and values, not per session/tab as expected. As we can see in the following diagram the life cycle of a static variable will persist during the whole server execution.

Solution

After some research of different approaches a StaticService is implemented, this service is global and will store the value of the static variable per session, handling by itself the session and the storage. It will give us the following methods:

  • Get which will return the value stored in the service for this static variable.

  • Set will set the value into the service for this static variable.

  • InitializeData, when we have a static property auto-implemented in the source code with a default value, we need to keep the value, but C# doesn’t allow non-auto-implemented properties to be initialized, so this method will set the corresponding value to the property in the first call on Get.

This service will involve changes on the conversion of the static variables that we’ll review in the following section.

Conversion Changes

This feature implies that we need changes in the converted code, principally on the getters and setters, each one will require to call the methods. One important decision we made during the design of this feature is to keep the code visible instead of using some attribute in order to increase the visibility and the maintainability of the converted code. Here we have a basic example with no initialization:

Winforms:

Blazor:

There is another example when an initialization is set on the static variable.

Winforms:

Blazor:

Service Setup

The StaticService requires some configuration in the program of our new application, is necessary to add the Session, DistribuitedMemoryCache service, HttpContextAccessor and initialize the services as we can see in the following code:

These services are required to be provided to the StaticServices to handle the different sessions and identify which one is the current session that is being consulted.

Guide to understand Solution Generator task
Handling Reference Parameters in Async Methods with Ref<T>

Label Component

Description

This component represents a label in Blazor using the Gap.Blazor.Label model. The label is styled dynamically based on the properties of the model and supports text alignment changes.

Usage

@using Gap.Blazor.Components
@using Gap.Blazor.Extensions
@inherits WMControlComponentBase
@namespace Gap.Blazor.Components

@if (model.Visible)
{
    <WMStyleBase model=@model></WMStyleBase>
    <label class="@model.GetStringClasses() @GetAutoSizeClass()" tabindex=-1>@model.Text</label>

    <style>
        .@model.GetComponentClass() {
            display:flex;
            overflow: hidden;
            align-items : @this.label.TextAlign.GetAlignItemsStyle();
            justify-content : @this.label.TextAlign.GetJustifyContentStyle();
            border: @this.label.BorderStyle.ToCss();
        }
    </style>
}

Properties

  • label: Instance of the Gap.Blazor.Label model.

Methods

  • OnInitialized(): Initializes the component and subscribes to the TextAlignChanged event.

  • SubscribeToModelChanges(): Method called when the TextAlignChanged event is triggered.

Dynamic Rendering

The component dynamically renders a label based on the Label model properties:

  • WMStyleBase: Applies styles based on the model.

  • label: Displays the text of the Label model.

Styles

The styles for the label are defined within the component:

  • display: Sets the display to flex.

  • overflow: Hides overflow content.

  • align-items: Aligns items based on the TextAlign property.

  • justify-content: Justifies content based on the TextAlign property.

  • border: Sets the border style based on the BorderStyle property.

Events

  • TextAlignChanged: Event triggered when the text alignment changes.

Application Data Component

Description

This component handles the rendering of various forms within the application using Blazor. It listens for changes in the application state and updates the UI accordingly.

Usage

@using Gap.Blazor
@using Gap.Blazor.Components
@using Microsoft.AspNetCore.Components.Rendering
@using System.Reflection
@namespace Gap.Blazor.Components

@code {
    private bool HasChanged = false;

    protected override void OnInitialized()
    {
        base.OnInitialized();
    }

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);
        if (firstRender)
        {
            Application.CurrentApplication.ItemHasChanged += CurrentApplication_ItemHasChanged;
        }
        this.HasChanged = false;
    }

    /// <summary>
    /// Handles the ItemHasChanged event of the CurrentApplication control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    private void CurrentApplication_ItemHasChanged(object sender, EventArgs e)
    {
        if (!HasChanged)
        {
            HasChanged = true;
            this.InvokeAsync(() => this.StateHasChanged());
        }
    }
}

<div>
    @foreach (Gap.Blazor.Form form in Application.ActiveForms)
    {
        if (!form.IsMdiChild){
            if (form.View != null)
            {
                var formArgs = new Dictionary<string, object>();
                formArgs.Add("model", form);
                <DynamicComponent @key="form" Type="@form.View" Parameters="@formArgs"></DynamicComponent>
            }
            else if (form is MessageBox msgBox)
            {
                <WMMessageBoxComponent @key="msgBox" model="msgBox"></WMMessageBoxComponent>
            }
            else if (form is InputBox inputBoxForm)
            {
                <WMInputBoxFormComponent @key="inputBoxForm" model="inputBoxForm"></WMInputBoxFormComponent>
            }
            else
            {
                <WMFormComponent @key="form" model="@form"></WMFormComponent>
            }
        }
    }
</div>

Properties

  • HasChanged: Boolean flag indicating if the application state has changed.

Methods

  • OnInitialized(): Initializes the component.

  • OnAfterRender(bool firstRender): Executes after the component has rendered. Subscribes to the ItemHasChanged event if it's the first render.

  • CurrentApplication_ItemHasChanged(object sender, EventArgs e): Handles the ItemHasChanged event of the CurrentApplication control.

Events

  • ItemHasChanged: Event triggered when an item in the application changes.

Dynamic Rendering

The component dynamically renders different types of forms based on their type:

  • DynamicComponent: Renders the form's view if it exists.

  • WMMessageBoxComponent: Renders a message box form.

  • WMInputBoxFormComponent: Renders an input box form.

  • WMFormComponent: Renders a general form.

Button Component

Description

This component represents a button in Blazor using the Gap.Blazor.Button model. The button is rendered using the TelerikButton component and is dynamically styled based on the properties of the model.

Usage

@using Gap.Blazor
@using Gap.Blazor.Components
@using Telerik.Blazor.Components
@inherits WMControlComponentBase
@namespace Gap.Blazor.Components

<WMStyleBase model=@buttonModel></WMStyleBase>
<div @onkeydown="@keyDownHandler">
    <TelerikButton Class="@(buttonModel.GetStringClasses()+" "+ buttonModel.GetToolTipClass())" Title="@buttonModel.ToolTipText" Visible=@buttonModel.Visible Enabled=@buttonModel.Enabled 
OnClick=@onClickHandler ThemeColor=@(ThemeConstants.Button.ThemeColor.Base) @ref="elementRef"
    TabIndex=@TabIndex>@buttonModel.Text</TelerikButton>
</div>

<style>
    .@model.GetComponentClass(){
        background-color: @GetBackColorHex();
    }
</style>

Properties

  • buttonModel: Instance of the Gap.Blazor.Button model.

  • elementRef: Reference to the TelerikButton element.

Methods

  • onClickHandler(): Handles the button click event.

  • Focus(): Method to focus the button.

  • GetBackColorHex(): Gets the button's background color in hexadecimal format.

Events

  • OnClick: Event triggered when the button is clicked.

Styles

The button's style is dynamically defined using the GetComponentClass() method, and the background color is obtained through GetBackColorHex().

Handling Reference Parameters in Async Methods with Ref<T>

Situation:

In C#, reference parameters (ref and out) are not supported in asynchronous methods. This limitation can be problematic when you need to modify a value within an async method. The Ref<T> class and ReferenceExtensions class provide a solution to this issue by wrapping the value in a reference-like object that can be passed around and modified within async methods.

Approach: Ref Class

The Ref<T> class is a generic helper class designed to wrap a value and provide getter and setter methods for accessing and modifying the value. This class mimics the behavior of reference parameters by allowing the value to be modified indirectly.

Key Properties and Methods:

  • Getter: A Func<T> delegate that retrieves the value.

  • Setter: An Action<T> delegate that sets the value.

  • Value: The actual value being wrapped.

  • RefValueType: The type of the wrapped value.

  • Equals: Overrides the Equals method to compare the wrapped value.

  • HasMethod: Checks if the wrapped value has a specified method.

  • HasProperty: Checks if the wrapped value has a specified property.

  • HasField: Checks if the wrapped value has a specified field.

  • ToString: Overrides the ToString method to represent the wrapped value.

  • GetHashCode: Overrides the GetHashCode method to provide a hash code for the wrapped value.

  • Operators: multiple operators (logical, comparison and bool) are supported.

ReferenceExtensions Class

The ReferenceExtensions class provides an extension method to easily wrap a value in a Ref<T> instance. This extension method simplifies the creation of Ref<T> objects and allows for a more fluent syntax.

Key Method:

  • Ref: An extension method that wraps a value in a Ref<T> instance.

How the Approach Works

  1. Wrapping the Value: The Ref<T> class wraps the value and provides getter and setter methods to access and modify the value.

  2. Passing the Wrapper: The wrapped value (Ref<T>) is passed to the async method instead of the original value.

  3. Modifying the Value: Within the async method, the value can be modified using the Value property of the Ref<T> instance.

  4. Reflecting Changes: After the async method completes, the changes made to the Ref<T> instance are reflected in the original value.

Conclusion

The Ref<T> class and ReferenceExtensions class provide a robust solution for handling reference parameters in async methods. By wrapping the value in a Ref<T> instance, you can pass and modify the value within async methods, overcoming the limitations of the C# language.

Footprint

ImageListStreamer

Implementation of the ImageList control

Problem

Working on the implementation of the ImageList, we encounter the issue of management of resources through the ImageListStreamer. The ImageListStreamer is basically a serializer of all the images that are stored inside the ImageList component.

This ImageListStreamer is initialized by the .resx file associated with the .designer in which the ImageList is created. The .rex is the one that created the instance of the ImageListStreamer, serialized in base64 all the images and associated the data with a specific public token and the System.Windows.Forms.ImageListStreamer class.

This makes it very difficult to use this data to serialize out of the implementation of the WindowsForm component because it always needs the .dlls hashes to validate the information. This means that any custom implementation of the ImageListStreamer out of WindowsForm will fail when it tries to convert or deserialize the information storage in the .resx file.

Work around

As a way to interpret this mechanism through the resource, we manage to find a work-around that uses the same resource to get every image and storage in the ImageList array. Basically, the ImageList creates an instance of the ImageListStreamer and inside this, creates a method that takes the resource as a parameter and, having stored the keys for every image, gets the object Image associated with the key.

The problem with this solution is that it needs changes in the migrated code like the call of this method that fills the ImageCollection or adds every Image manually to the .resx file and associates that image name to the key so the resource can find it when it is searched.

Possible Solutions

A possible way to manage this issue is to create a pre-serializer that changes the values that reference the System.Windows.Form.ImageListStreamer for our implementation. That might let us create an instance that references the ImageStream in the .resx file and serialize normal inside the Blazor.ImageListStreamer.

Another solution is to somehow decode the images inside the ImageStream and storage separate in the .resx files of the migration project. This will help us with the work-around to minimize the manual changes in case the first solution does not work.

Blazor DCP: Gap.Blazor.Application Class Reference

The Application All Applications of any Desktop Compatibility Platform should associate the Forms with the corresponding IApplication implementation. More...

Public Member Functions

Application ()

Initializes a new instance of the Application class.

void

Dispose ()

void

Removes the active form.

Protected Member Functions

virtual void

Releases unmanaged and - optionally - managed resources.

Properties

static Application

Gets the current application.

Gets or sets the active forms.

Form

Gets or sets the active form.

EventHandler

Gets or sets the item has changed.

The Application All Applications of any Desktop Compatibility Platform should associate the Forms with the corresponding IApplication implementation.

<mapfrom>System.Windows.Forms.Application.</mapfrom>

◆ Dispose()

protectedvirtual

virtual void Gap.Blazor.Application.Dispose

(

bool

disposing

)

Releases unmanaged and - optionally - managed resources.

Parameters

disposing

true to release both managed and unmanaged resources; false to release only unmanaged resources.

◆ RemoveActiveForm()

void Gap.Blazor.Application.RemoveActiveForm

(

Form

form

)

Removes the active form.

Parameters

◆ ActiveForm

Form Gap.Blazor.Application.ActiveForm

getset

Gets or sets the active form.

The active form.

◆ ActiveForms

getset

Gets or sets the active forms.

The active forms.

◆ CurrentApplication

Gets the current application.

The current application.

◆ ItemHasChanged

EventHandler Gap.Blazor.Application.ItemHasChanged

getset

Gets or sets the item has changed.

The item has changed.


The documentation for this class was generated from the following file:

  • src/Gap.Blazor/Controls/Application.cs

Solution Generator

This document helps to understand the SolutionGeneration task, starting from the ProjectGeneration to the WFNetConversionTool call and settings to execute the Generation service.

First thing to understand is what ProjectGeneration does. ProjectGeneration contains the services and templates that generate the output files for a project, using some configurations. In this file, we will be focusing on the SolutionGeneration for Blazor.

We need to start talking about the Interfaces of the Abstractions. This interfaces define the properties and methods needed to execute the generation of the project.

We can find the interfaces in the Mobilize.ProjectGeneration.Abstraction project under the SolutionGenerator repository.

Go to IProjectGenerator, we can find some properties. You can see this properties as parameters that the Generate task needs.

Pay special attention to this method definition, because this will be called inside the Task to call the generator you need.

Now that we see this Interface, we need to understand… Why is this needed? Well, let’s jump on it.

Under the same project, we have a class called ProjectGeneratorBase.

This class implements the IProjectGenerator interface. If you navigate a bit in this class, you will find the GenerateProject implementation.

Now, we need to use the template used by the respective GenerateProject. In this case, we should go into the SolutionTemplate.tt.

Avoid touching the SolutionTemplate.cs

You can modify the template as we did here.

And save the file. When you save, a popup is showing. Press Yes. This is to synchronize the tt with the cs(the cs is generated dynamically by handlebars).

Pack the projects as a nupkg

To test the changes we can pack the projects locally, or, upload the changes to a branch and wait for the build to generate an alpha version.

To pack locally just run the next command under the build folder. Change the path for every csproj

nuget.exe pack 
S:\Repositories\EF\EF-TransformationCore\src\Mobilize.ProjectGeneration.Abstractions\Mobilize.ProjectGeneration.Abstractions.nuspec -version 22.33.11 -properties configuration=debug

Test the changes

Once you pack the changes, you need to install the package in the WFNetConversionTool. If you pack it locally, add the build folder to the package sources in the Nuget Package Manager of Visual Studio.

Or install the alpha version if you commit the changes to a branch.

Changes in the WFNetConversionTool

Now, is necessary to add the BlazorSolutionGenerationTask and the BlazorSolutionGenerator

Here we have our BlazorSolutionGenerator, that will set the params and config to call the GenerateProject from the ProjectGeneration.

Here in the Run method, we need to pass the config to the SolutionGenerationParams

And now, we can call the BlazorSolutionGenerator from the Task.

GroupBox Component

Description

This component represents a GroupBox in Blazor, which is used to group other components within a fieldset. The component is styled dynamically based on the properties of the GroupBox model.

Usage

Properties

  • groupBox: Instance of the Blazor.GroupBox model.

Methods

  • GetLegendForeColorHex(): Gets the legend's ARGB fore color in hexadecimal format.

Dynamic Rendering

The component dynamically renders a fieldset with a legend based on the GroupBox model properties:

  • WMStyleBase: Applies styles based on the groupBox model.

  • fieldset: Container for grouping child components.

  • legend: Displays the text of the GroupBox.

Styles

The styles for the fieldset and legend are defined within the component:

  • fieldset: Sets the width and border style.

  • legend: Positions the legend and sets its appearance.

Events

  • ChildContent: Renders the child content within the fieldset.

Beta version

This Beta version of WebMap for Blazor includes the following release features:

Desktop Compatibility Platform (DCP)

The DCP support is focus on basic and complex controls requires for SKS demo controls like:

Mapping Framework

To generate mapping events and models for C# .Net control to Blazor control we use Telerik library framework to create custom control maps.

Features Implemented

  • Static variables management status: since in a web environment static variables represents a restriction on the sessions that can be created / accessed this reduce the capabilities of the tool, so we create a mechanism to map static variables as services assign.

  • .Net9 Framework outcome: our applications conversion results use references for .Net 9 Framework to use latest version of supported framework.

  • DataGridView support: initial challenges on conversion are Grids management so we support basic methods, properties and events required for DataGridView.

  • Mdi Forms conversion: emulate the winforms Mdi behavior of encapsulation of forms and allow navigation with several screens.

Static scope and service scope

(Form form)

(bool disposing)

[get]

< Form >

[get, set]

[get, set]

[get, set]

<Form> Gap.Blazor.Application.ActiveForms

virtual void Gap.Blazor.Application.Dispose

(

bool

disposing

)

@namespace Gap.Blazor.Components
@using Gap.Blazor
@using System.Collections
@using Telerik.Blazor.Components
@inherits WMControlComponentBase

@if (this.Visible)
{   
    <WMStyleBase model=@groupBox></WMStyleBase>
    <div>
        <fieldset class="@this.groupBox.GetStringClasses()">
            <legend>@this.Text</legend>
            @ChildContent
        </fieldset>
    </div>

    <style>
        fieldset {
            width: 100%;
            border: 1px LightGrey solid;
        }

        legend {
            position: absolute;
            float: none;
            transform: translateY(-50%);
            width: auto;
            margin: 0 0.3em 0 0.3em;
            padding: 0 0.3em;
            border-width: 0;
            font-size: inherit;
            background-color: inherit;
        }
        
        .@model.GetComponentClass() legend {
            color: @GetLegendForeColorHex() !important;
        }
    </style>
}

Button

CheckBox

ComboBox

DataGridView

DateTimePicker

Form

GroupBox

Label

ListView

MdiContainers

MenuStrip

MessageBox

PictureBox

StatusStrip

TextBox

ToolStrip

ToolTip

RemoveActiveForm
Dispose
CurrentApplication
ActiveForms
ActiveForm
ItemHasChanged
List
List
Telerik UI for Blazor version 6.2.0
Highly coupled Model, View Model and Controller in a WinForms App
View Model decoupling, API extraction
Original Windows Forms App
LiveView and Streaming Rendering patterns applied
Deployment options
Blazor as a LiveView framework
From Windows Forms to Web
Blazor standard flow
Simple application to modernize
Modernized application structure
Sample execution flow
Cloud deployment options for Hardware devices Telemetry/control

WebMap for Blazor Release Notes

This are release notes and versioning for WebMap for Blazor:

Beta version

11-01-2024

Release Notes