DEV Community

Cover image for Elanat WebForms Core 2.1 Released with a Major Update
Elanat Framework
Elanat Framework

Posted on

Elanat WebForms Core 2.1 Released with a Major Update

WebForms Core

WebForms Core, or WFC, is a new technology based on the Commander-Executor concept, developed by Elanat.

WebForms Core Organizations in GitHub

WebForms Core is a DSL for defining commands for creating, modifying, and controlling the DOM and the execution flow of the user interface, running alongside a client-side Runtime. This DSL enables declarative and Model-driven abstractions, allowing data flow, UI generation and modification, user interactions, and operation ordering to be defined without directly implementing DOM-related logic on the client side. The Runtime then interprets and executes these commands and applies the requested changes to the user interface.

WebForms VS WebForms Core VS MVC

WebForms Core is not designed only for forms and CRUD operations. It can even be extended to the creation of an interactive browser game.

Version 2.1 introduces high-level declarative capabilities. This version provides a higher abstraction over the WFC core, combining the advantages of declarative development through advanced methods and mechanisms with imperative development.

WebForms Core 2.1 supports JavaScript ES2020 and modern browsers based on common web standards from 2020–2021 and later versions. The previous version (2) supported ES2022, and in this version we committed to supporting some older browsers as well.

Therefore, this version is fully compatible with up-to-date browsers on Windows 7 and mobile devices released from 2015 onward, including Android 6.0 (with the latest Chrome version available for it, Chrome 106 in 2022) and iOS 15 (with Safari 15 in 2021).

Structure-centric UI Generation

In WebForms Core, it is possible to go beyond the Component level and focus on automatically generating UI structures instead of manually constructing individual UI components.

In this approach, rather than defining every Input, Label, form, and table individually, the developer defines rules and data structures, and the system creates the UI based on them.

For example, a Model or database table structure can be used as the basis for automatically generating Add and Edit forms, input fields, and even data presentation structures.

The developer is therefore primarily specifying "what data exists and what structure should be generated", rather than "how every HTML element should be constructed."

Compared with Blazor:

Blazor primarily provides a Component-centric UI Model, while WebForms Core can provide, in addition to reusable Components and structures, a Structure-centric or Metadata-driven UI Generation Model.

ASP.NET WebForms VS WebForms Core VS ASP.NET MVC

Other systems can also build such generators, and this is not an exclusive technological advantage. The important point is that the architecture and tools of WebForms Core are highly suitable for building systems that automatically generate DOM/UI structures.

Compared with React

React is essentially a component and state-based UI library.
WebForms Core offers something different: a DSL + Runtime to define and implement UI flow, Data Flow, and Execution Flow.

Compatibility Between WebForms and WebFormsJS

For example, WebFormsJS version 2.0.1 remains compatible with the WebForms class version 2.0 and 2.0.9.

Likewise, the WebForms class version 2.0.1 remains compatible with WebFormsJS version 2.0 and 2.0.9.

WFC is a continuously evolving technology, and using the latest versions of the technology is recommended.

WebForms Core 2.1

Version 2.1 is another major update, just like the previous two versions.

We spent more than three months writing tests for compatibility between the WebForms classes and the WebFormsJS library.

At Elanat, WFC 2.1 achieved a more than 99% quality assurance rate for WFC 2.1.

This percentage represents the success rate of our tests: we continued testing until we could no longer find problems.

The WebForms class is currently very stable. If a problem is reported in WebFormsJS in the coming days, we will fix it immediately and release version 2.1.1 and later. These patch versions remain fully compatible with the WebForms class version 2.1.

Creating HTML Comments for WebForms Responses

WebForms Core uses HTML comments as one of its mechanisms for storing and executing WebForms Action Controls.

When an HTML response contains the WebForms comment structure:

<!--[web-forms]
...
-->
Enter fullscreen mode Exit fullscreen mode

the WebForms response comment can be handled automatically.

However, the following structure:

[web-forms]
...
Enter fullscreen mode Exit fullscreen mode

is not automatically converted into an HTML comment.

WebForms Core 2.1 provides the following option for this purpose:

WebFormsOptions.CreateCommentForWebFormsResponse = false;
Enter fullscreen mode Exit fullscreen mode

When enabled, WebFormsJS can create the required HTML comment structure for WebForms responses that use the [web-forms] format.

This provides additional flexibility when handling WebForms responses.

Security Improvements

To improve security, any spaces in the HTML comment are ignored.
Therefore, Action Controls must be placed directly after the string <!--

Acceptable Example

<!--[web-forms]
bc<main>=green-->
Enter fullscreen mode Exit fullscreen mode

Ignored Examples

