| File: Columns\PropertyColumn.cs | Web Access |
| Project: src\aspnetcore\src\Components\QuickGrid\Microsoft.AspNetCore.Components.QuickGrid\src\Microsoft.AspNetCore.Components.QuickGrid.csproj (Microsoft.AspNetCore.Components.QuickGrid) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Linq.Expressions; using Microsoft.AspNetCore.Components.Rendering; namespace Microsoft.AspNetCore.Components.QuickGrid; /// <summary> /// Represents a <see cref="QuickGrid{TGridItem}"/> column whose cells display a single value. /// </summary> /// <typeparam name="TGridItem">The type of data represented by each row in the grid.</typeparam> /// <typeparam name="TProp">The type of the value being displayed in the column's cells.</typeparam> public class PropertyColumn<TGridItem, TProp> : ColumnBase<TGridItem> { private Expression<Func<TGridItem, TProp>>? _lastAssignedProperty; private Func<TGridItem, string?>? _cellTextFunc; private GridSort<TGridItem>? _sortBuilder; /// <summary> /// Defines the value to be displayed in this column's cells. /// </summary> [Parameter, EditorRequired] public Expression<Func<TGridItem, TProp>> Property { get; set; } = default!; /// <summary> /// Optionally specifies a format string for the value. /// /// Using this requires the <typeparamref name="TProp"/> type to implement <see cref="IFormattable" />. /// </summary> [Parameter] public string? Format { get; set; } /// <inheritdoc/> public override GridSort<TGridItem>? SortBy { get => _sortBuilder; set => throw new NotSupportedException($"PropertyColumn generates this member internally. For custom sorting rules, see '{typeof(TemplateColumn<TGridItem>)}'."); } /// <inheritdoc /> protected override void OnParametersSet() { // We have to do a bit of pre-processing on the lambda expression. Only do that if it's new or changed. if (_lastAssignedProperty != Property) { _lastAssignedProperty = Property; var compiledPropertyExpression = Property.Compile(); if (!string.IsNullOrEmpty(Format)) { // TODO: Consider using reflection to avoid having to box every value just to call IFormattable.ToString // For example, define a method "string Format<U>(Func<TGridItem, U> property) where U: IFormattable", and // then construct the closed type here with U=TProp when we know TProp implements IFormattable // If the type is nullable, we're interested in formatting the underlying type var nullableUnderlyingTypeOrNull = Nullable.GetUnderlyingType(typeof(TProp)); if (!typeof(IFormattable).IsAssignableFrom(nullableUnderlyingTypeOrNull ?? typeof(TProp))) { throw new InvalidOperationException($"A '{nameof(Format)}' parameter was supplied, but the type '{typeof(TProp)}' does not implement '{typeof(IFormattable)}'."); } _cellTextFunc = item => ((IFormattable?)compiledPropertyExpression!(item))?.ToString(Format, null); } else { _cellTextFunc = item => compiledPropertyExpression!(item)?.ToString(); } _sortBuilder = GridSort<TGridItem>.ByAscending(Property); } if (Title is null && Property.Body is MemberExpression memberExpression) { Title = memberExpression.Member.Name; } } /// <inheritdoc /> protected internal override void CellContent(RenderTreeBuilder builder, TGridItem item) => builder.AddContent(0, _cellTextFunc!(item)); }