Skip to content

ParameterState: Add ResolveEffectiveParameter, ParameterStateCollection for shared handlers#12347

Merged
ScarletKuro merged 36 commits intoMudBlazor:devfrom
ScarletKuro:param_util
Dec 24, 2025
Merged

ParameterState: Add ResolveEffectiveParameter, ParameterStateCollection for shared handlers#12347
ScarletKuro merged 36 commits intoMudBlazor:devfrom
ScarletKuro:param_util

Conversation

@ScarletKuro
Copy link
Member

@ScarletKuro ScarletKuro commented Dec 23, 2025

Continuation of #12269

In some components, MudBlazor components are a good example, there is a dependency between parameters such as Text and Value. Typically, Value represents a value of type T, while Text is its textual(string) representation.

Writing the "dominance" logic directly in the shared handler is externally complex. To simplify this, we introduce ResolveEffectiveParameter, which handles all the edge cases. This method returns an union EffectiveParameterResult.

Visual Summary Table (Dominant = Text)

NB! This table applies only when these parameter change in single render cycle (i.e. at once)

Text Change Value Change Effective Parameter Final Effective Color Reason
#fcefe5 -> #5fa9e2 #fcefe5 -> null Text #5fa9e2 Non-null wins
#fcefe5 -> #5fa9e2 #fcefe5 -> #5fa9e2 Text #5fa9e2 Both changed non-null → dominant wins
#fcefe5 -> null #fcefe5 -> #5fa9e2 Value #5fa9e2 Non-null wins
#fcefe5 -> null #fcefe5 -> null Text null Both null → dominant wins
#fcefe5 -> #5fa9e2 #fcefe5 -> #fcefe5 Text #5fa9e2 Only Text changed, non-null wins
#fcefe5 -> null #fcefe5 -> #fcefe5 Value #fcefe5 Text changed to null → pick non-null Value
#fcefe5 -> #fcefe5 #fcefe5 -> #5fa9e2 Value #5fa9e2 Only Value changed, non-null wins
#fcefe5 -> #fcefe5 #fcefe5 -> #fcefe5 None No change

We also need ParameterChangedContext, because we not only require ParameterView but also the previous value of a parameter, similar to ParameterChangedEventArgs.LastValue. However, we need a non-generic ParameterStateValue and a collection of these (ParameterStateCollection). This allows us to track the last values of parameters, which is necessary for shared change handlers to use ResolveEffectiveParameter.

Old way

private async Task OnTextAndValueChangedHandlerAsync(ParameterView parameterView)
{
	var hasText = parameterView.TryGetValue<string?>(nameof(Text), out var text);
	var hasValue = parameterView.TryGetValue<MudColor?>(nameof(Value), out var value);
	switch (hasText, hasValue, text, value)
	{
		case (true, true, not null, null):
			await _valueState.SetValueAsync(MudColor.Parse(text));
			return;

		case (true, true, null, not null):
			await _textState.SetValueAsync(value.ToString(MudColorOutputFormats.Hex));
			return;

		case (true, true, not null, not null):
			await _textState.SetValueAsync(value.ToString(MudColorOutputFormats.Hex));
			return;

		case (false, true, _, not null):
			await _textState.SetValueAsync(value.ToString(MudColorOutputFormats.Hex));
			return;

		case (true, false, not null, _):
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			await _valueState.SetValueAsync(MudColor.Parse(text));
			return;

		default:
			await _textState.SetValueAsync(value?.ToString(MudColorOutputFormats.Hex));
			await _valueState.SetValueAsync(value);
			return;
	}
}

New way

private async Task OnTextAndValueChangedHandlerAsync(ParameterChangedContext context)
{
	var effectiveParameter = context.ResolveEffectiveParameter(_valueState, _textState, nameof(Value));
	// Value
	if (effectiveParameter is { IsParameter1: true })
	{
		await _textState.SetValueAsync(effectiveParameter.Parameter1Value.ToString(MudColorOutputFormats.Hex));
	}
	// Text
	if (effectiveParameter is { IsParameter2: true })
	{
		await _valueState.SetValueAsync(MudColor.Parse(effectiveParameter.Parameter2Value));
	}
}

Checklist:

  • I've read the contribution guidelines
  • My code follows the style of this project
  • I've added or updated relevant unit tests

Copilot AI and others added 7 commits December 23, 2025 18:35
- Created ParameterStateValue struct to hold parameter name, value, and last value
- Created ParameterStateCollection for efficient lookup of parameter state values
- Created ParameterChangedContext combining ParameterView and ParameterStateCollection
- Updated all handler interfaces and implementations to accept ParameterChangedContext
- Optimized collection of parameter state values in CollectChangedHandlers
- Updated test components and unit tests to use new API
- Breaking change: WithChangeHandler signatures now use ParameterChangedContext instead of separate ParameterView parameter

Co-authored-by: ScarletKuro <[email protected]>
…okup

- Changed from array with O(n) linear search to FrozenDictionary with O(1) lookup
- Updated constructor to accept IReadOnlyDictionary instead of array
- Modified enumerator to work with dictionary
- Updated CreateHandlerCollection to build FrozenDictionary from parameter state values
- Maintains same public API while improving lookup performance

Co-authored-by: ScarletKuro <[email protected]>
@mudbot mudbot bot added breaking change This change will require consumer code updates enhancement Adds a new feature or enhances existing functionality (not fixing a defect) in the main library labels Dec 23, 2025
This was referenced Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change This change will require consumer code updates enhancement Adds a new feature or enhances existing functionality (not fixing a defect) in the main library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants