Blazor FAQ - Event handling

Find answers for the most frequently asked questions
Expand All Collapse All

Using JavaScript onmousemove and onkeypress events to trigger the Timer function in a Razor component can identify whether a user is active or inactive. If the user has not performed any onmousemove or onkeypress events, the user is considered in an inactive state and the Timer function is called after certain TimeInterval to log the user out.

Follow these steps to log the user out automatically if they’re not active:

Call the onmousemove and onkeypress properties in a JavaScript function to fire the Timer function in the Razor component.
[_Host.cshtml]

<body>
     // . . .
     <script>
         function timeOutCall(dotnethelper) {
             document.onmousemove = resetTimeDelay;
             document.onkeypress = resetTimeDelay;
  
             function resetTimeDelay() {
                 dotnethelper.invokeMethodAsync("TimerInterval");
             }
         }
     </script>
 </body>

Now call the Timer method to identify whether the user is active or not. Add navigation to the logout action when the user is inactive.
[MainLayout.razor]

@using System.Timers
@inject NavigationManager UriHelper
@inject IJSRuntime JSRuntime
  
 // . . .
 @code {
     [CascadingParameter]
     private Task<AuthenticationState> stateAuthenticate { get; set; }
     private Timer timerObj;
  
     protected override async Task OnInitializedAsync()
     {
         // Set the Timer delay.
         timerObj = new Timer(7000);
         timerObj.Elapsed += UpdateTimer;
         timerObj.AutoReset = false;
         // Identify whether the user is active or inactive using onmousemove and onkeypress in JS function.
         await JSRuntime.InvokeVoidAsync("timeOutCall", DotNetObjectReference.Create(this));
     }
  
     [JSInvokable]
     public void TimerInterval()
     {
         // Resetting the Timer if the user in active state.
         timerObj.Stop();
         // Call the TimeInterval to logout when the user is inactive.
         timerObj.Start();
     }
  
     private void UpdateTimer(Object source, ElapsedEventArgs e)
     {
         InvokeAsync(async() => {
             // Log out when the user is inactive.
             var authstate = await stateAuthenticate;
             if (authstate.User.Identity.IsAuthenticated)
             {
                 UriHelper.NavigateTo("logout", true);
             }
         });
     }
 } 
Permalink

The LocationChanged event handler is triggered when there is a change in the navigation location. In the example below, a JavaScript interop function is used to display an alert to the user whenever the navigation location changes:

@inject NavigationManager UriHelper
@inject IJSRuntime JSRuntime 

<p> When navigating to another page, an alert box will be displayed to indicate the navigation. </p> 

@code { 
    protected override void OnInitialized ()   
    { 
        UriHelper.LocationChanged += DetectNavigation; 
    } 

    private void DetectNavigation ( object sender, LocationChangedEventArgs e ) 
    { 
        JSRuntime.InvokeVoidAsync("alert", "Navigation event is triggered"); 
    } 
} 

Refer to this documentation for more details. 

Permalink

Blazor has a built-in InputFile component from .NET 5.0 that can be used to upload files to the server. You can check the file size in the OnChange event and upload or prevent uploading the files to the server.

[Index.razor]

@page “/”

<h3>File Upload</h3>

<InputFile OnChange=”OnInputFileChange” />

@if (file?.Size < 1000000)
{
    <p>Name: @file.Name</p>
    <p>Size in bytes: @file.Size</p>
    <p>Last modified date: @file.LastModified.DateTime.ToShortDateString()</p>
    <p>Content type (not always supplied by the browser): @file.ContentType</p>
}
else
{
    <p>File size exceeds more than 1MB</p>
}

@code {
    IBrowserFile file;

    void OnInputFileChange(InputFileChangeEventArgs e)
    {
        file = e.File;
        if (e.File.Size < 1000000)
        {
            // Upload files to the server from here
        }
    }
}

Note: Syncfusion offers a feature-rich, easy-to-use File Upload component for Blazor. You can check it out here.