<!-- [web-forms]
bc<main>=green-->
Enter fullscreen mode Exit fullscreen mode
<!--
[web-forms]
bc<main>=green-->
Enter fullscreen mode Exit fullscreen mode

The location of the response has been improved in the settings.

A new StateBodyLocation option has been added for storing the State body instead of using ResponseLocation.

StateBodyLocation specifies where State changes are stored. By default, it is the <body> element.

ResponseLocation specifies where a server response should be inserted when the response is not only an Action Control—for example, when the server returns an HTML section. Its default is also <body>, but using <main> is recommended:

WebFormsOptions.ResponseLocation = "<main>";
Enter fullscreen mode Exit fullscreen mode

Both StateBodyLocation and ResponseLocation use the WebForms Place Criteria (WPC) DSL for their values.

External URL Security

New security options have also been added for external URLs:

WebFormsOptions.DisableLoadExternalHost = false;
WebFormsOptions.UseLoadExternalHostOnlyInAcceptedList = false;
WebFormsOptions.LoadExternalHostOnlyInAcceptedList = ["example.com"];
Enter fullscreen mode Exit fullscreen mode

URLs are filtered using a list and wildcard rules.

This restriction applies to external URL requests outside the current and primary server domain.

Additional Security Control with DisablePassObject

WebForms Core 2.1 also includes the following security-related option:

WebFormsOptions.DisablePassObject = false;
Enter fullscreen mode Exit fullscreen mode

This option provides additional control over object passing behavior inside the WebFormsJS runtime.

Like other WebFormsJS security options, it can be configured depending on the requirements and security model of the application.

Action Control Values Are Now Arrays

In this version, the string-based structure of Action Control values has been changed to an array-based structure.

This provides complete control over Fetch results and value types.

For example, when the result of a Fetch operation is an object, it is no longer automatically converted to a string.

This allows Action Controls to work more naturally with different types of values, including numeric values and objects.

Form Submit Check Validity

In this version, the ability to fully validate HTML forms in WebFormsJS has been added, and it is possible to change the Validation warning text for HTML inputs in the settings section.

An option in the settings called CheckValidityForFormSubmit has also been added, which is enabled by default.

We will explain this section soon in a new article with an example.

Append and Replace for Stored Data

New capabilities have been added for appending to and replacing temporarily and permanently stored data.

The following methods have been added:

  • AppendCacheValue
  • ReplaceCacheValue
  • AppendSaveValue
  • ReplaceSaveValue

Cache refers to localStorage, while Save refers to sessionStorage.

URL Hash and Segment

A new capability has been added to SetMasterPage.

It is now possible to access the URL after #, including its hash and Segment information.

Segment

A new Segment mechanism has been introduced for URLs and hashes.

A ~ character can be used after #. Everything after ~ is treated as a URL path/Segment.

For example, a URL can contain a structure such as:

#~admin/users/edit
Enter fullscreen mode Exit fullscreen mode

This mechanism makes it possible to build more powerful SPA systems.

The mechanism is also automatically enabled for internal links.

The Fetch helper class can now access Segments in the URL.

Negative indexes are supported as well:

form.message(Fetch.HashSegment(-1));
Enter fullscreen mode Exit fullscreen mode

means the last Segment.

Query access in the Fetch helper has also been converted into a method, allowing a value to be retrieved from the query by its name.

form.message(Fetch.Query("name"));
Enter fullscreen mode Exit fullscreen mode

Separator Changes

Version 2.1 contains a fundamental change to the internal separators.

In version 2.1, the separators previously represented by the | and , characters have been replaced with ASCII control characters 28, 29, 30, and 31.

Previously, the structure was effectively based on multiple character-level separators. For example, | was used as a separator while , was also used by Fetch. This could cause conflicts when a Fetch operation itself contained a list of arguments.

The new structure eliminates these conflicts.

There is now no longer even the smallest conflict caused by those common characters.

Template Engine

A new template rendering engine has been added to replace repeated manual Replace operations.

The main method is:

BindTemplate(...)
Enter fullscreen mode Exit fullscreen mode

This performs a general replacement based on NameValue data from INI, JSON, and XML sources.

The data is automatically inserted into the template according to the specified pattern.

For example:

form.BindXMLToTemplate(
    "XMLTag",
    Fetch.LoadUrl("/api/xml_data.xml"),
    "contact",
    "{{value}}"
);

form.BindINIToTemplate(
    "INITag",
    Fetch.LoadUrl("/api/ini_data.ini"),
    "contact",
    "{{value}}"
);
Enter fullscreen mode Exit fullscreen mode

An XML source can be:

<?xml version="1.0" encoding="UTF-8"?>
<contact>
    <name>John Doe</name>
    <phone>+1-555-123-4567</phone>
    <email>john.doe@example.com</email>
    <address>123 Main Street, Springfield, IL 62701, USA</address>
    <postalCode>62701</postalCode>
    <birthDate>1990-05-15</birthDate>
