Bind Value Format

Posted on  by admin
-->

This article explains data binding features for Razor components and Document Object Model (DOM) elements in Blazor apps.

With Blazor the date doesn't format. With it `does.

See "InputDate" > https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-3.0.

  • .NET Core SDK (reflecting any global.json):Version: 3.1.100-preview1-014459Commit: ac3b59712d.
  • Runtime Environment:OS Name: WindowsOS Version: 10.0.18362OS Platform: WindowsRID: win10-x64Base Path: C:\Program Files\dotnet\sdk\3.1.100-preview1-014459\.

Host (useful for support):Version: 3.1.0-preview1.19506.1Commit: bbf5542781. .NET Core SDKs installed:2.1.802 [C:\Program Files\dotnet\sdk]2.2.402 [C:\Program Files\dotnet\sdk]3.0.100 [C:\Program Files\dotnet\sdk]3.1.100-preview1-014459 [C:\Program Files\dotnet\sdk].

To install additional .NET Core runtimes or SDKs:https://aka.ms/dotnet-download.

Connect to a data source. For more information, see Connecting to a Data Source. In Visual Studio, select the control on the form, and then open the Properties window. Expand the (DataBindings) property, and then in the (Advanced) box, click the ellipsis button () to display the Formatting and Advanced Binding dialog box, which has a complete list of properties for that control.

Select the property you want to bind, and then select the Binding arrow. A list of available data sources is displayed. Expand the data source you want to bind to until you find the single data element you want. For example, if you are binding to a column value in a dataset's table, expand the name of the dataset, and then expand the table name to display column names.

Select the name of an element to bind to.

In the Format type box, select the format you want to apply to the data displayed in the control. In every case, you can specify the value displayed in the control if the data source contains DBNull. Otherwise, the options vary slightly, depending on the format type you choose. The following table shows the format types and options. Select OK to close the Formatting and Advanced Binding dialog box and return to the Properties window. Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

Occurs when the property of a control is bound to a data value. The following code example creates a Binding, adds a ConvertEventHandler delegate to both the Parse and Format events, and adds the Binding to the BindingsCollection of a TextBox control through the DataBindings property. The DecimalToCurrencyString event delegate, added to the Format event, formats the bound value (a Decimal type) as currency using the ToString method. The CurrencyStringToDecimal event delegate, added to the Parse event, converts the value displayed by the control back to the Decimal type.

This example assumes the presence of a DataSet named ds.

The Format event is raised when data is pushed from the data source into the control.

  • You can handle the Format event to convert unformatted data from the data source into formatted data for display.
  • When data is pulled from the control into the data source, the Parse event is raised to unformat the displayed value, then the Format event occurs to reformat the data for display. This ensures that the bound control displays correctly formatted data regardless of whether the user enters formatted or unformatted data in the control.

Getting started

The Format and Parse events allow you to create custom formats for displaying data. For example, if the data in a table is of type Decimal, you can display the data in the local currency format by setting the Value property of the ConvertEventArgs to the formatted value in the Format event.

You must consequently unformat the displayed value in the Parse event.

The Format event occurs whenever the Current value of the BindingManagerBase changes, which includes:. The first time the property is bound. Any time the Position changes. Whenever the data-bound list is sorted or filtered, which is accomplished when a DataView supplies the list.

Remarks

The Format event also occurs after the Parse event. For example, when a control loses focus, its contents are parsed. Immediately afterward, as new data is pushed into the control, the Format event occurs allowing the new contents to be formatted.

  • For more information about handling events, see Handling and Raising Events. We covered Directives earler – if you are unfamiliar with directives, please read the section about them before you continue.
  • We previously covered Directives, and Directive Attributes. In this section we will cover assigning values to Directive Attributes by demonstrating how they are utilised when using two-way binding.

As a quick recap, a Directive is an identifier within an element that starts with an @ symbol.

  • A Directive Attribute is additional information provided to the directive in the form @directive:attribute.
  • For example, the preventDefault attribute applied to the @onclick directive would prevent a submit button from actually submitting a form.