Permalink

To detect a browser keypress event, set the @onkeypress event to the corresponding element. For hotkey functions, you can use the event arguments to check whether the Ctrl, Shift, or Alt key is pressed.

[Index.razor]

@page "/"

<input type="text" @onkeypress="KeyboardEventHandler" />

<h4>@KeyPressed</h4>

@code {
    string KeyPressed = string.Empty;
    bool IsShiftKey;
    private void KeyboardEventHandler(KeyboardEventArgs args)
    {
        if (args.ShiftKey || args.CtrlKey || args.AltKey)
        {
            // Do some hotkey function process here
        }
        else
        {
            KeyPressed = "Key Pressed is " + args.Key;
        }
    }
}

For more information on this topic, check this documentation.

Permalink

In Blazor, all native events are bound to async task. You can bind the button click event with async task handler. In the following example, the button click event bound to async method.

<button @onclick="@onClick"> Click </button>

@code {

    private async Task onClick()
    {
        await service.GetItems();
    }
}
Permalink

Blazor provides support for passing the route parameter as DateTime format. This needs to be mentioned while defining the route parameter in the route as @page “/route/{parameter:datetime}” at the top of the .razor component file..

Refer to the following code sample.

index.razor

@page "/"

<button @onclick="CurrentTime">Current Time</button>

@code {
    public void CurrentTime()
    {
        NavManager.NavigateTo("/time/" + DateTime.Now);
    }
}

Time.razor

@page "/time/{param:datetime}"

<h3>Time</h3>
<p>@Param</p>
@code {
    [Parameter]
    public DateTime Param { get; set; }
}
Permalink

To upload files in Blazor applications, install the NuGet package, BlazorInputFile. This package has the component, Blazor input file that is used to upload files.
You also need to include the input file scripts in HTML and add BlazorInputs to the _Imports.razor file.

[_Host.cshtml]

  <script src="_content/BlazorInputFile/inputfile.js"></script>
[index.razor]

@page "/upload"

<h3>Upload</h3>

<InputFile OnChange="HandleFileSelected"  class="btn-primary"/>

@if (file != null)
{
    <p>Name: @file.Name</p>
    <p>Size in bytes: @file.Size</p>
    <p>Last modified date: @file.LastModified.ToShortDateString()</p>
    <p>Content type (not always supplied by the browser): @file.Type</p>
}

@code {
    IFileListEntry file;

    void HandleFileSelected(IFileListEntry[] files)
    {
        file = files.FirstOrDefault();
    }
}

You can handle multiple file uploads by adding multiple attribute to the InputFile component.

@page "/multiupload"

<h3>Multiple File Upload</h3>

<InputFile multiple OnChange="HandleFileSelected"  class="btn-primary"/>

@if (file != null)
{
   <p>Name: @file.Name</p>
   <p>Size in bytes: @file.Size</p>
    <p>Last modified date: @file.LastModified.ToShortDateString()</p>
    <p>Content type (not always supplied by the browser): @file.Type</p>
}

@code {
    IFileListEntry file;

    void HandleFileSelected(IFileListEntry[] files)
    {
        file = files.FirstOrDefault();
    }
}

Please refer to this link here for more information on file uploading in Blazor.

Note: Syncfusion offers feature rich as well as easy to use file upload component. For more information, please check the link.

Permalink

Blazor detects the UI changes in common scenarios like EventCallback (button click, dropdown select, etc.), and refreshes the component. However, there are some situations in an app where a UI refresh needs to be triggered manually to re-render the component. The StateHasChanged method is used to force re-render a component UI.

@using System.Threading;

<h1>@Count</h1>

<button @onclick=@StartCountdown>Start Timer</button>

@functions {
    private int Count { get; set; } = 10;

    void StartCountdown()
    {
        var timer = new Timer(new TimerCallback(_ =>
        {
            if (Count > 0)
            {
                Count--;

                // Note that the following line is necessary because otherwise
                // Blazor would not recognize the state change and not refresh the UI
                InvokeAsync(() =>
                {

                    StateHasChanged();
                });
            }
        }), null, 1000, 1000);
    }
}
Permalink