</contact>
Enter fullscreen mode Exit fullscreen mode

And an INI source can be:

[contact]
name=Emma Wilson
phone=+44-7700-900-123
email=emma.wilson@example.co.uk
address=45 Baker Street, London, UK
postalCode=NW1 6XE
birthDate=1988-12-03
Enter fullscreen mode Exit fullscreen mode

The template engine can then use the same structure to generate the required UI.

HTML Before BindTemplate

<h2>BindXMLToTemplate</h2>
<address id="XMLTag" class="contact-info">
  <p><strong>Name:</strong> {{name}}</p>
  <p>
    <strong>Phone:</strong> 
    <a href="tel:{{phone}}">{{phone}}</a>
  </p>
  <p>
    <strong>Email:</strong> 
    <a href="mailto:{{email}}">{{email}}</a>
  </p>
  <p><strong>Address:</strong> {{address}}</p>
  <p><strong>Postal Code</strong> {{postalcode}}</p>
  <p><strong>Birth Date:</strong> {{birthdate}}</p>
</address>

<h2>BindINIToTemplate</h2>
<address id="INITag" class="contact-info">
  <hr>
  <p><b>Name:</b> {{name}}</p>
  <br>
  <p><b>Phone:</b> <a href="tel:{{phone}}">{{phone}}</a></p>
  <br>
  <p><b>Email:</b> <a href="mailto:{{email}}">{{email}}</a></p>
  <br>
  <p><b>Address:</b> {{address}}</p>
  <br>
  <p><b>Postal Code:</b> {{postalCode}}</p>
  <br>
  <p><b>Birth Date:</b> {{birthDate}}</p>
  <hr>
</address>
Enter fullscreen mode Exit fullscreen mode

HTML After BindTemplate

<div><h2>BindXMLToTemplate</h2>
<address id="XMLTag" class="contact-info">
  <p><strong>Name:</strong> John Doe</p>
  <p>
    <strong>Phone:</strong> 
    <a href="tel:+1-555-123-4567">+1-555-123-4567</a>
  </p>
  <p>
    <strong>Email:</strong> 
    <a href="mailto:john.doe@example.com">john.doe@example.com</a>
  </p>
  <p><strong>Address:</strong> 123 Main Street, Springfield, IL 62701, USA</p>
  <p><strong>Postal Code</strong> 62701</p>
  <p><strong>Birth Date:</strong> 1990-05-15</p>
</address>

<h2>BindINIToTemplate</h2>
<address id="INITag" class="contact-info">
  <hr>
  <p><b>Name:</b> Emma Wilson</p>
  <br>
  <p><b>Phone:</b> <a href="tel:+44-7700-900-123">+44-7700-900-123</a></p>
  <br>
  <p><b>Email:</b> <a href="mailto:emma.wilson@example.co.uk">emma.wilson@example.co.uk</a></p>
  <br>
  <p><b>Address:</b> 45 Baker Street, London, UK</p>
  <br>
  <p><b>Postal Code:</b> NW1 6XE</p>
  <br>
  <p><b>Birth Date:</b> 1988-12-03</p>
  <hr>
</address>
Enter fullscreen mode Exit fullscreen mode

New Replace Capabilities

Several new Replace mechanisms have been added.

ReplaceActionControl

ReplaceActionControl replaces all Action Controls when executed.

For example:

form.ReplaceActionControl("{{segment}}", Fetch.Segment(2));

form.SetAttribute("<form>", "action", "/admin/update");
form.SetAttribute("-", "method", "put");
form.AddHidden("-", "id", Fetch.FormatStoreByJSONQuery("fs_json", "[{{segment}}].id"));
form.SetValue("(add)", "Click to update content");
form.SetName("-", "update");
Enter fullscreen mode Exit fullscreen mode

AssignReplace

AssignReplace replaces a value only in one line of Action Controls.

Its default index is -1, and it is usually written immediately after the Action Control that should be modified.

form.Message("My Message: Page Title is {{Value}}");
form.AssignReplace(
    "{{Value}}",
    Fetch.GetText(InputPlace.Head.Child("<title>"))
);
Enter fullscreen mode Exit fullscreen mode

AppendFetchReplace

AppendFetchReplace operates directly on Fetch.

form.Message(
        Fetch.GetText(
                InputPlace.Head.Child("{{Value}}")
    ).AppendFetchReplace("{{Value}}", "<title>")
);
Enter fullscreen mode Exit fullscreen mode

These mechanisms make it possible to dynamically modify Action Controls and Fetch results without manually reconstructing the entire command structure.

Debugger

WebForms Core 2.1 introduces a new Debugger.