In addition to these, it is also possible to assign values to some directive attributes in the following form:. Although there is no reason why these attribute values should be constrained to two-way binding in particular, it just so happens that the only place currently in the Blazor framework that utilises this ability happens to be two-way binding, which is why this subject is covered beneath the two-way binding section.

Examples

Note: Although we will use the HTML element here for simplicity, for a richer user experience (adding validation etc) I recommend using the Blazor components (InputDate, etc) within an component.

These are covered in the section on Forms. First we need a page with the following members defined in the @code section, so we have something to bind to:.

First we’ll start off with a standard two-way binding to the Name member of our Blazor page.

The important part of the preceding mark-up is @bind-value=Name. This sets up two-way binding for the HTML attribute named value on the element, and binds it to the Name member. If we run our app now, we will see that the Name = @Name text above the input does not change to reflect what we type in the until either after the input element loses focus, or we press the Enter key.

The @bind directive has a directive attribute named event. Setting the value for this directive form takes the following format:. Valid values for “x” are either onchange or oninput. onchange is the assumed default when no value for :event is specified. It is the behaviour we saw when we ran our sample – the binding only occurs when the control loses focus or when the user presses the enter key. oninput is the only other possible value for :event, and instructs Blazor to hook into the JavaScript oninput event of the HTML element and update the bound member every time the event is fired.

  • This results in the bound member being updated immediately every time the user changes the value in the input. Note: The -value is the name of the HTML attribute or Blazor component property to bind to.
  • For HTML elements the leading letter will be lowercase, for component properties the leading letter will be uppercase, the name of the directive and the name of the binding target are separated by the - symbol.
  • Add the following mark-up to our page and run the application. @bind-value:event="oninput" is the key to instructing Blazor to use immediate change detection. First we tell Blazor we want to bind the value HTML attribute of input to our Name member (@bind-value=Name), and then we tell Blazor to hook into the oninput event of the HTML element so our binding occurs immediately every time the value of the element changes (@bind-value:event="oninput").

See also

Although the date is displayed in the

This is one of the reasons I recommend use of the Blazor components inside an EditForm when editing data, because this gives us the ability to use components such as .

As mentioned in the section Descending from InputBase, the Blazor input components have a pair of complementing protected methods for working with translating the bound value to and from strings.

The @bind directive doesn’t add code to bind directly to our member and simply convert it to/from string values.

Instead, it redirects presentation of the current value and parsing of input values through a BindConverter. If we look in the .cs file Blazor generates for a one-way binding such as [email protected] we’ll see C# that looks something like this (edited for brevity). Now if we look at the generated file for two-way binding we’ll see something similar to the following (abridged) code for displaying the value:.

Applies to

And something similar to the following (again abridged) code for converting user input back into the bound member. The code hooks into the HTML onchange event and then, via the binder, sets our member value when the event is triggered. The different when setting the @bind-value:format directive attribute value is that the format we provide passed in the generated code to both BindConverter.Format and EventCallback.Factory.CreateBinder.

People of the world have different customs and cultures, this is one of the things that makes the world such an interesting place.

Screenshot

Unfortunately, this is also one of the things that makes writing software more difficult. Add the following mark-up to our page:. And ensure the following member is added to the page in the @code section:.

Entering the value 12.42 one might expect to have a balance of just over 12 Turkish Lira but, as we can see, we have just accidentally given someone 1,242 Turkish Lira.

Of course, someone living in Turkey would have known to type in 12,42 instead – but this highlights the need to specify cultures correctly when our application is intended for use in other countries.

As with the format directive attribute, the @bind-value:culture specified will be passed as a named (optional) value to both the Binder and BindConverter.

If you have not heard of The Turkey test, then I recommend reading this excellent article. Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community. By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails. Already on GitHub? Sign in to your account . Modified4 months ago.

Is is possible to bind a value in Blazor client in particular format. but that didnt do anything, i received a value such as 11/12/2019 1:03:17 PM.

1212 gold badges5757 silver badges105105 bronze badges.

105105 bronze badges. It's bind:format, you have sample in the doc : Data binding paraph format string.

44 gold badges5454 silver badges6363 bronze badges.

6363 bronze badges.