You can  bind an input of type time in Blazor by using the @bind property to the time value and @bind:format property to specify the supported time format in the DOM input element.

Refer to the following code sample.

<input type="time" @bind="SomeTime" @bind:format="HH:mm"/>

@code {
    public DateTime SomeTime = new DateTime();
}
Permalink

String comparison with case insensitivity can be carried out using the  String.Compare method, where the first and second parameters are the strings to be compared and the third parameter is for ignoring case sensitivity (case insensitive comparison).

@page "/"

<h1>Case in-sensitive string comparision</h1>

<br />

<EditForm Model="@_stringCompare">
    String 1:
    <InputText id="string1" @bind-Value="_stringCompare.String1" />
    <br />
    String 2:
    <InputText id="string2" @bind-Value="_stringCompare.String2" />
    <br />
</EditForm>

<br />

<button @onclick="Compare">Compare</button>

<br />
<br />

<p>@Output</p>

<br />

@code {

    private StringCompare _stringCompare = new StringCompare();
    public string Output = "";

    public class StringCompare
    {
        public string String1 { get; set; }
        public string String2 { get; set; }
    }

    public async void Compare()
    {
        int CheckValue = String.Compare(_stringCompare.String1, _stringCompare.String2, true);
        Output = "Entered Strings are " + (CheckValue == 0 ? "" : "not ") + "Similar";
        await Task.Run(() => TimeOutMethod());
        Output = "";
        await Task.CompletedTask; 
    }

    void TimeOutMethod() => Task.Delay(3000).Wait();
}

View Sample in GitHub

Permalink

You can check/uncheck the checkbox in the Blazor programmatically by using the @bind parameter in the checkbox. Assign a Boolean property to @bind in the checkbox and toggle the Boolean property programmatically.

@page 
<input type="checkbox" @bind="@boolvalue" />
<br />
Checkbox: @val
<br />
<button class="btn btn-primary" @onclick="@ToggleCheckbox ">toggle</button>

@code {

public bool boolvalue { get; set; }
public string val;
 void ToggleCheckbox()
 {
        if (boolvalue)
        {
            val = "unchecked";
        }
        else
        {
            val = "checked";
        }
        boolvalue = !boolvalue;
}
}

Permalink

To get the checkbox value when it is checked or unchecked use the onchange event by calling a method in onchange event using lambda expression and passing the checkbox value to it.

@foreach (var check in CheckBoxList())
{
 <input class="custom-checkbox" type="checkbox" @onchange="eventArgs => { CheckboxClicked(check, eventArgs.Value); }" />
@check
 <br />
}

@code {

public List<string> CheckBox { get; set; } = new List<string>();
void CheckboxClicked(string CheckID, object checkedValue)
 {
if ((bool)checkedValue)
{
        if (!CheckBox.Contains(CheckID))
        {
                CheckBox.Add(CheckID);
         }
 }
 else
 {
        if (CheckBox.Contains(CheckID))
         {
                CheckBox.Remove(CheckID);
         }
 }
 }

public List<String> CheckBoxList()
{
List<String> checkBox = new List<String>();
checkBox.Add("CheckBox 1");
checkBox.Add("CheckBox 2");
return checkBox;
}
}
Permalink

Blazor does not have support to manipulate DOM elements directly, but we can still achieve it by using JavaScript interop. By creating a reference to an element, we can send it as a parameter to the JavaScript method via interop. In that JavaScript method, we can manipulate the DOM elements.

[script.js]
window.setElementText = (element, text) => {
    console.log(element);
    element.innerText = text;
}

Refer the script file in HTML page

[index.html/_Host.cshtml]
<head>
 .....
 <script src="~/script.js"></script>
 ....
</head>
[Counter.razor]

@page "/counter"

@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<p>Last button clicked: @lastButtonClicked</p>