The Debugger allows developers to control the execution of Action Controls with operations such as:

  • Go
  • Pause
  • Stop
  • Step
  • Close

A simple server-side example:

using CodeBehind;

public partial class DebugerController : CodeBehindController
{
    public void PageLoad(HttpContext context)
    {
        if (context.Request.Query.ContainsKey("many-action-controls"))
        {
            Button_Click();
            return;
        }

        WebForms form = new WebForms();

        form.SetCommentEvent("StartDebuger", HtmlEvent.OnClick, "start-debuger");
        form.SetGetEvent("Button", HtmlEvent.OnClick, "?many-action-controls");
        form.StartIndex("start-debuger");
        form.CreateDebugger();

        Write(form.ExportToHtmlComment());
    }

    public void Button_Click()
    {
        var form = new WebForms();

        form.Message("Message 1", "success");
        form.Message("Message 2", "help");
        form.SetBackgroundColor("<main>", "lightgreen");
        form.ElementExists("<main>");
        form.Message("<main> element is exist");
        form.Else();
        form.Message("<main> element is not exist");
        form.Message("Continue Action Control", "help");

        IgnoreAll();
        Write(form.Response());
    }
}
Enter fullscreen mode Exit fullscreen mode

If you want CreateDebugger() to immediately pause code interpretation, pass true:

form.CreateDebugger(true);
Enter fullscreen mode Exit fullscreen mode

This starts the Debugger in the paused state.

The Debugger is implemented inside WebFormsJS and provides a client-side control panel for controlling execution.

Debugger screenshot
Debugger screenshot

Repeat

Repeat is an extension over the GoTo command.

It provides a higher-level mechanism for repeating a group of Action Controls.

For example:

public WebForms Repeat(WebForms newForm, int repeat)
{
    if (newForm == null)
        return this;

    string bodyData = newForm.GetWebFormsData();

    if (string.IsNullOrEmpty(bodyData))
        return this;

    int startLine = bodyData.Split('\n').Length * -1;

    AppendForm(newForm);
    GoTo(startLine, repeat - 1);

    return this;
}
Enter fullscreen mode Exit fullscreen mode

There are also overloads that allow an index to be specified:

form.Repeat(f =>
{
    f.Message("Processing...");
    f.SetText("<main>", "Updated");
}, 5);
Enter fullscreen mode Exit fullscreen mode

Instead of explicitly working with GoTo, developers can use Repeat as a higher-level abstraction.

Then

The Then method was added to the Condition section.

It allows conditional commands to be continued without requiring StartBracket().

For example:

form.ElementExists("<main>")
    .Then(f =>
    {
        f.Message("The main element exists.");
        f.SetText("<main>", "Updated");
    });
Enter fullscreen mode Exit fullscreen mode

This makes conditional code easier to compose.

Conditions and Nested Execution

Conditions and loops must be able to execute in a nested manner.

In WebForms Core 2.1, the condition system has been completely rewritten in WebFormsJS.

else support has also been added to conditions.

This allows structures such as:

form.ElementExists("<main>");
form.Message("Main exists");

form.Else();
form.Message("Main does not exist");
Enter fullscreen mode Exit fullscreen mode

The new architecture allows conditions and loops to be nested and combined.

Repeat and Then were added to the server-side WebForms class, while the underlying condition execution has been completely rewritten in WebFormsJS.

Unfortunately, due to the length of the article, we cannot cover nested conditions in this article. However, we will introduce nested conditions in a new article soon.

ForEach

ForEach is a new feature introduced in WebForms Core 2.1.

It provides a declarative way to iterate over stored data and execute a set of Action Controls for each item.

For example:

WebForms form = new();

form.NotExist(Fetch.Cache("series-data"));
form.AddCacheValue("series-data", Fetch.LoadUrl("/api/series-data"));

form.ForEach("[0]", Fetch.Cache("series-data"), "foreach-data")
.Then(f =>
{
    f.AddText("{series-container}", Fetch.LoadHtml("/api/template.html", "SeriesAdmin"));

    f.BindJSONToTemplate("{series-card}-1", Fetch.FormatStore("foreach-data"), "[0]", "{{value}}");

    f.SetDeleteEvent("{delete}-1", HtmlEvent.OnClick, Fetch.GetAttribute("{delete}-1", "path"));
});

Write(form.ExportToHtmlComment());
Enter fullscreen mode Exit fullscreen mode

Because conditions and loops can now be nested, ForEach can be combined with conditional logic and other execution mechanisms.

This makes it possible to construct more complex server-defined UI workflows without manually implementing the corresponding client-side loop.

If you leave the selection name blank, a point (.) is chosen as the selection name. It is recommended to assign a unique identifier called ForEach to avoid conflicts.

The loop count is also added to the selection name as the character i.

