Skip to content

Utilities

CascadingValueProvider

Bit.BlazorUI

Replaces a stack of nested CascadingValue components with one flat list. Each value is cascaded by its type or by a name, a later value shadows an earlier one of the same type and name, and changing a value refreshes only the components that consume it.

Notes

Every value renders a real CascadingValue, so consumers match by type and name exactly as they do with a hand-written one, and the provider adds no markup of its own. For a value the whole app needs, prefer a root-level cascading value (AddCascadingValue on the service collection).

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

Each item of Values becomes a CascadingValue around the child content. A bare value is cascaded by its type and a (value, name) tuple by its name; the consumer reads them with plain [CascadingParameter] properties.


Child component with cascading parameters:

Theme: Light
Notifications: 2
Authenticated: True
User (named parameter): Saleh Xafan [CTO]
User (typed parameter): Yaser Moradi [CEO]

Live values

Build Values from state and every render pushes the current values down. Only the consumers of a value that actually changed re-render.







Changing cascading values:

Theme: Light
Notifications: 2
Authenticated: True
User (named parameter): Saleh Xafan [CTO]
User (typed parameter): Ava Smith [Product manager]

ValueList

A BitCascadingValueList captures the static type of each value through Add<T>, so a null or a nullable value is cascaded as the type it is declared with, and it can be revised in place with Set, Remove, Contains and Find. With both parameters set, ValueList is provided first and Values wins a conflict: defaults plus overrides.




ValueList defaults, Theme overridden by Values:

Theme: Dark
Notifications: null
Authenticated: null
User (named parameter): null
User (typed parameter): Ava Smith [Product manager]

Nesting

An inner provider overrides only the types and names it redefines; everything else keeps flowing down from the outer one.


Outer provider:

Theme: Light
Notifications: 7
Authenticated: null
User (named parameter): null
User (typed parameter): Ava Smith [Product manager]

Inner provider (only Theme is overridden):

Theme: Dark
Notifications: 7
Authenticated: null
User (named parameter): null
User (typed parameter): Ava Smith [Product manager]

ValueType

ValueType decides which cascading parameters a value reaches and defaults to the value's runtime type. Pass it explicitly with new(value, typeof(T)), or use BitCascadingValue.From<T>, to cascade a value as a nullable, base or interface type. Cascading null under the same type clears an outer value for a subtree.


Outer provider cascades 5 as int?: 5
Inner provider cascades null as the same int?: null

IsFixed & Enabled

IsFixed, or the Fixed<T> factory, marks a value that never changes, so its consumers skip change tracking. Enabled set to false drops a value as if it had never been added, so an outer value of the same type or name shows through again.



Fixed and conditional cascading values:

Theme: Dark
Notifications: 3
Authenticated: null
User (named parameter): null
User (typed parameter): Ava Smith [Product manager]

Change notifications

The provider listens to every BitCascadingValue it holds: assigning Value refreshes the consumers, NotifyChangedAsync(newValue) does the same and waits for the render, and NotifyChanged() or NotifyChangedAsync() pushes an object that was mutated in place. It works from any thread, with no StateHasChanged call on the page. A BitCascadingValue<T> types its Value and NotifyChangedAsync(newValue) as T, so a value of the wrong type does not even compile.




Self-refreshing cascading values:

Status: Idle
Progress: null

AutoNotify

Observed<T> turns AutoNotify on: an INotifyPropertyChanged or INotifyCollectionChanged value is watched and refreshes the consumers on each change, and the watch is dropped once no provider listens. The job below never calls NotifyChanged.




Self-watching cascading values:

Status: Idle
Count: 0

Lazy & computed values

Lazy<T> runs its factory once, the first time the value is provided, so a disabled or shadowed value is never built; IsValueCreated reports whether it has run. Computed<T> re-runs its factory on every render, so a list built once still tracks the state it reads. Click the button: Theme and Notifications follow it, the lazy factory count does not.




Lazy and computed cascading values:

Theme: Light
Notifications: 0
Authenticated: null
User (named parameter): null
User (typed parameter): Ava Smith [Product manager]

Lazy factory runs so far: 1

API

Every parameter, public member, sub-class and enum this component exposes.

BitCascadingValueProvider parameters

Name Type Default value Description
ChildContent RenderFragment? null The content to which the values should be provided.
Values IEnumerable<BitCascadingValue>? null The cascading values to be provided for the children. These values are provided after (so they take precedence over) the ones of the ValueList parameter.
ValueList BitCascadingValueList? null The cascading value list to be provided for the children. These values are provided before (so they can be overridden by) the ones of the Values parameter.

BitCascadingValue properties

One value to cascade: what is cascaded, as which type, under which name, and whether it is fixed or provided at all. Bare values and (value, name) tuples of the common primitive, date, string and BitDir types convert to it implicitly.

Name Type Default value Description
BitCascadingValue(object? value, string? name, bool isFixed, Type? valueType = null, bool enabled = true) constructor Creates a cascading value. Shorter overloads take (value, name), (value, isFixed), (value, valueType) and (value, name, valueType). The valueType is required when the value is null.
Value object? null The value to be provided. Assigning a value not assignable to ValueType throws an ArgumentException; assigning a different value raises Changed. A lazy factory runs on the first read, a computed one on every read; assigning replaces either factory for good, so a computed value stops tracking its source.
Name string? null The optional name of the cascading value, matched case-insensitively; an empty or white-space name means no name. Renaming a live value re-creates its CascadingValue so the consumers are matched again under the new name.
IsFixed bool false Marks a value that never changes, so its consumers are not subscribed for change notifications. Toggling it re-creates the underlying CascadingValue.
Enabled bool true Whether the value is provided at all. A disabled value is skipped as if it had never been added, so an outer or root-level value of the same type and name shows through.
AutoNotify bool false Watches the cascaded object itself, so an INotifyPropertyChanged or INotifyCollectionChanged value raises Changed on its own. The watch is only held while a provider is listening.
ValueType Type Value?.GetType() The TValue of the underlying CascadingValue, which decides the cascading parameters the value reaches. Read-only; defaults to the runtime type of the value, so pass it explicitly for null values, nullable value types, base types and interfaces.
IsValueCreated bool true Whether the value is already available. It is only false for a lazily created value whose factory has not run yet.
IsComputed bool false Whether the value is re-read from a factory on every read, as the Computed factories create it.
Changed event Action<BitCascadingValue>? Raised whenever the value changes; the hosting provider listens to it and re-renders on its own.
ChangedAsync event Func<BitCascadingValue, Task>? The awaitable counterpart of Changed, which the provider subscribes to so NotifyChangedAsync completes only once it has re-rendered.
NotifyChanged() void Raises Changed and ChangedAsync on demand, which pushes an object that was mutated in place down to the consumers.
NotifyChangedAsync() Task The awaitable form of NotifyChanged; completes once every listening provider has re-rendered, like CascadingValueSource.NotifyChangedAsync.
From<T>(T value, string? name = null, bool isFixed = false, bool enabled = true) BitCascadingValue Creates a cascading value whose ValueType is the static type of T.
Fixed<T>(T value, string? name = null, bool enabled = true) BitCascadingValue Creates a fixed (IsFixed) cascading value whose ValueType is the static type of T.
Lazy<T>(Func<T> valueFactory, string? name = null, bool isFixed = false, bool enabled = true) BitCascadingValue Creates a value whose factory runs once, the first time it is provided, so a disabled or shadowed value is never built. A failing factory is retried on the next read. An overload taking an explicit ValueType is available as well.
Computed<T>(Func<T> valueFactory, string? name = null, bool isFixed = false, bool enabled = true) BitCascadingValue Creates a value whose factory runs every time it is provided, so one long-lived value keeps tracking the state it is derived from. An overload taking an explicit ValueType is available as well.
Observed<T>(T value, string? name = null, bool enabled = true) BitCascadingValue Creates a value with AutoNotify turned on, so an object reporting its own mutations refreshes the consumers without any call to NotifyChanged.

