
De igual forma como vimos en el artículo pasado tuve la necesidad de crear un filtro booleano para el MudDataGrid, pues de la misma forma necesité un filtroque se comportara igual que los filtros de Excel: con una lista de valores únicos con checkboxes, para que el usuario pudiera filtrar por uno o varios elementos.
MudBlazor no trae esto de forma nativa, así que desarrollé mi propio componente Razor llamado ExcelLikeFilterColumn.
En este artículo te explico cómo funciona y cómo integrarlo fácilmente en tu proyecto.
¿Por qué hacer un filtro tipo Excel?
Cuando presentas datos tabulares ―por ejemplo, catálogos, direcciones, marcas, categorías, usuarios― los filtros tipo Excel son una bendición:
- Muestran los valores distintos de la columna.
- Permiten seleccionar varios valores a la vez.
- Son intuitivos para cualquier usuario.
- Se despliegan dentro de un popover con scroll.
- Se integran perfectamente con MudDataGrid.
Así que decidí replicar ese comportamiento con MudBlazor.
¿Cómo funciona internamente?
Mi componente:
- Recibe los items del grid mediante
Items="context.Items". - Usa generics para el tipo de item y el tipo de valor (
TItem,TValue). - Obtiene los valores únicos de la columna usando
ValueSelector. - Construye un popover con un checkbox de “Todos” y un checkbox para cada valor distinto.
- Permite personalizar la etiqueta a mostrar mediante
LabelSelector. - Aplica el filtro usando
FilterDefinition<TItem>tal como lo hace MudDataGrid internamente.
La experiencia final es prácticamente idéntica a un filtro de Excel.
@typeparam TItem
@typeparam TValue
<MudIconButton OnClick="@(() => _open = true)" Icon="@_icon" Size="Size.Small" />
<MudOverlay Visible="@_open" OnClick="@(() => _open = false)" />
<MudPopover Open="@_open" AnchorOrigin="Origin.BottomCenter" TransformOrigin="Origin.TopCenter" Class="px-4 py-2">
<MudStack Spacing="0">
<MudCheckBox T="bool" Label="Todos" Size="Size.Small"
Value="@_selectAll"
ValueChanged="@SelectAllChanged" />
<MudStack Spacing="0" Style="overflow-y:auto;max-height:250px">
@foreach (var val in _distinctValues)
{
<MudCheckBox T="bool" Label="@GetLabel(val)"
Value="@_selectedValues.Contains(val)"
ValueChanged="@(v => ValueChanged(v, val))"
Size="Size.Small" />
}
</MudStack>
<MudStack Row="true">
<MudButton OnClick="Clear">Limpiar</MudButton>
<MudSpacer/>
<MudButton Color="Color.Primary" OnClick="Apply">Aplicar</MudButton>
</MudStack>
</MudStack>
</MudPopover>
@code {
[Parameter] public IEnumerable<TItem> Items { get; set; } = Enumerable.Empty<TItem>();
[Parameter] public Func<TItem, TValue?> ValueSelector { get; set; } = default!;
[Parameter] public Func<TItem, string> LabelSelector { get; set; } = default!;
[Parameter] public FilterContext<TItem> Context { get; set; } = default!;
private bool _open;
private bool _selectAll = true;
private string _icon = Icons.Material.Outlined.FilterAlt;
private HashSet<TValue> _selectedValues = new();
private List<TValue> _distinctValues = new();
private FilterDefinition<TItem>? _filterDefinition;
protected override void OnParametersSet()
{
_distinctValues = Items
.Select(ValueSelector)
.Where(v => v is not null)
.Distinct()!
.ToList();
if (_selectAll)
_selectedValues = _distinctValues.ToHashSet();
// Crear el FilterDefinition una sola vez (o regenerarlo si cambian Items)
_filterDefinition ??= new FilterDefinition<TItem>
{
FilterFunction = x =>
{
var val = ValueSelector(x);
return val is not null && _selectedValues.Contains(val);
}
};
}
private string GetLabel(TValue val)
{
var item = Items.FirstOrDefault(x => EqualityComparer<TValue>.Default.Equals(ValueSelector(x)!, val));
return item is null ? val?.ToString() ?? "" : LabelSelector(item);
}
private void SelectAllChanged(bool value)
{
_selectAll = value;
if (value)
_selectedValues = _distinctValues.ToHashSet();
else
_selectedValues.Clear();
}
private void ValueChanged(bool value, TValue val)
{
if (value)
_selectedValues.Add(val);
else
_selectedValues.Remove(val);
_selectAll = _selectedValues.Count == _distinctValues.Count;
}
private async Task Clear()
{
_selectedValues.Clear();
_selectAll = false;
_icon = Icons.Material.Outlined.FilterAlt;
if (_filterDefinition is not null)
await Context.Actions.ClearFilterAsync(_filterDefinition);
_open = false;
}
private async Task Apply()
{
_icon = _selectedValues.Count == _distinctValues.Count
? Icons.Material.Outlined.FilterAlt
: Icons.Material.Filled.FilterAlt;
if (_filterDefinition is not null)
await Context.Actions.ApplyFilterAsync(_filterDefinition);
_open = false;
}
}
Ejemplo de uso: ExcelLikeFilterColumn
Si tienes una propiedad como Marca, que tiene un ID y un nombre, puedes filtrar así:
<PropertyColumn Property="x => x.MarcaNombre" Title="Marca">
<FilterTemplate>
<ExcelFilterColumn TItem="DireccionesViewModel" TValue="short"
Items="context.Items"
ValueSelector="x => x.MarcaId!.Value"
LabelSelector="x => x.MarcaNombre.ToUpperInvariant()"
Context="context" />
</FilterTemplate>
</PropertyColumn>Con esto:
ValueSelectordefine cuál es el valor real a filtrar.LabelSelectordefine el texto que verá el usuario.- Los valores duplicados se agrupan automáticamente.
- El filtro soporta múltiples selecciones.
Conclusión
Este filtro tipo Excel me ha sido ayuda para crear filtros con los que los usuarios están más familiarizados los usuarios, esto les brinda una manera familiar y potente de filtrar valores sin tener que escribir texto ni conocer criterios avanzados.