By convention, a property can be bound to a corresponding event handler by including an @bind-{PROPERTY}:event attribute assigned to the handler, where the {PROPERTY} placeholder is the property. is equivalent to writing:

In a more sophisticated and real-world example, the following PasswordEntry component:

  • Sets an element's value to a password field.
  • Exposes changes of a Password property to a parent component with an EventCallback that passes in the current value of the child's password field as its argument.
  • Uses the onclick event to trigger the ToggleShowPassword method. For more information, see ASP.NET Core Blazor event handling.

Shared/PasswordEntry.razor:

The PasswordEntry component is used in another component, such as the following PasswordBinding component example.

Pages/PasswordBinding.razor:

When the PasswordBinding component is initially rendered, the password value of Not set is displayed in the UI. After initial rendering, the value of password reflects changes made to the Password component parameter value in the PasswordEntry component.

Note

The preceding example binds the password one-way from the child PasswordEntry component to the parent PasswordBinding component. Two-way binding isn't a requirement in this scenario if the goal is for the app to have a shared password entry component for reuse around the app that merely passes the password to the parent. For an approach that permits two-way binding without writing directly to the child component's parameter, see the NestedChild component example in the Bind across more than two components section of this article.

Perform checks or trap errors in the handler. The following revised PasswordEntry component provides immediate feedback to the user if a space is used in the password's value.

Shared/PasswordEntry.razor:

Specifying a custom culture

You can bind parameters through any number of nested components, but you must respect the one-way flow of data:

  • Change notifications flow up the hierarchy.
  • New parameter values flow down the hierarchy.

A common and recommended approach is to only store the underlying data in the parent component to avoid any confusion about what state must be updated, as shown in the following example.

Pages/Parent.razor:

Shared/NestedChild.razor:

Warning

Generally, avoid creating components that write directly to their own component parameters. The preceding NestedChild component makes use of a BoundValue property instead of writing directly to its ChildMessage parameter. For more information, see ASP.NET Core Razor components.

Shared/NestedGrandchild.razor:

For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see ASP.NET Core Blazor state management.

Additional context

Razor components provide data binding features with the @bind Razor directive attribute with a field, property, or Razor expression value.

The following example binds:

  • An element value to the C# inputValue field.
  • A second element value to the C# InputValue property.

When an element loses focus, its bound field or property is updated.

Pages/Bind.razor:

The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered.

As a demonstration of how data binding composes in HTML, the following example binds the InputValue property to the second element's value and onchange attributes. The second element in the following example is a concept demonstration and isn't meant to suggest how you should bind data in Razor components.

Pages/BindTheory.razor:

When the BindTheory component is rendered, the value of the HTML demonstration element comes from the InputValue property. When the user enters a value in the text box and changes element focus, the onchange event is fired and the InputValue property is set to the changed value. In reality, code execution is more complex because @bind handles cases where type conversions are performed. In general, @bind associates the current value of an expression with a value attribute and handles changes using the registered handler.

Bind a property or field on other Document Object Model (DOM) events by including an @bind:event="{EVENT}" attribute with a DOM event for the {EVENT} placeholder. The following example binds the InputValue property to the element's value when the element's oninput event is triggered. Unlike the onchange event, which fires when the element loses focus, oninput fires when the value of the text box changes.

Page/BindEvent.razor:

Razor attribute binding is case sensitive:

  • @bind and @bind:event are valid.
  • @Bind/@Bind:Event (capital letters B and E) or @BIND/@BIND:EVENT (all capital letters) are invalid.

Standard two-way binding

There's no sensible way to represent a 's value.

Immediate change detection using directive attributes

When a user provides an unparsable value to a databound element, the unparsable value is automatically reverted to its previous value when the bind event is triggered.

Consider the following component, where an element is bound to an int type with an initial value of 123.

Pages/UnparsableValues.razor:

By default, binding applies to the element's onchange event. If the user updates the value of the text box's entry to 123.45 and changes the focus, the element's value is reverted to 123 when onchange fires. When the value 123.45 is rejected in favor of the original value of 123, the user understands that their value wasn't accepted.

