MVVM中取消LostFocus:WPF TextBox验证前阻止失焦
要实现“验证通过才允许TextBox失去焦点”的需求,你当前用LostFocus事件的方式是行不通的——因为LostFocus是在焦点已经转移之后才触发的,这时候已经没法撤销焦点变化了。得换个思路,用**PreviewLostFocus事件**(这是焦点转移前触发的隧道事件,可以取消操作)配合命令来实现,具体步骤如下:
1. 修改XAML代码
把原来的LostFocus事件改成PreviewLostFocus,同时给InvokeCommandAction加上PassEventArgsToCommand="True",这样命令就能拿到CancelEventArgs参数,用来取消焦点转移:
<TextBox Grid.Column="0" Text="{Binding Input, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"> <b:Interaction.Triggers> <b:EventTrigger EventName="PreviewLostFocus"> <b:InvokeCommandAction Command="{Binding LostFocusCommand}" PassEventArgsToCommand="True" /> </b:EventTrigger> </b:Interaction.Triggers> </TextBox>
注意这里给Text绑定加上了ValidatesOnDataErrors=True,这是为了让TextBox能识别ViewModel里的验证逻辑。
2. 在ViewModel中实现验证和命令逻辑
首先,你的ViewModel需要实现验证接口(比如IDataErrorInfo或者INotifyDataErrorInfo)来判断输入是否合法,然后在命令里根据验证结果决定是否阻止焦点失去。下面是用IDataErrorInfo的示例:
using System.ComponentModel; using System.Windows; using System.Windows.Input; // 这里假设你用的是MvvmLight的RelayCommand,也可以自己实现带参数的ICommand using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; public class MyViewModel : ViewModelBase, IDataErrorInfo { private string _input; public string Input { get => _input; set => Set(ref _input, value); } public ICommand LostFocusCommand { get; } public MyViewModel() { LostFocusCommand = new RelayCommand<CancelEventArgs>(ExecutePreviewLostFocus); } private void ExecutePreviewLostFocus(CancelEventArgs e) { // 检查当前Input是否有验证错误 if (!string.IsNullOrEmpty(this["Input"])) { // 验证不通过,阻止焦点转移 e.Cancel = true; // 可选:给用户提示错误信息,比如弹出MessageBox MessageBox.Show("输入不合法,请修正后再离开!"); } } // IDataErrorInfo 实现 public string Error => null; public string this[string columnName] { get { if (columnName == nameof(Input)) { // 这里写你的验证规则,比如:不能为空且长度至少3位 if (string.IsNullOrWhiteSpace(Input) || Input.Length < 3) { return "输入不能为空,且长度至少3位"; } } return null; } } }
关键细节说明
- 为什么用PreviewLostFocus?:
LostFocus事件是焦点已经转移后的通知事件,无法撤销;而PreviewLostFocus是焦点转移前的前置事件,通过设置e.Cancel = true可以直接阻止焦点离开TextBox。 - 验证接口的选择:如果你的验证逻辑比较复杂(比如异步验证),推荐用
INotifyDataErrorInfo;简单场景用IDataErrorInfo就足够了。 - 命令参数的传递:
PassEventArgsToCommand="True"是Microsoft.Xaml.Behaviors(原System.Windows.Interactivity)里InvokeCommandAction的属性,能把事件参数传给命令,这样我们才能在ViewModel里控制是否取消操作。
内容的提问来源于stack exchange,提问作者ar.gorgin
相关产品推荐
相关产品推荐