<button @ref=button1 class="btn btn-primary" @onclick="@(()=>IncrementCount(@button1))">Click me</button>
<button @ref=button2 class="btn btn-primary" @onclick="@(()=>IncrementCount1(@button2))">Click me</button>


@code {

    private ElementReference button1;
    private ElementReference button2;

    private int currentCount = 0;
    private string lastButtonClicked = "None";

    private async void IncrementCount(ElementReference element)
    {
        currentCount++;
        lastButtonClicked = "Button 1";
        await JSRuntime.InvokeVoidAsync("setElementText", element, "Button 1 was clicked");

    }
    private async void IncrementCount1(ElementReference element)
    {
        currentCount++;

            lastButtonClicked = "Button 2";
            await JSRuntime.InvokeVoidAsync("setElementText", element, "Button 2 was clicked");
    }
}
Permalink

We can specify what event the bind attribute should use to handle updating the value. But, in components, we need to define the event in the child component, for example, the oninput event on the child component, which triggers a method to update the value and invokes the ValueChanged EventCallback. The parent component has also been updated to use bind-Value when passing its Value parameter to the child component.

In the sample, we’re able to type in the text box on the parent component, both the values will be updated on every input change since we have bound the oninput event.

[CustomInput.razor]

<input value="@Value" @oninput="@OnInputChange" />
<h4>Welcome to Blazor Application, @Value</h4>

@code {
    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }
    private async Task OnInputChange(ChangeEventArgs args )
    {
        Value = (string)args.Value;
        await ValueChanged.InvokeAsync(Value);
    }
}
[Index.razor]

@page "/"

<CustomInput @bind-Value="@InputValue" @bind-Value:event="ValueChanged"></CustomInput>

@code { 
       public string InputValue = "Example";
}
Permalink

You can capture input keyboard events by attaching the event handler to the keyboard events such as Keydown, Keypress, or Keyup with the input control. In the following example, the keyboard event handler is attached to all these three events. If you want to identify which keyboard event is captured, you can get the type using the KeyboardEventArg.Type property.

[index.razor]

@page "/"

<input type="text" @onkeydown="KeyboardEventHandler " @onkeypress=" KeyboardEventHandler " @onkeyup="KeyboardEventHandler "/>

<h4>@KeyPressed </h4>
<h4>@EventInfo</h4>

@code {
    string KeyPressed = "";
    string EventInfo = "";
    private void KeyboardEventHandler(KeyboardEventArgs args)
    {
        KeyPressed = "Key Pressed is " + args.Key;
        EventInfo = "Event Type " + args.Type;
    }
}
Permalink

To read the current value of an input using @onkeypress event, this event uses the KeyboardEventArgs. You can also get the last pressed key value in args.Key.

[index.razor]

@page"/"

<input type="text" @onkeypress="@Keypress" />
<h1>@KeyPressed</h1>

@code {
    string KeyPressed = "";
    private void Keypress(KeyboardEventArgs args)
    {
        KeyPressed = "Key Pressed is " + args.Key;
    }
}

Permalink

While making an API call, create and run an asynchronous task with the Run method to notify the wait using a spinner. The completion of the task can be notified using the  CompletedTask property.

[index.razor]

@page "/" 

<style> 
    .loader { 
        border: 5px solid #f3f3f3; 
        border-radius: 50%; 
        border-top: 5px solid #f58205; 
        width: 30px; 
        height: 30px; 
        -webkit-animation: spin 2s linear infinite; /* Safari */ 
        animation: spin 2s linear infinite; 
    } 
    /* Safari */ 
    @@-webkit-keyframes spin { 
        0% { 
            -webkit-transform: rotate(0deg); 
        } 
        100% { 
            -webkit-transform: rotate(360deg); 
        } 
    } 
    @@keyframes spin { 
        0% { 
            transform: rotate(0deg); 
        } 
        100% { 
            transform: rotate(360deg); 
        } 
    } 
</style> 