For the oninput event (@bind:event="oninput"), a value reversion occurs after any keystroke that introduces an unparsable value. When targeting the oninput event with an int-bound type, a user is prevented from typing a dot (.) character. A dot (.) character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. There are scenarios where reverting the value on the oninput event isn't ideal, such as when the user should be allowed to clear an unparsable value. Alternatives include:

  • Don't use the oninput event. Use the default onchange event, where an invalid value isn't reverted until the element loses focus.
  • Bind to a nullable type, such as int? or string and provide custom get and set accessor logic to handle invalid entries.
  • Use a form validation component, such as InputNumber or InputDate. Form validation components provide built-in support to manage invalid inputs. Form validation components:
    • Permit the user to provide invalid input and receive validation errors on the associated EditContext.
    • Display validation errors in the UI without interfering with the user entering additional webform data.

Specifying a custom binding format

Data binding works with a single DateTime format string using @bind:format="{FORMAT STRING}", where the {FORMAT STRING} placeholder is the format string. Other format expressions, such as currency or number formats, aren't available at this time but might be added in a future release.

Pages/DateBinding.razor:

In the preceding code, the element's field type (type attribute) defaults to text.

Nullable System.DateTime and System.DateTimeOffset are supported:

Specifying a format for the date field type isn't recommended because Blazor has built-in support to format dates. In spite of the recommendation, only use the yyyy-MM-dd date format for binding to function correctly if a format is supplied with the date field type:

Custom binding formats

C# get and set accessors can be used to create custom binding format behavior, as the following DecimalBinding component demonstrates. The component binds a positive or negative decimal with up to three decimal places to an element by way of a string property (DecimalValue).

Pages/DecimalBinding.razor:

Binding with component parameters

A common scenario is binding a property of a child component to a property in its parent component. This scenario is called a chained bind because multiple levels of binding occur simultaneously.

Component parameters permit binding properties of a parent component with @bind-{PROPERTY} syntax, where the {PROPERTY} placeholder is the property to bind.

You can't implement chained binds with @bind syntax in the child component. An event handler and value must be specified separately to support updating the property in the parent from the child component.

The parent component still leverages the @bind syntax to set up the databinding with the child component.

The following ChildBind component has a Year component parameter and an EventCallback. By convention, the EventCallback for the parameter must be named as the component parameter name with a "Changed" suffix. The naming syntax is {PARAMETER NAME}Changed, where the {PARAMETER NAME} placeholder is the parameter name. In the following example, the EventCallback is named YearChanged.

EventCallback.InvokeAsync invokes the delegate associated with the binding with the provided argument and dispatches an event notification for the changed property.

Shared/ChildBind.razor:

For more information on events and EventCallback, see the EventCallback section of the ASP.NET Core Blazor event handling article.

In the following Parent component, the year field is bound to the Year parameter of the child component. The Year parameter is bindable because it has a companion YearChanged event that matches the type of the Year parameter.

Pages/Parent.razor:

By convention, a property can be bound to a corresponding event handler by including an @bind-{PROPERTY}:event attribute assigned to the handler, where the {PROPERTY} placeholder is the property. is equivalent to writing:

In a more sophisticated and real-world example, the following PasswordEntry component:

  • Sets an element's value to a password field.
  • Exposes changes of a Password property to a parent component with an EventCallback that passes in the current value of the child's password field as its argument.
  • Uses the onclick event to trigger the ToggleShowPassword method. For more information, see ASP.NET Core Blazor event handling.

Shared/PasswordEntry.razor:

The PasswordEntry component is used in another component, such as the following PasswordBinding component example.

Pages/PasswordBinding.razor:

When the PasswordBinding component is initially rendered, the password value of Not set is displayed in the UI. After initial rendering, the value of password reflects changes made to the Password component parameter value in the PasswordEntry component.

Note

The preceding example binds the password one-way from the child PasswordEntry component to the parent PasswordBinding component. Two-way binding isn't a requirement in this scenario if the goal is for the app to have a shared password entry component for reuse around the app that merely passes the password to the parent. For an approach that permits two-way binding without writing directly to the child component's parameter, see the NestedChild component example in the Bind across more than two components section of this article.

Perform checks or trap errors in the handler. The following revised PasswordEntry component provides immediate feedback to the user if a space is used in the password's value.