BitCascadingValue<T> properties

A BitCascadingValue whose ValueType is T, so its value is read and assigned as a T, like the framework's CascadingValueSource<T>. It goes anywhere a BitCascadingValue does. Every value created from a static type is one - the factories, the typed BitCascadingValueList helpers and the implicit conversions - so a type pattern reaches its typed members; one created with an explicit Type or an untyped constructor is not.

Name Type Default value Description
BitCascadingValue<T>(T value, string? name = null, bool isFixed = false, bool enabled = true) constructor Creates a cascading value that is cascaded as T.
Value T The value to be provided, typed as T. Assigning a different value refreshes the consumers. It may be null even for a non-nullable reference T, since the untyped Value accepts null.
NotifyChangedAsync(T newValue) Task Assigns Value and completes once every listening provider has re-rendered with it, like CascadingValueSource.NotifyChangedAsync(newValue). Notifies even when the value is unchanged, and like assigning Value, replaces a lazy or computed factory for good.

BitCascadingValueList properties

A List<BitCascadingValue> with typed helpers for building and revising a set of cascading values; its collection initializer takes { value, name } pairs.

Name Type Default value Description
Add<T>(T value, string? name = null, bool isFixed = false, bool enabled = true) void Adds a typed BitCascadingValue to the list, cascading the value as the static type of T.
Add(BitCascadingValue? value) void Adds an already created BitCascadingValue to the list. A null item is ignored. An overload taking a BitCascadingValue<T> adds it as it is too, rather than cascading the value object itself.
Add(object? value, Type valueType, string? name = null, bool isFixed = false, bool enabled = true) void Adds a BitCascadingValue with an explicit ValueType to the list, for when the cascaded type is only known at runtime.
AddIf<T>(bool condition, T value, string? name = null, bool isFixed = false, bool enabled = true) void Adds a typed BitCascadingValue to the list only when the given condition is true.
AddIf(bool condition, BitCascadingValue? value) void Adds an already created BitCascadingValue only when the condition is true; paired with a lazy value, the value of a skipped entry is never built. An overload taking a BitCascadingValue<T> is available as well.
AddFixed<T>(T value, string? name = null, bool enabled = true) void Adds a fixed (IsFixed) typed BitCascadingValue to the list.
AddFixed(object? value, Type valueType, string? name = null, bool enabled = true) void Adds a fixed (IsFixed) BitCascadingValue with an explicit ValueType to the list.
AddLazy<T>(Func<T> valueFactory, string? name = null, bool isFixed = false, bool enabled = true) void Adds a lazy value, whose factory runs once, the first time it is provided. An overload taking an explicit ValueType is available as well.
AddComputed<T>(Func<T> valueFactory, string? name = null, bool isFixed = false, bool enabled = true) void Adds a computed value, whose factory runs every time it is provided. An overload taking an explicit ValueType is available as well.
AddObserved<T>(T value, string? name = null, bool enabled = true) void Adds a typed BitCascadingValue that watches the value itself, so an INotifyPropertyChanged or INotifyCollectionChanged object refreshes the consumers on its own.
Find<T>(string? name = null) BitCascadingValue? Finds the entry the type and name resolve to: the last one matching both, since it shadows the others. An entry created from a static type is a BitCascadingValue<T>, so match it with a type pattern rather than a cast. An overload taking an explicit ValueType is available as well.
Contains<T>(string? name = null) bool Whether the list holds an entry of the static type of T carrying the given name, regardless of whether it is enabled.
Remove<T>(string? name = null) bool Removes every entry of the static type of T carrying the given name, and reports whether anything was removed. An overload taking an explicit ValueType is available as well.
Set<T>(T value, string? name = null, bool isFixed = false, bool enabled = true) void Replaces every entry of the static type of T and the given name with one new entry, in the place of the first, or appends it when there is none.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.