Fetch.FormatStore("foreach-datai")
Enter fullscreen mode Exit fullscreen mode

JSON Path Improvements

The Add, Read, Delete, and Edit operations for JSON have been improved.

Advanced JSON path operations are now possible using conditional queries and negative indexing.

For example, negative indexes can be used to address items relative to the end of an array.

This provides more flexibility when working with JSON data stored inside WebForms Core.

This section will also be reviewed in detail soon as a new article.

WebForms Place Criteria (WPC)

WebForms Place Criteria is a lightweight query language for locating and filtering web contexts, including DOM elements, document objects, browser windows, and other client-side objects.

WPC has been significantly expanded in this version.

HTML Tag Selection in InputPlace

An HTML tag can now be selected in InputPlace using the . character.

Attribute-based selection has also been added, allowing elements to be selected according to both an attribute name and its value.

For example, attribute conditions can use operators such as:

^
$
*
~
Enter fullscreen mode Exit fullscreen mode

These correspond to operations such as starts-with, ends-with, contains, and word matching.

Child Selection

A powerful child selector has been added.

For example:

form.SetText("UserBox|<>0", "Hello");
Enter fullscreen mode Exit fullscreen mode

<>0 means the first child.

The developer no longer needs to know the name of the first child.

For example, instead of having to know whether the first child is a <p>:

<p>0
Enter fullscreen mode Exit fullscreen mode

the generic child selector can be used:

<>0
Enter fullscreen mode Exit fullscreen mode

This is also available as part of the WebForms class.

All Selection

All tags can be selected using the All property or *:

InputPlace.All
Enter fullscreen mode Exit fullscreen mode

or:

*
Enter fullscreen mode Exit fullscreen mode

The DSL also supports selecting all matching names, tags, classes, attributes, and children.

Criteria

A new and powerful criteria mechanism has been added.

For example, elements can be filtered by inner text:

{my-class}*?t=abc
Enter fullscreen mode Exit fullscreen mode

or by attributes:

"data-id'*abc"
Enter fullscreen mode Exit fullscreen mode

Criteria can be chained together, allowing multiple filters and operations to be applied to the same result.

The criteria system supports operations for:

  • Inner text
  • All inner text
  • Attribute values
  • Tag names
  • Visibility
  • Enabled state
  • Checked state
  • Direct children
  • Descendants
  • Ranges
  • Next siblings
  • Previous siblings
  • Intersection
  • Union
  • Difference

For example, visibility can be tested using:

?v
Enter fullscreen mode Exit fullscreen mode

and enabled or checked elements can be filtered using:

?e
?c
Enter fullscreen mode Exit fullscreen mode

Ranges also support negative indexes.

The criteria engine is implemented in WebFormsJS and provides a lightweight query language on top of the WFC element-selection system.

The criteria section will be reviewed in detail soon as part of a new article.

State Improvements

SPA links now manage State and browser history more reliably.

Synchronizing State with the browser's Back and Forward buttons was particularly complex when the user pressed these buttons before the configured State registration delay had elapsed.

The default delay is 500 milliseconds and can be changed through configuration.

The SPALink system now handles State much more closely to the behavior of normal browser history.

StartState

The StartState method has been added:

public void StartState() => StartIndex("$");
Enter fullscreen mode Exit fullscreen mode

WebForms Core interprets Action Controls line by line until it reaches a # character.

The # represents an index.

For example:

form.SetCommentEvent(
    "RunMessage",
    HtmlEvent.OnClick,
    "run-message"
);

form.SetGetEvent(
    "Button",
    HtmlEvent.OnClick,
    "?get-server-value"
);

form.StartIndex("run-message");
form.Message("Hello World!");
Enter fullscreen mode Exit fullscreen mode

The first two lines are executed normally.

When the RunMessage button (any tags with RunMessage ID) is clicked, the run-message index is executed, causing the Action Control associated with Message() to run.

StartState is a special index that is executed when returning to a previously registered State, such as when the user navigates using the browser's Back or Forward buttons.

LoadState and HasState

A new LoadState method has been added.

The Fetch helper also now includes:

Fetch.HasState("/contact");
Enter fullscreen mode Exit fullscreen mode

which can be used to determine whether a State exists.

More Control Over Browser History and DOM Event Listeners

WebForms Core 2.1 includes additional options for controlling SPA navigation and browser history behavior.

WebFormsOptions.IgnoreQueryAndHashInSPALink = false;
WebFormsOptions.ReloadOnMissingHistory = true;
WebFormsOptions.CloseAllFixedFeaturesAfterHistoryReview = true;
WebFormsOptions.RestoreListenersAfterHistoryReview = true;
Enter fullscreen mode Exit fullscreen mode

The previous sections discussed the improvements made to State management and the synchronization of WebForms Core with the browser's Back and Forward buttons.

Version 2.1 also adds more control over situations that occur during history review.

One particularly important option is:

WebFormsOptions.RestoreListenersAfterHistoryReview = true;
Enter fullscreen mode Exit fullscreen mode

DOM replacement can create a common problem in SPA-style applications: elements may be replaced and previously attached event listeners may no longer be available.

WebFormsJS 2.1 improves this process through the following runtime functionality:

cb_RestoreListenersAfterDOMReplace
Enter fullscreen mode Exit fullscreen mode

This helps WebFormsJS restore the required listeners after DOM replacement and during history-related operations.

The new history options also provide additional control when a requested browser history state is missing and when fixed UI features need to be reviewed or closed during history navigation.

Initial State Loader

A Loader has been added for registering the initial State.

The new WebFormsOptions.UseLoaderForFirstPageLoad option enables an animation Loader during the initial page load.

The Loader is displayed while the initial data is being registered, preventing the user from interacting with the page before the initial State is ready.

The default delay is 500 milliseconds and can be configured.

Dynamically Changing WebFormsJS Options

WebForms Core 2.1 also introduces the ability to dynamically change WebFormsJS options through Action Controls.

This allows configuration to be changed during runtime instead of requiring all options to remain static.

The ChangeOption method allows an individual WebFormsJS option to be changed during execution:

form.ChangeOption("QueueDebounceDelay", "500");
Enter fullscreen mode Exit fullscreen mode

In this example, the QueueDebounceDelay option is dynamically set to 500.

If the developer later wants to restore the original value of that specific option, the ResetOption method can be used:

form.ResetOption("QueueDebounceDelay");
Enter fullscreen mode Exit fullscreen mode

It is also possible to reset all dynamically changed WebFormsJS options at once:

form.ResetOption(); // Reset all WebFormsJS options
Enter fullscreen mode Exit fullscreen mode

This makes WebFormsJS configuration more flexible. Instead of treating options as static configuration that must remain unchanged after the page loads, WebForms Core can modify individual runtime behaviors dynamically and later restore either a specific option or the entire configuration to its original state.

Better SSE and WebSocket Connection Management

WebForms Core 2.1 provides more control over persistent connections and adds more methods for better SSE and WebSocket management.

Developers can configure the maximum number of reconnection attempts:

WebFormsOptions.WebSocketReconnectMaxRetries = 5;
WebFormsOptions.SSEReconnectMaxRetries = 5;
Enter fullscreen mode Exit fullscreen mode

WebForms Core can also check the current connection state through the Fetch helper class.

SSE Connection State

Fetch.SSEIsConnected("/events")
Enter fullscreen mode Exit fullscreen mode

The following methods are also available for managing SSE connections:

form.DisconnectSSE("/events");
form.DisconnectAllSSE();
Enter fullscreen mode Exit fullscreen mode

WebSocket Connection State

Fetch.WebSocketsIsConnected("/socket")
Enter fullscreen mode Exit fullscreen mode

WebSocket connections can also be explicitly removed:

form.DeleteWebSocket("/socket");
Enter fullscreen mode Exit fullscreen mode

These additions provide better control over the complete lifecycle of persistent connections.

Instead of only creating SSE and WebSocket connections, applications can now more easily monitor whether connections are active, configure reconnection behavior, disconnect SSE connections individually or collectively, and remove WebSocket connections when they are no longer needed.

Retry Management for Requests

WebForms Core 2.1 provides additional control over requests that do not produce the expected result.

Developers can configure whether WebFormsJS should retry a request and define both the maximum number of retry attempts and the interval between them.

WebFormsOptions.IgnoreEmptyResult = false;

WebFormsOptions.UseRetryRequest = true;
WebFormsOptions.MaxRetryCount = 3;
WebFormsOptions.RetryRequestInterval = 3000;
Enter fullscreen mode Exit fullscreen mode

In this example, WebFormsJS can retry a request up to three times, with a three-second interval between attempts.

The IgnoreEmptyResult option also provides control over how empty responses are handled.

These options provide greater flexibility when dealing with temporary connection problems or situations where a request may need to be attempted again.

Runtime Console Diagnostics

WebForms Core 2.1 provides more detailed control over diagnostic messages generated by the WebFormsJS runtime.

Developers can enable or disable general console output and individually control diagnostic messages for different parts of the runtime.

WebFormsOptions.AddConsoleMessage = true;
WebFormsOptions.UseConsoleStackTrace = false;

WebFormsOptions.AddConsoleMessageForHTTP = false;
WebFormsOptions.AddConsoleMessageForURL = true;
WebFormsOptions.AddConsoleMessageForWebSockets = true;
WebFormsOptions.AddConsoleMessageForSSE = true;
WebFormsOptions.AddConsoleMessageForModule = true;
Enter fullscreen mode Exit fullscreen mode

