MudBlazor:如何在自定义十进制输入组件中启用实时千分位分隔符?
实现Blazor MudTextField实时千分位格式化+聚焦全选
需求:
- 接收十进制数字,输入时立即显示千分位分隔符(例:输入
123456.78实时显示123,456.78) - 字段获得焦点时自动全选所有文本
当前代码仅在失去焦点后才显示格式化文本,无法实时生效,原代码如下:
@using System.Globalization <MudTextField T="string" @ref="_input" Text="@_displayValue" TextChanged="@OnTextChanged" Immediate="true" Placeholder="@Placeholder" Margin="Margin.Dense" Variant="@Variant" Class="@($"small-input {Class}")" Pattern="^[0-9]*\.?[0-9]+$" @onfocus="OnInputFocus"/> @code { [Parameter] public decimal? Value { get; set; } [Parameter] public EventCallback<decimal> ValueChanged { get; set; } [Parameter] public string? Placeholder { get; set; } [Parameter] public Variant Variant { get; set; } = Variant.Outlined; [Parameter] public string Class { get; set; } = string.Empty; private MudTextField<string> _input = default!; private string _displayValue = string.Empty; protected override void OnInitialized() { if (Value.HasValue) { _displayValue = FormatNumber(Value.Value); } } private void OnTextChanged(string newValue) { var raw = newValue.Replace(",", ""); if (decimal.TryParse(raw, out var parsed)) { Value = parsed; ValueChanged.InvokeAsync(parsed); _displayValue = FormatNumber(parsed); } else { _displayValue = newValue; } } private async Task OnInputFocus(FocusEventArgs args) { await _input.SelectAsync(); } private string FormatNumber(decimal number) => number.ToString("###,##0.##", CultureInfo.InvariantCulture); }
解决方案
修改后的代码
@using System.Globalization @using System.Text.RegularExpressions <MudTextField T="string" @ref="_input" @bind-Text="_displayValue" Immediate="true" Placeholder="@Placeholder" Margin="Margin.Dense" Variant="@Variant" Class="@($"small-input {Class}")" @oninput="OnInput" @onfocus="OnInputFocus"/> @code { [Parameter] public decimal? Value { get; set; } [Parameter] public EventCallback<decimal> ValueChanged { get; set; } [Parameter] public string? Placeholder { get; set; } [Parameter] public Variant Variant { get; set; } = Variant.Outlined; [Parameter] public string Class { get; set; } = string.Empty; private MudTextField<string> _input = default!; private string _displayValue = string.Empty; private int _cursorPosition; protected override void OnInitialized() { if (Value.HasValue) { _displayValue = FormatNumber(Value.Value); } } private async Task OnInput(ChangeEventArgs e) { var inputValue = e.Value?.ToString() ?? string.Empty; // 过滤非法字符,仅保留数字和单个小数点 var rawValue = Regex.Replace(inputValue, @"[^0-9.]", ""); var parts = rawValue.Split('.'); if (parts.Length > 2) { rawValue = $"{parts[0]}.{string.Join("", parts.Skip(1))}"; } if (decimal.TryParse(rawValue, out var parsedDecimal)) { Value = parsedDecimal; await ValueChanged.InvokeAsync(parsedDecimal); // 保存当前光标位置 _cursorPosition = await _input.GetCursorPositionAsync(); var formattedValue = FormatNumber(parsedDecimal); // 计算新增的千分位逗号数量 var commasAdded = formattedValue.Count(c => c == ','); // 根据原光标位置调整(小数点前的位置需要加上逗号数量,小数点后的位置不变) if (!rawValue.Contains('.')) { _cursorPosition += commasAdded; } else { var dotIndexInRaw = rawValue.IndexOf('.'); if (_cursorPosition <= dotIndexInRaw) { _cursorPosition += commasAdded; } } _displayValue = formattedValue; // 延迟确保DOM更新后恢复光标位置 await Task.Delay(1); await _input.SetCursorPositionAsync(_cursorPosition); } else { _displayValue = inputValue; } } private async Task OnInputFocus(FocusEventArgs args) { await Task.Delay(1); await _input.SelectAsync(); } private string FormatNumber(decimal number) => number.ToString("###,##0.##", CultureInfo.InvariantCulture); }
核心修改说明
- 替换触发事件:用
@oninput替代TextChanged,确保每一次按键输入都实时触发处理逻辑,而非等待输入框状态变更。 - 光标位置处理:格式化时保存并恢复光标位置,避免添加千分位逗号后光标跳转到末尾,提升输入流畅度。
- 输入合法性校验:用正则过滤非数字/小数点字符,同时确保仅保留一个小数点,避免无效输入。
- 聚焦全选优化:增加
Task.Delay(1)确保组件渲染完成后再执行全选,避免因DOM未更新导致的全选失效。
内容的提问来源于stack exchange,提问作者Alireza Noori
相关产品推荐
相关产品推荐

