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 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.
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]
...
-->
the WebForms response comment can be handled automatically.
However, the following structure:
[web-forms]
...
is not automatically converted into an HTML comment.
WebForms Core 2.1 provides the following option for this purpose:
WebFormsOptions.CreateCommentForWebFormsResponse = false;
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-->
Ignored Examples
<!-- [web-forms]
bc<main>=green-->
<!--
[web-forms]
bc<main>=green-->
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>";
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"];
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;
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:
AppendCacheValueReplaceCacheValueAppendSaveValueReplaceSaveValue
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
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));
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"));
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(...)
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}}"
);
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>
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
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>
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>
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");
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>"))
);
AppendFetchReplace
AppendFetchReplace operates directly on Fetch.
form.Message(
Fetch.GetText(
InputPlace.Head.Child("{{Value}}")
).AppendFetchReplace("{{Value}}", "<title>")
);
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());
}
}
If you want CreateDebugger() to immediately pause code interpretation, pass true:
form.CreateDebugger(true);
This starts the Debugger in the paused state.
The Debugger is implemented inside WebFormsJS and provides a client-side control panel for controlling execution.
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;
}
There are also overloads that allow an index to be specified:
form.Repeat(f =>
{
f.Message("Processing...");
f.SetText("<main>", "Updated");
}, 5);
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");
});
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");
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());
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")
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:
^
$
*
~
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");
<>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
the generic child selector can be used:
<>0
This is also available as part of the WebForms class.
All Selection
All tags can be selected using the All property or *:
InputPlace.All
or:
*
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
or by attributes:
"data-id'*abc"
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
and enabled or checked elements can be filtered using:
?e
?c
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("$");
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!");
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");
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;
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;
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
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");
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");
It is also possible to reset all dynamically changed WebFormsJS options at once:
form.ResetOption(); // Reset all WebFormsJS options
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;
WebForms Core can also check the current connection state through the Fetch helper class.
SSE Connection State
Fetch.SSEIsConnected("/events")
The following methods are also available for managing SSE connections:
form.DisconnectSSE("/events");
form.DisconnectAllSSE();
WebSocket Connection State
Fetch.WebSocketsIsConnected("/socket")
WebSocket connections can also be explicitly removed:
form.DeleteWebSocket("/socket");
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;
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;
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.
SetPutEventSetPutEventListenerRemovePutEventRemovePutEventListenerCallPutBack
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());
}
}
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"]
);
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"]
);
If you need to escape $, use two $ characters:
$$
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'"]);
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
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;
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
AddStatecould 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, andtextareavalues. - 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
CompressionLevel.Fastest
RAW: 411.73 kB
RAW + GZip: 101.14 kB
Minify: 179.24 kB
Minify + GZip: 61.26 kB
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
ThenRepeatForEach- 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)