This allows developers to control diagnostic output for:

  • HTTP requests
  • URL processing
  • WebSocket connections
  • Server-Sent Events
  • WebFormsJS modules

The AddConsoleMessageForHTTP feature is disabled by default. When enabled, it logs XMLHttpRequest requests to the Console.

The UseConsoleStackTrace option can also be used to control whether stack trace information is included in diagnostic output. This feature is disabled by default and when enabled, errors are displayed with additional details, such as the error line in the Console.

These options are particularly useful during development and debugging because developers can focus on specific parts of the WebFormsJS runtime instead of enabling unnecessary diagnostic output for everything.

Message, Confirm, and Alert

The message, confirm, and alert features have been rewritten.

They are now fully synchronized with the current page State.

This makes these UI interactions behave consistently with State changes and SPA navigation.

HTTP Methods

The TRACE and CONNECT methods have been removed.

The PUT method can now also send requests outside a <form> element.

This is useful because some servers may not handle the PUT method correctly with enctype="multipart/form-data" and may require POST or additional server-side configuration.

In practice, public servers have introduced non-standard limitations around this behavior.

The new PUT methods are similar to other common methods such as GET, DELETE, and PATCH.

  • SetPutEvent
  • SetPutEventListener
  • RemovePutEvent
  • RemovePutEventListener
  • CallPutBack

Removal of <web-forms>

The <web-forms> tag has been permanently removed.

Previously, this tag stored Action Control values inside the HTML itself and allowed the tag to be executed offline.

WebForms Core 2.1 removes this mechanism and retains HTML comments as the offline Action Control mechanism.

For example:

using CodeBehind;

public partial class CommentBackController : CodeBehindController
{
    public void PageLoad(HttpContext context)
    {
        if (context.Request.Query.ContainsKey("run_ac_in_comment"))
        {
            Button1_OnClick(context);
            return;
        }

        WebForms form = new WebForms();

        form.SetGetEventListener(
            "Button1",
            HtmlEventListener.Click,
            "?run_ac_in_comment"
        );

        form.SetCommentEvent(
            "Button2",
            HtmlEvent.OnClick,
            "1",
            "<main>"
        );

        form.SetBackgroundColor("<main>", "silver");
        form.Message("Message 1");
        form.Message("Message 2");

        form.StartIndex();
        form.Message("Message 3");
        form.Message("Message 4");

        form.StartIndex();
        form.Message("Message 5");
        form.Message("Message 6");

        form.StartIndex("Message7-8");
        form.Message("Message 7");
        form.Message("Message 8");

        Write(form.ExportToHtmlComment());
    }

    private void Button1_OnClick(HttpContext context)
    {
        WebForms form = new WebForms();

        form.CallCommentBack("Message7-8");

        IgnoreAll();
        Write(form.Response());
    }
}
Enter fullscreen mode Exit fullscreen mode

The Action Controls remain inside HTML comments and can therefore be executed offline without the old <web-forms> element.

JavaScript Access with $

The $ character can now be used to access WebFormsJS and JavaScript data.

For example:

form.SetMethodEventListener(
    "EventListenerDblQuot",
    HtmlEventListener.Click,
    "alert",
    ["$document.getElementsByTagName('button')[1].outerHTML"]
);
Enter fullscreen mode Exit fullscreen mode

The $ mechanism can be used anywhere except where an InputPlace is expected.

If $@ is used, the object itself is passed rather than its output:

form.SetMethodEvent(
    "EventDblQuot",
    HtmlEvent.OnClick,
    "alert",
    ["$@document"]
);
Enter fullscreen mode Exit fullscreen mode

If you need to escape $, use two $ characters:

$$
Enter fullscreen mode Exit fullscreen mode

For example, $$ represents an escaped $.

Automatic Number Conversion

