A challenge to convert a WebForms application to Blazor is to resolve all PostBack elements of the WebForms using concepts of Single Page Applications.
The answer to this question is simple: Blazor Components.
After I had written the article Blazor: Is It Javascript’s Beginning of the End?, I received lots of complaints from the Javascript lovers (like me). Because I use the word WebForms in this article, it won't be a surprise if some Haters show up.
It is essential to say that if you are a new developer or have never seen ASP.NET CORE before, I suggest you read my article .NET CORE for .NET Developers first.

The Blazor

Blazor is a new Microsoft technology to create frontend using ASP.NET CORE and C#, instead of Javascript.
There are two types of Blazor applications: One that runs at the server-side called Blazor Server App, and it uses SignalR to communicate between client/server.
Blazor - More Than A Deluxe WebForms
The other type of Blazor application is Blazor Web Assembly, still in a preview version.
In that case, the application runs at the web browser, even C# DLLs.
Blazor - More Than A Deluxe WebForms
Blazor is considered Microsoft’s SPA (Single Page Application) that could fight and replace popular technologies like Angular, Vue, React, and other Javascript frameworks.

Blazor: MMVV – Model View View Model

The concepts and design patterns of MVVM are not actual.
I have been using MVVM since Silverlight, KnockoutIS, AngularJS until now in Angular, Vue, and also Xamarin Forms.
The MVVM listens to all changes that are made in a View or a Model (Javascript/C#).
If any state changes, all references in a View or a Model are updated.
Blazor - More Than A Deluxe WebForms
If you have been developing in Angular/AngularJs, imagine the variable above @currentCount like this {{ @currentCount }}.

Blazor: POC – Proof of Concept

Even Blazor uses ASP.NET CORE and Razor Views. It is reasonable to do a POC to learn more about the workflow/behavior of some things like passing parameters between components.
In the WebForms application that I am using to convert to Blazor of course there are lots of TextBox and DropDownList components.
I have decided to create all these components in Blazor with the same names, properties, and events as WebForms.
So, I created some fields and features,
Blazor - More Than A Deluxe WebForms
  • When filling the Password MaxLength TextBox field, it will limit (in characters) the Password Textbox field.
  • When leaving blank the Token TextBox field, it will be marked in a red line an invalid one.
  • When clicking the Select Three button, it will select the item 3 on the ItemsList DropDownList field.
  • When clicking the Disable Week Day button, it will disable the Week Days DropDownList field.
  • When clicking the Include Options button, it will include one more option on the Dynamic Options DropDownList field.
  • The Labels at the bottom will be updated when any field changed.
The usability of these components in the Razor Views are,
TextBox String
  1. <div class="col-md-4 mb-3">
  2. <label>User:</label>
  3. <TextBox Id="tbxUser"
  4. CssClass="teste"
  5. @bind-Text="@user"></TextBox>
  6. </div>
TextBox Number
  1. <div class="col-md-4 mb-3">
  2. <label>Password MaxLength:</label>
  3. <TextBox Id="tbxMax"
  4. @bind-Text="@max"
  5. TextMode="TextBoxMode.Number"></TextBox>
  6. </div>
TextBox Password
  1. <div class="col-md-4 mb-3">
  2. <label>Password:</label>
  3. <TextBox Id="tbxPassword"
  4. @bind-Text="@password"
  5. MaxLength="@max"
  6. TextMode="TextBoxMode.Password"></TextBox>
  7. </div>
TextBox Multiline
  1. <div class="col-md-12 mb-3">
  2. <label>Token:</label>
  3. <TextBox Id="tbxToken"
  4. @bind-Text="@token"
  5. TextMode="TextBoxMode.MultiLine"
  6. Required="true"
  7. Rows="5"></TextBox>
  8. </div>
In the case of the DropDownList, there are two ways to use it: one is declaring all DropDownListItem as options. A second way is to provide a DataSource.
  1. <div class="row">
  2. <div class="col-md-4 mb-3">
  3. <label>Week Days:</label>
  4. <DropDownList Id="ddlWeekDays"
  5. @bind-SelectedValue="@weekDay">
  6. <DropDownListItem Text="Sunday" Value="1"></DropDownListItem>
  7. <DropDownListItem Text="Monday"></DropDownListItem>
  8. <DropDownListItem Text="Tusday" Selected="true"></DropDownListItem>
  9. <DropDownListItem Text="Wednesday"></DropDownListItem>
  10. <DropDownListItem Text="Thursday"></DropDownListItem>
  11. <DropDownListItem Text="Friday"></DropDownListItem>
  12. <DropDownListItem Text="Saturday"></DropDownListItem>
  13. </DropDownList>
  14. </div>
  15. </div>
  16. <div class="row">
  17. <div class="col-md-4 mb-3">
  18. <label>Items List:</label>
  19. <DropDownList Id="ddlItemsList"
  20. DataSource="@items"
  21. @bind-SelectedValue="@item"></DropDownList>
  22. </div>
  23. </div>

Blazor: The Components – Model

One of the things that are also similar between Blazor and WebForms is the “code-behind”.
It is not mandatory but is similar.
I created the following architecture and heritance for the components,
Blazor - More Than A Deluxe WebForms
The TextBox.razor file is for HTML like INPUT tags, and this is a class that has a parent one called TextBoxComponent, which contains properties for text fields.
  1. public class TextBoxComponent : ControlComponent
  2. {
  3. public TextBoxComponent()
  4. {
  5. MaxLength = "500";
  6. }
  7. [Parameter]
  8. public bool Required { get; set; }
  9. [Parameter]
  10. public string Text { get; set; }
  11. [Parameter]
  12. public string MaxLength { get; set; }
  13. [Parameter]
  14. public string Rows { get; set; }
  15. [Parameter]
  16. public TextBoxMode TextMode { get; set; }
  17. [Parameter]
  18. public EventCallback<string> TextChanged { get; set; }
  19. [Parameter]
  20. public EventCallback<string> MaxLengthChanged { get; set; }
  21. [Parameter]
  22. public EventCallback<string> TextValueChanged { get; set; }
  23. protected async Task OnChangeAsync(
  24. ChangeEventArgs e)
  25. {
  26. Text = e.Value as string;
  27. IsValid = !(Required && string.IsNullOrEmpty(Text));
  28. await TextChanged.InvokeAsync(Text);
  29. await TextValueChanged.InvokeAsync(Text);
  30. }
  31. }
The Parameter attribute in component property means that it will be available to pass a value in other components like the sample below,
  1. <TextBox Id="tbxUser" CssClass="teste" @bind-Text="@user"></TextBox>
The TextBoxComponent has a parent called ControlCompont, which contains properties for all types of fields like Id and CssClass,
  1. public class ControlComponent : ComponentBase
  2. {
  3. public ControlComponent()
  4. {
  5. IsValid = true;
  6. }
  7. [Parameter]
  8. public string Id { get; set; }
  9. [Parameter]
  10. public string CssClass { get; set; }
  11. [Parameter]
  12. public bool Disabled { get; set; }
  13. [Parameter]
  14. public bool IsValid { get; set; }
  15. public string ValidCssClass => IsValid ? "" : "is-invalid";
  16. public string AllCssClass => $"form-control {CssClass ?? ""} {ValidCssClass}";
  17. public void ToggleDisabled()
  18. {
  19. Disabled = !Disabled;
  20. }
  21. }

Blazor: The Components – View

The TextBox component view is pretty simple,
  1. @inherits TextBoxComponent
  2. @if (TextMode == TextBoxMode.MultiLine)
  3. {
  4. <textarea id="@(Id ?? "tbx1")"
  5. class="@AllCssClass"
  6. maxlength="@MaxLength"
  7. rows="@Rows"
  8. disabled="@Disabled"
  9. required="@Required"
  10. @onchange="OnChangeAsync">@Text</textarea>
  11. }
  12. else
  13. {
  14. <input type="@TextMode.ToString().ToLower()"
  15. id="@(Id ?? "tbx1")"
  16. class="@AllCssClass"
  17. value="@Text"
  18. maxlength="@MaxLength"
  19. disabled="@Disabled"
  20. required="@Required"
  21. @onchange="OnChangeAsync" />
  22. }
As you can see above, all the HTML INPUT/TEXTAREA attributes use C# properties.
One attribute calls for our attention, I am talking about the @onchange attribute that contains a reference for an asynchronous C# method.
  1. protected async Task OnChangeAsync(
  2. ChangeEventArgs e)
  3. {
  4. Text = e.Value as string;
  5. IsValid = !(Required && string.IsNullOrEmpty(Text));
  6. await TextChanged.InvokeAsync(Text);
  7. await TextValueChanged.InvokeAsync(Text);
  8. }
When the input value changes, Blazor fires an event and calls the OnChangeAsync method.
When this happens, all references of Text are updated, even labels, other inputs, or a C# model.
This concept is called Two-Way-Binding. I mean that when you modify a property in a component, the same is updated outside.
That is possible because the TextChanged property called inside the OnChangeAsync method.
The rule to create that binding is: Create an EventCallback property, named with PropertyName + Changed, and put a Parameter attribute on it.
  1. [Parameter]
  2. public EventCallback<string> TextChanged { get; set; }
After that, call that property at any moment in your C# component.

Blazor: DropDownList and DropDownListItem

One of the differences between the TextBox and DropDownList component is the DataSource property that receives a list of items used in a Foreach statement.
  1. @inherits DropDownListComponent
  2. <select @onchange="OnChangeAsync"
  3. id="@Id"
  4. class="@AllCssClass"
  5. disabled="@Disabled">
  6. @foreach (var data in DataSource)
  7. {
  8. var value = data.Value ?? data.Text;
  9. <option value="@value" selected="@(value == SelectedValue)">@data.Text</option>
  10. }
  11. </select>
  12. <CascadingValue Value=this>
  13. @ChildContent
  14. </CascadingValue>
Another difference is that DropDownList can receive DropDownListItem components as child ones.
  1. <div class="row">
  2. <div class="col-md-4 mb-3">
  3. <label>Week Days:</label>
  4. <DropDownList Id="ddlWeekDays"
  5. @bind-SelectedValue="@weekDay">
  6. <DropDownListItem Text="Sunday" Value="1"></DropDownListItem>
  7. <DropDownListItem Text="Monday"></DropDownListItem>
  8. <DropDownListItem Text="Tusday" Selected="true"></DropDownListItem>
  9. <DropDownListItem Text="Wednesday"></DropDownListItem>
  10. <DropDownListItem Text="Thursday"></DropDownListItem>
  11. <DropDownListItem Text="Friday"></DropDownListItem>
  12. <DropDownListItem Text="Saturday"></DropDownListItem>
  13. </DropDownList>
  14. </div>
  15. </div>
ChildContent works like a @Body in a Razor View, so you must use it somewhere in your child component.
  1. [Parameter]
  2. public RenderFragment ChildContent { get; set; }
  1. <CascadingValue Value=this>
  2. @ChildContent
  3. </CascadingValue>
The DropDownListItem component has a reference of its parent DropDownList, and when it renders, it inserts a new option in the parent’s DataSource property.
  1. public class DropDownListItemComponent : ComponentBase
  2. {
  3. [Parameter]
  4. public string Text { get; set; }
  5. [Parameter]
  6. public string Value { get; set; }
  7. [Parameter]
  8. public bool Selected { get; set; }
  9. [CascadingParameter]
  10. public DropDownListComponent ParentDropDownList { get; set; }
  11. protected override void OnInitialized()
  12. {
  13. ParentDropDownList?.AddItem(this);
  14. base.OnInitialized();
  15. }
  16. }
  17. internal void AddItem(
  18. DropDownListItemComponent item)
  19. {
  20. DataSource.Add(new Models.Item
  21. {
  22. Text = item.Text,
  23. Value = item.Value ?? item.Text
  24. });
  25. if (item.Selected)
  26. {
  27. SelectedValue = item.Value ?? item.Text;
  28. SelectedValueChanged.InvokeAsync(SelectedValue).GetAwaiter();
  29. }
  30. }
You can download the full source code in my Github.

Blazor: Remarks

In a Blazor application,
The _Imports.razor file works like the Views/web.config in an MVC application.
The Startup.cs file contains all configuration to make Blazor and ASP.NET work.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddRazorPages();
  4. services.AddServerSideBlazor(); // here
  5. services.AddSingleton<WeatherForecastService>();
  6. }
  7. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  8. {
  9. if (env.IsDevelopment())
  10. {
  11. app.UseDeveloperExceptionPage();
  12. }
  13. else
  14. {
  15. app.UseExceptionHandler("/Error");
  16. }
  17. app.UseStaticFiles();
  18. app.UseRouting();
  19. app.UseEndpoints(endpoints =>
  20. {
  21. endpoints.MapBlazorHub(); // here
  22. endpoints.MapFallbackToPage("/_Host"); // here
  23. });
  24. }
The Visual Studio nests Razor components and their classes to make it easy to maintain the source code.
Blazor - More Than A Deluxe WebForms
The Tag Helpers and Razor Views still work, because Blazor is an ASP.NET Core application.
At least, all Blazor magic only works with the Pages/_Host.cshtml file that calls the App.razor component, which references the blazor.server.js script file, responsible for the SignalR stuff.
As I said, this article is about Blazor Server, and it is more potent than I had imagined. I think it will become accessible, even more, when Microsoft launches the Blazor Web Assembly version.
And about you? Have you used Blazor before?
Thank you for reading it 🙂