Code/CascadingValue.razor.cs
using System;
using BetterUI.Extensions;
using Sandbox.Diagnostics;
namespace BetterUI;
/// <summary>
/// A panel that cascades a value to its children.
/// </summary>
/// <remarks>
/// Children that have a property with the same name as the Name property,
/// and the same type as the Value property, will have that property set to the Value.
/// </remarks>
public partial class CascadingValue : Panel
{
/// <summary>
/// The name of the property to cascade.
/// </summary>
/// <remarks>
/// If empty, the name of the property will be the same as the name of the property
/// on the child panel.
/// </remarks>
public string Name { get; set; } = null!;
/// <summary>
/// The value to cascade.
/// </summary>
public object Value { get; set; } = null!;
protected override void OnAfterTreeRender( bool firstTime )
{
EnsurePropertiesUpdated();
}
/// <summary>
/// Ensures that all properties on descendant panels are updated with the cascading value.
/// </summary>
private void EnsurePropertiesUpdated()
{
Assert.NotNull(Value, "CascadingValue must have a value");
IterateChildren();
}
/// <summary>
/// Iterates over all descendant panels and attempts to set the cascading value to their properties.
/// </summary>
private void IterateChildren()
{
foreach (var panel in Descendants)
TrySetValueToPanelProperties(panel);
}
/// <summary>
/// Tries to set the cascading value to the properties of a given panel if they match the specified name and type.
/// </summary>
/// <param name="panel">The panel whose properties are to be set.</param>
private void TrySetValueToPanelProperties(Panel panel)
{
var type = TypeLibrary.GetType(panel.GetType());
var properties = type.Properties;
foreach (var property in properties)
{
var valueType = Value.GetType();
var name = string.IsNullOrEmpty(Name) ? property.Name : Name;
if (!property.IsCascadingProperty(name, valueType))
continue;
property.SetValue(panel, Value);
panel.StateHasChanged();
}
}
protected override int BuildHash() => HashCode.Combine( Name, Value );
}