<h1>Counter</h1> 

<p> 
    Current count: <div class="@(spin ? "loader" : "")"> @(spin ? "" : currentCount.ToString()) </div> 
</p> 

<button class="btn btn-primary" @onclick="@IncrementCount"> Click me </button> 
<button class="btn btn-dark" @onclick="@AsyncCallback"> API Callback </button> 

@code { 
    int currentCount = 0; 
    bool spin = false; 
    void IncrementCount () 
    { 
        currentCount++; 
    } 
    async Task AsyncCallback () 
    { 
        spin = true; 
        await Task.Run(() => APICallback());  //<==check this!!! 
        currentCount++; 
        spin = false; 
        await Task.CompletedTask; 
    } 

    void APICallback () => Task.Delay(1500).Wait(); 
}

Output:

View Sample in GitHub

Permalink

To detect keypress event in a div tag, use the @onkeydown event on the current element. In the created example there is a div element with keydown event, and on rendering, the focus to the div element is set by using the JS Interop.

[script.js]
window.SetFocusToElement = (element) => {
         element.focus();
};
[index.razor]
@inject IJSRuntime jsRuntime
@using Microsoft.AspNetCore.Components.Web

<div class="jumbotron"  @onkeydown="@KeyDown"  tabindex="0"  @ref="myDiv">
            <h1 class="display-4"> @KeyPressed </h1>
</div>
@code {
    string KeyPressed = "";
    protected ElementReference myDiv;  // set the @ref for attribute

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await jsRuntime.InvokeVoidAsync("SetFocusToElement", myDiv);
        }
    }
    protected void KeyDown(KeyboardEventArgs args)
    {
        KeyPressed = $"Key Pressed: [{args.Key}]";// get key pressed in the arguments
    }
}
Permalink

Blazor provides support for suppressing events of the HTML DOM element in two ways

  • Suppressing default event
  • Suppressing event propagation

Suppressing default event:                                                                                                                        

The default events of the HTML DOM element can be suppressed by using the @on{Event}:preventDefault directive attribute provided in Blazor. This is useful in scenarios like suppressing the on key press event of an input text box.

<input type="text" @onkeypress:preventDefault />

You can also add your own event listener as done for event handling.

<input type="text" @onkeypress="EventHandlerMethod" @onkeypress:preventDefault />

Supressing event propagation:

HTML DOM element’s events tend to propagate some events to their parent element and then its parent element, and so on. Blazor provides support to supress the event propagation using the @on{EVENT}:stopPropagation directive attribute. This takes a Boolean variable as the input. This is visualized as follows.

<button @onclick:stopPropagation="true">Click</button>

As the stopPropagation attribute which takes the value of a Boolean variable can be bound to a property, the propagation can be stopped based on the user requirement.

Permalink

You can call the preventDefault action in onclick event of an anchor tag. Refer to the following code sample to prevent opening a link when the link is clicked.

<a href="https://www.syncfusion.com/" target="_blank" @onclick:preventDefault>Syncfusion</a>

This feature was made available from .NET Core 3.1 Preview 2 for Blazor.

You can also perform custom action for onclick in the anchor tag by defining the same onclick event and mapping it to custom action.

<a href="https://www.syncfusion.com/" target="_blank" @onclick:preventDefault  @onclick="@OnClick">Syncfusion</a>
@code {
         public void OnClick(){
              //triggers on click
         }
}
Permalink

Blazor provides support for event handling of HTML element by referring a pointer to the function that is used to handle the event, For handling @onclick event of the HTML element, define a function/method in the @code section and then refer the delegate typed value to the @onclick attribute of the HTML element.

Refer to the following code sample.

<button class="btn btn-primary" @onclick="ButtonClick">Click</button>

@code {
         private void ButtonClick(MouseEventArgs args){
          Console.WriteLine(“ButtonClicked”)
         }
}

Here, the ButtonClick is the event handler function (delegate typed value) assigned to @onclick of the button (HTML element).

Permalink

In Blazor developers can handle any event by using the on<eventname> attribute with an HTML element. The attribute’s value is treated as an event handler.

Blazor provides a set of event argument types that map to events. Following is a list of event types and the event argument type they map to:

  • Focus events: FocusEventArgs
  • Mouse events: MouseEventArgs
  • Drag events: DragEventArgs
  • Keyboard events: KeyboardEventArgs
  • Input events: ChangeEventArgs/EventArgs
  • Clipboard events: ClipboardEventArgs/EventArgs
  • Touch events: TouchEventArgs
  • Pointer events: PointerEventArgs
  • Media events: EventArgs
  • Progress events: ProgressEventArgs

In the example, the event keypress is handled by using lambda expression the event is triggered for every keypress in the input element.

<h3>Example 2</h3>
<input type="text" @onkeypress="@(e => KeyPressed(e))" />
<p>Message: @pressedkeys</p>

@code {
string pressedkeys;
private void KeyPressed(KeyboardEventArgs args)
{
    pressedkeys = "You pressed key: " + args.Key;
}
}

Reference link: https://visualstudiomagazine.com/articles/2018/10/01/blazor-event-handling.aspx

Permalink

To pass values from a child to a parent component, see the following.

Parent component

[Parent.razor]
@page "/ParentComponent"

<h1>Parent Component</h1>

<ChildComponent @bind-Password="_password" />

@code {
    private string _password;
 }

Child component

[ChildComponent.razor]
<h1>Child Component</h1>

Password:

<input @oninput="OnPasswordChanged"
       required
       type="@(_showPassword ? "text" : "password")"
       value="@Password" />

<button class="btn btn-primary" @onclick="ToggleShowPassword">
    Show password
</button>
@code {
    private bool _showPassword;
    [Parameter]
    public string Password { get; set; }
    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        Password = e.Value.ToString();

        return PasswordChanged.InvokeAsync(Password);
    }
    private void ToggleShowPassword()
    {
        _showPassword = !_showPassword;
    }
}

Reference link:  https://docs.microsoft.com/en-us/aspnet/core/blazor/data-binding#child-to-parent-binding-with-chained-bind

Permalink

Blazor provides a set of event argument types that map to events. By using this event argument types we can get the event data for the specific event. For example, you need to use KeyboardEventArgs to get the event data for the keypress event in the input element for every key press.

 <input type="text" @onkeypress="@KeyPressed" />
  
 @code {
   private void KeyPressed(KeyboardEventArgs args)
   {
     Console.WriteLine(args.Key + " was pressed");
   }
 } 

Refer to this Visual Studio Magazine link for more events and event args.

Permalink

The value of an input element is updated in the wrapper with the change events of elements in Blazor. To get the current value for each character input, you must use the oninput event of the input element. This can be achieved by binding the oninput event (native event) using the @bind:event=oninput.

 @CurrentValue
 <input type="text" @bind="@CurrentValue" @oninput="@((e) => { CurrentValue=(string)e.Value;})" />
  
 @code {
     private string CurrentValue {get;set;} = "blazor";
 } 
Permalink

You can use a lambda expression to pass multiple arguments to an onclick event.

@for(var i=0; i<5; i++) {
     var index = i;
     <button @onclick="@(e => click(index, 5 * index))">Button @i</button>
 } 
@code {
     private void click(int a, int b) {
         Console.WriteLine(a + b);
     }
 }   
Permalink

You can bind events to DOM elements started with the on<event> with a delegate-type value. The attribute’s name always starts with “on.” In the following code example, the “UpdateHeading” event is triggered when the button is clicked.

<button class="btn   btn-primary" @onclick="@UpdateHeading">
       Update heading
   </button>
    
 @code {
       private void UpdateHeading(MouseEventArgs e)
       {
           ...
       }
   }  

Refer to this Blazor documentation for more information.

Permalink

Share with

Couldn't find the FAQs you're looking for?

Please submit your question and answer.