DataGrid文本框:数字输入限制+空白自动填充默认值0的问题排查
问题描述
我在DataGrid的数据模板中使用TextBox绑定了Price字段(数据从文件加载),需要实现两个功能:
- 限制用户仅能输入带小数点的数字
- 不允许单元格为空,空白时自动填充默认值0
我尝试用TextChanged和TouchLeave事件处理,但均未生效,想知道错误原因、修复方法以及应该使用哪个事件。
原XAML代码:
<DataGridTemplateColumn Header="Price" Width="100"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBox Text="{Binding Price}" PreviewTextInput="OnPreviewTextInput" PreviewKeyDown="TextBox_PreviewKeyDown" TextChanged="TextBox_TextChanged" TouchLeave="TextBox_TouchLeave" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>
原后台代码:
private void OnPreviewTextInput(object sender, TextCompositionEventArgs e) { string CultureName = Thread.CurrentThread.CurrentCulture.Name; CultureInfo ci = new CultureInfo(CultureName); ci.NumberFormat.NumberDecimalSeparator = "."; Thread.CurrentThread.CurrentCulture = ci; string input = ((TextBox)sender).Text + e.Text; string pattern = "^[0-9]+[" + ci.NumberFormat.NumberDecimalSeparator + "]?([0-9]+)?$"; Regex regex = new Regex(pattern); e.Handled = !regex.IsMatch(input); } private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { // Prohibit space if (e.Key == Key.Space) e.Handled = true; } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { if (string.IsNullOrEmpty(e.ToString())) { e.Handled = true; ((TextBox)sender).Text = 0.ToString(); } } private void TextBox_TouchLeave(object sender, TouchEventArgs e) { if (string.IsNullOrEmpty(e.ToString())) { e.Handled = true; ((TextBox)sender).Text = 0.ToString(); } }
错误原因
- TextChanged事件逻辑错误:判断空值时用
string.IsNullOrEmpty(e.ToString()),实际是检查事件参数的字符串表示,而非TextBox的真实文本,导致填充0的逻辑永远不会触发;另外直接修改Text属性会再次触发TextChanged,可能引发循环调用。 - TouchLeave事件场景局限:该事件仅在触摸设备上用户离开控件时触发,PC端鼠标操作完全不会触发,且同样错误地判断事件参数而非文本内容。
- 绑定配置缺失:Text绑定未设置
UpdateSourceTrigger,默认是LostFocus,数据更新不及时;若Price是数值类型,空文本会导致绑定失败,无法同步值到数据源。
修复方案
1. 选择合适的事件
推荐使用LostFocus事件,它能覆盖鼠标、键盘、触摸所有操作场景,配合PreviewTextInput做输入限制,同时优化绑定配置。
2. 修改XAML代码
添加绑定的UpdateSourceTrigger=PropertyChanged,并替换TouchLeave为LostFocus:
<DataGridTemplateColumn Header="Price" Width="100"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBox Text="{Binding Price, UpdateSourceTrigger=PropertyChanged}" PreviewTextInput="OnPreviewTextInput" PreviewKeyDown="TextBox_PreviewKeyDown" LostFocus="TextBox_LostFocus"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>
3. 修改后台代码
输入限制逻辑优化(解决多小数点问题)
原正则允许多个小数点(如12..3),修正后确保仅能输入一个小数点:
private void OnPreviewTextInput(object sender, TextCompositionEventArgs e) { var textBox = sender as TextBox; if (textBox == null) return; string decimalSeparator = "."; // 拼接选中区域替换后的完整文本,更准确 string fullText = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength) .Insert(textBox.SelectionStart, e.Text); // 禁止以小数点开头 if (fullText.StartsWith(decimalSeparator)) { e.Handled = true; return; } // 限制仅能有一个小数点 if (fullText.Count(c => c == decimalSeparator[0]) > 1) { e.Handled = true; return; } // 正则匹配:允许整数或带一个小数点的小数 Regex regex = new Regex($"^[0-9]*{decimalSeparator}?[0-9]*$"); e.Handled = !regex.IsMatch(fullText); }
空值处理逻辑(LostFocus事件)
private void TextBox_LostFocus(object sender, RoutedEventArgs e) { var textBox = sender as TextBox; if (textBox == null) return; // 去除前后空白后判断是否为空 if (string.IsNullOrWhiteSpace(textBox.Text)) { textBox.Text = "0"; // 手动更新绑定源,确保数据同步到Price属性 BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty); binding?.UpdateSource(); } } private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Space) e.Handled = true; }
4. 额外建议(可选)
如果Price是数值类型(如double),可添加绑定验证避免空文本导致的绑定错误:
<TextBox Text="{Binding Price, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" .../>
随后在ViewModel的Price属性中实现验证逻辑,或通过IDataErrorInfo接口完成数据验证。
内容的提问来源于stack exchange,提问作者user838691
相关产品推荐
相关产品推荐