Shared/PasswordEntry.razor:

Bind across more than two components

You can bind parameters through any number of nested components, but you must respect the one-way flow of data:

  • Change notifications flow up the hierarchy.
  • New parameter values flow down the hierarchy.

A common and recommended approach is to only store the underlying data in the parent component to avoid any confusion about what state must be updated, as shown in the following example.

Pages/Parent.razor:

Shared/NestedChild.razor:

Warning

Generally, avoid creating components that write directly to their own component parameters. The preceding NestedChild component makes use of a BoundValue property instead of writing directly to its ChildMessage parameter. For more information, see ASP.NET Core Razor components.

Shared/NestedGrandchild.razor:

For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see ASP.NET Core Blazor state management.

Additional resources

Razor components provide data binding features with the @bind Razor directive attribute with a field, property, or Razor expression value.

The following example binds:

  • An element value to the C# inputValue field.
  • A second element value to the C# InputValue property.

When an element loses focus, its bound field or property is updated.

Pages/Bind.razor:

The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered.

As a demonstration of how data binding composes in HTML, the following example binds the InputValue property to the second element's value and onchange attributes. The second element in the following example is a concept demonstration and isn't meant to suggest how you should bind data in Razor components.

Pages/BindTheory.razor:

When the BindTheory component is rendered, the value of the HTML demonstration element comes from the InputValue property. When the user enters a value in the text box and changes element focus, the onchange event is fired and the InputValue property is set to the changed value. In reality, code execution is more complex because @bind handles cases where type conversions are performed. In general, @bind associates the current value of an expression with a value attribute and handles changes using the registered handler.

Bind a property or field on other Document Object Model (DOM) events by including an @bind:event="{EVENT}" attribute with a DOM event for the {EVENT} placeholder. The following example binds the InputValue property to the element's value when the element's oninput event is triggered. Unlike the onchange event, which fires when the element loses focus, oninput fires when the value of the text box changes.

Page/BindEvent.razor:

Razor attribute binding is case sensitive:

  • @bind and @bind:event are valid.
  • @Bind/@Bind:Event (capital letters B and E) or @BIND/@BIND:EVENT (all capital letters) are invalid.

Binding element option value as a C# object null value, because:

  • HTML attributes can't have null values. The closest equivalent to null in HTML is absence of the HTML value attribute from the element.
  • When selecting an with no value attribute, the browser treats the value as the text content of that 's element.

The Blazor framework doesn't attempt to suppress the default behavior because it would involve:

  • Creating a chain of special-case workarounds in the framework.
  • Breaking changes to current framework behavior.

The Blazor framework doesn't automatically handle null to empty string conversions when attempting two-way binding to a to a null value (dotnet/aspnetcore #23221).

Unparsable values

When a user provides an unparsable value to a databound element, the unparsable value is automatically reverted to its previous value when the bind event is triggered.

Consider the following component, where an element is bound to an int type with an initial value of 123.

Pages/UnparsableValues.razor:

By default, binding applies to the element's onchange event. If the user updates the value of the text box's entry to 123.45 and changes the focus, the element's value is reverted to 123 when onchange fires. When the value 123.45 is rejected in favor of the original value of 123, the user understands that their value wasn't accepted.

For the oninput event (@bind:event="oninput"), a value reversion occurs after any keystroke that introduces an unparsable value. When targeting the oninput event with an int-bound type, a user is prevented from typing a dot (.) character. A dot (.) character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. There are scenarios where reverting the value on the oninput event isn't ideal, such as when the user should be allowed to clear an unparsable value. Alternatives include:

  • Don't use the oninput event. Use the default onchange event, where an invalid value isn't reverted until the element loses focus.
  • Bind to a nullable type, such as int? or string and provide custom get and set accessor logic to handle invalid entries.
  • Use a form validation component, such as InputNumber or InputDate. Form validation components provide built-in support to manage invalid inputs. Form validation components:
    • Permit the user to provide invalid input and receive validation errors on the associated EditContext.
    • Display validation errors in the UI without interfering with the user entering additional webform data.

Format strings