In the new structure, data that can be converted to numbers is automatically added as Number.
If the output is of numeric type and you do not want it to be converted to a number, enclose the number between two single quotes (') or double quotes (").

form.SetMethodEvent("Event", HtmlEvent.OnClick, "add", ["'10'", "'24'"]);
Enter fullscreen mode Exit fullscreen mode

The output of the above example is the string 1024.

Memory Management

Several improvements have been made to memory management.

A Garbage Collector-like structure has been added that now removes and manages previously allocated data more efficiently.

The new code has also been reviewed to avoid introducing memory leaks.

InputPlace and Naming Changes

The AppendPlace extension function has been renamed to:

Child
Enter fullscreen mode Exit fullscreen mode

This better reflects its purpose within the InputPlace DSL.

Session, Session Cache, and Saved naming have also been removed.

From now on, only the name Save is used for sessionStorage.

Numeric Comparisons

The IsGreaterThan and IsLessThan conditions have been improved.

Numbers and strings are now correctly distinguished, allowing numerical comparisons to work properly.

For example, numeric values are no longer incorrectly compared as strings.

Query and Hash State

A new option prevents query and hash links from creating a new browser State:

WebFormsOptions.IgnoreQueryAndHashInSPALink = false;
Enter fullscreen mode Exit fullscreen mode

The default value is false.

When enabled, SPA links containing query strings or hashes can be excluded from automatic State creation.

Other Features

We have already described a number of new features in version 2.1:

  • Added WASM Mediator support for C# programming language.

Full article:

https://dev.to/elanatframework/net-webcil-container-meets-webforms-core-21-39f5

  • New Morph feature.

Full article:

https://dev.to/elanatframework/morphing-feature-in-webforms-core-21-5d6o

Additional Fixes and Improvements

This release also contains many smaller improvements and fixes:

  • Improved performance.
  • Improved existing methods.
  • Added several new minor capabilities.
  • Improved Garbage Collection and data cleanup.
  • Fixed selection of multiple tags when only one matching tag exists.
  • Prevented SSE from running inside Service Workers because of an interaction conflict.
  • Improved IncreaseHeight.
  • Improved IncreaseWidth.
  • Fixed scrolling after clicking SPA links.
  • Fixed an issue where AddState could not change the title at the same time.
  • Fixed event registration issues in CommentBack.
  • Fixed changing Action Controls for InputPlace lists such as <button>*.
  • Fixed State support for attributes of selected tags.
  • Fixed State support for input, select, and textarea values.
  • Fixed additional requests being generated in conditional states.
  • Support for PATCH and DELETE when submitting a form
  • Better detection of browser support for compression

WebForms and WebFormsJS Patch-Version Compatibility

WebForms Core maintains compatibility across patch versions.

For example, WebForms.cs version 2.1.5 remains compatible with WebFormsJS 2.1.

The same compatibility principle applies to the other WebForms classes.

Patch versions such as 2.1.1, 2.1.2, 2.1.5, and later can contain bug fixes and improvements while remaining compatible with the WebFormsJS 2.1 core version.

It is recommended to always use the latest patch versions available.

Package Size

We used ASP.NET 10 for testing.

Because .NET 9 introduced a planned replacement of the older zlib dependency with zlib-ng, and we are using .NET 10, comparing package sizes directly with previous versions is more difficult.

The .NET team also discussed a similar issue in its official .NET 9 performance evaluation: with the compression infrastructure changing to zlib-ng, Fastest can produce noticeably larger output than the previous implementation while providing significantly faster compression. The .NET team specifically recommends reevaluating the balance between compression speed and compression ratio when using Fastest.

Therefore, while WebFormsJS 2.0 previously showed 39.59 KB with GZip + Minify under .NET 7.0 using Fastest, our new test under .NET 10 shows 47.84 KB.

CompressionLevel.SmallestSize

RAW:             411.73 kB
RAW + GZip:       63.22 kB
Minify:          179.24 kB
Minify + GZip:    43.37 kB
Enter fullscreen mode Exit fullscreen mode

CompressionLevel.Fastest

RAW:             411.73 kB
RAW + GZip:      101.14 kB
Minify:          179.24 kB
Minify + GZip:    61.26 kB
Enter fullscreen mode Exit fullscreen mode

As you can see, WebFormsJS 2.1 is approximately 60 KB under the ideal server-side compression configuration.

There is still a long way to go before reaching 100 KB! 😄

The Settings section is intentionally left readable.

Please note that starting with version 2.1, the Settings section is not included in Minify.

This is intentional so that developers can configure the settings more easily and have an easier time transferring and applying their configuration when upgrading to newer versions.

Conclusion

WebForms Core 2.1 is a major expansion of the technology's declarative capabilities.

The new version goes beyond basic server-driven DOM manipulation by introducing higher-level mechanisms for:

  • Declarative UI generation
  • Template rendering
  • URL Segments
  • Advanced State management
  • Nested conditions
  • Then
  • Repeat
  • ForEach
  • WPC criteria
  • Advanced element selection
  • Runtime option management
  • Debugging
  • Improved JSON path operations
  • Dynamic Action Control replacement
  • Improved JavaScript interoperability
  • Better browser history integration
  • Improved memory management

The goal is not to eliminate imperative development.

Instead, WebForms Core 2.1 provides higher-level abstractions where they are useful while preserving the direct, imperative capabilities of the WFC core.

This makes it possible to move between low-level DOM instructions, reusable Components, declarative conditions and loops, metadata-driven UI generation, and more advanced server-driven application architectures—all within the same technology.

Top comments (0)