Data binding works with a single DateTime format string using @bind:format="{FORMAT STRING}", where the {FORMAT STRING} placeholder is the format string. Other format expressions, such as currency or number formats, aren't available at this time but might be added in a future release.

Pages/DateBinding.razor:

In the preceding code, the element's field type (type attribute) defaults to text.

Nullable System.DateTime and System.DateTimeOffset are supported:

Specifying a format for the date field type isn't recommended because Blazor has built-in support to format dates. In spite of the recommendation, only use the yyyy-MM-dd date format for binding to function correctly if a format is supplied with the date field type:

Custom binding formats

C# get and set accessors can be used to create custom binding format behavior, as the following DecimalBinding component demonstrates. The component binds a positive or negative decimal with up to three decimal places to an element by way of a string property (DecimalValue).

Pages/DecimalBinding.razor:

Binding with component parameters

A common scenario is binding a property of a child component to a property in its parent component. This scenario is called a chained bind because multiple levels of binding occur simultaneously.

Component parameters permit binding properties of a parent component with @bind-{PROPERTY} syntax, where the {PROPERTY} placeholder is the property to bind.

You can't implement chained binds with @bind syntax in the child component. An event handler and value must be specified separately to support updating the property in the parent from the child component.

The parent component still leverages the @bind syntax to set up the databinding with the child component.

The following ChildBind component has a Year component parameter and an EventCallback. By convention, the EventCallback for the parameter must be named as the component parameter name with a "Changed" suffix. The naming syntax is {PARAMETER NAME}Changed, where the {PARAMETER NAME} placeholder is the parameter name. In the following example, the EventCallback is named YearChanged.

EventCallback.InvokeAsync invokes the delegate associated with the binding with the provided argument and dispatches an event notification for the changed property.

Shared/ChildBind.razor:

For more information on events and EventCallback, see the EventCallback section of the ASP.NET Core Blazor event handling article.

In the following Parent component, the year field is bound to the Year parameter of the child component. The Year parameter is bindable because it has a companion YearChanged event that matches the type of the Year parameter.

Pages/Parent.razor:

By convention, a property can be bound to a corresponding event handler by including an @bind-{PROPERTY}:event attribute assigned to the handler, where the {PROPERTY} placeholder is the property. is equivalent to writing:

In a more sophisticated and real-world example, the following PasswordEntry component:

  • Sets an element's value to a password field.
  • Exposes changes of a Password property to a parent component with an EventCallback that passes in the current value of the child's password field as its argument.
  • Uses the onclick event to trigger the ToggleShowPassword method. For more information, see ASP.NET Core Blazor event handling.

Shared/PasswordEntry.razor:

The PasswordEntry component is used in another component, such as the following PasswordBinding component example.

Pages/PasswordBinding.razor:

When the PasswordBinding component is initially rendered, the password value of Not set is displayed in the UI. After initial rendering, the value of password reflects changes made to the Password component parameter value in the PasswordEntry component.

Note

The preceding example binds the password one-way from the child PasswordEntry component to the parent PasswordBinding component. Two-way binding isn't a requirement in this scenario if the goal is for the app to have a shared password entry component for reuse around the app that merely passes the password to the parent. For an approach that permits two-way binding without writing directly to the child component's parameter, see the NestedChild component example in the Bind across more than two components section of this article.

Perform checks or trap errors in the handler. The following revised PasswordEntry component provides immediate feedback to the user if a space is used in the password's value.

Shared/PasswordEntry.razor:

Bind across more than two components

You can bind parameters through any number of nested components, but you must respect the one-way flow of data:

  • Change notifications flow up the hierarchy.
  • New parameter values flow down the hierarchy.

A common and recommended approach is to only store the underlying data in the parent component to avoid any confusion about what state must be updated, as shown in the following example.

Pages/Parent.razor:

Shared/NestedChild.razor:

Warning

Generally, avoid creating components that write directly to their own component parameters. The preceding NestedChild component makes use of a BoundValue property instead of writing directly to its ChildMessage parameter. For more information, see ASP.NET Core Razor components.

Shared/NestedGrandchild.razor:

For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see ASP.NET Core Blazor state management.

Additional resources