C# 在WPF中,如何对LostFocus进行绑定,而对PropertyChanged进行验证?
我希望能够将UpdateSourceTrigger设置为LostFocus(默认设置)的文本框绑定,但在用户在文本中键入时执行验证。我能想到的最好办法是处理TextChanged事件,并在视图模型上调用验证方法。我想知道是否有更好的解决办法C# 在WPF中,如何对LostFocus进行绑定,而对PropertyChanged进行验证?,c#,wpf,data-binding,C#,Wpf,Data Binding,我希望能够将UpdateSourceTrigger设置为LostFocus(默认设置)的文本框绑定,但在用户在文本中键入时执行验证。我能想到的最好办法是处理TextChanged事件,并在视图模型上调用验证方法。我想知道是否有更好的解决办法 我的视图模型侦听模型中的属性更改以更新自身(包括格式)。我不想将UpdateSourceRigger设置为PropertyChanged进行绑定,因为这会导致文本在用户键入时立即格式化(例如,用户可能希望键入“1.2”,但在他/她键入“1”时,由于视图模型的
我的视图模型侦听模型中的属性更改以更新自身(包括格式)。我不想将UpdateSourceRigger设置为PropertyChanged进行绑定,因为这会导致文本在用户键入时立即格式化(例如,用户可能希望键入“1.2”,但在他/她键入“1”时,由于视图模型的自动格式化,文本会变为“1.0”。详细阐述了我留下的评论,下面是一个如何做到这一点的例子 仅供参考,我使用nuget软件包MvvmLight安装管道 main window.xaml
<StackPanel>
<TextBox x:Name="myTextBox" Text="{Binding SomeNumberViewModel.IntermediateText, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="100" Margin="5"/>
<Button Content="Hi" HorizontalAlignment="Center" Padding="5,15" Margin="5"/>
</StackPanel>
MainViewModel.cs
public MainViewModel ViewModel
{
get
{
return this.DataContext as MainViewModel;
}
}
public MainWindow()
{
InitializeComponent();
this.myTextBox.LostFocus += MyTextBox_LostFocus;
}
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// If the IntermediateText has no validation errors, then update your model.
if (string.IsNullOrEmpty(this.ViewModel.SomeNumberViewModel[nameof(this.ViewModel.SomeNumberViewModel.IntermediateText)]))
{
// Update your model and it gets formatted result
this.ViewModel.SomeNumberViewModel.ModelValue = this.ViewModel.SomeNumberViewModel.IntermediateText;
// Then, update your IntermediateText to update the UI.
this.ViewModel.SomeNumberViewModel.IntermediateText = this.ViewModel.SomeNumberViewModel.ModelValue;
}
}
public class MainViewModel : ViewModelBase
{
private SomeNumberViewModel someNumberViewModel;
public string MyTitle { get => "Stack Overflow Question 65279367"; }
public SomeNumberViewModel SomeNumberViewModel
{
get
{
if (this.someNumberViewModel == null)
this.someNumberViewModel = new SomeNumberViewModel(new MyModel());
return this.someNumberViewModel;
}
}
}
public class SomeNumberViewModel : ViewModelBase, IDataErrorInfo
{
public SomeNumberViewModel(MyModel model)
{
this.Model = model;
}
private string intermediateText;
public string IntermediateText { get => this.intermediateText; set { this.intermediateText = value; RaisePropertyChanged(); } }
public string ModelValue
{
get => this.Model.SomeNumber.ToString("0.00");
set
{
try
{
this.Model.SomeNumber = Convert.ToDouble(value);
RaisePropertyChanged();
}
catch
{
}
}
}
public MyModel Model { get; private set; }
public string Error { get => null; }
public string this[string columnName]
{
get
{
switch (columnName)
{
case "IntermediateText":
if (!string.IsNullOrEmpty(this.IntermediateText) && FormatErrors(this.IntermediateText))
return "Format errors";
break;
}
return string.Empty;
}
}
/// <summary>
/// Only allow numbers to be \d+, or \d+\.\d+
/// For Example: 1, 1.0, 11.23, etc.
/// Anything else is a format violation.
/// </summary>
/// <param name="numberText"></param>
/// <returns></returns>
private bool FormatErrors(string numberText)
{
var valid = (Regex.IsMatch(numberText, @"^(\d+|\d+\.\d+)$"));
return !valid;
}
}
public class MyModel
{
public double SomeNumber { get; set; }
}
SomeNumberViewModel.cs
public MainViewModel ViewModel
{
get
{
return this.DataContext as MainViewModel;
}
}
public MainWindow()
{
InitializeComponent();
this.myTextBox.LostFocus += MyTextBox_LostFocus;
}
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// If the IntermediateText has no validation errors, then update your model.
if (string.IsNullOrEmpty(this.ViewModel.SomeNumberViewModel[nameof(this.ViewModel.SomeNumberViewModel.IntermediateText)]))
{
// Update your model and it gets formatted result
this.ViewModel.SomeNumberViewModel.ModelValue = this.ViewModel.SomeNumberViewModel.IntermediateText;
// Then, update your IntermediateText to update the UI.
this.ViewModel.SomeNumberViewModel.IntermediateText = this.ViewModel.SomeNumberViewModel.ModelValue;
}
}
public class MainViewModel : ViewModelBase
{
private SomeNumberViewModel someNumberViewModel;
public string MyTitle { get => "Stack Overflow Question 65279367"; }
public SomeNumberViewModel SomeNumberViewModel
{
get
{
if (this.someNumberViewModel == null)
this.someNumberViewModel = new SomeNumberViewModel(new MyModel());
return this.someNumberViewModel;
}
}
}
public class SomeNumberViewModel : ViewModelBase, IDataErrorInfo
{
public SomeNumberViewModel(MyModel model)
{
this.Model = model;
}
private string intermediateText;
public string IntermediateText { get => this.intermediateText; set { this.intermediateText = value; RaisePropertyChanged(); } }
public string ModelValue
{
get => this.Model.SomeNumber.ToString("0.00");
set
{
try
{
this.Model.SomeNumber = Convert.ToDouble(value);
RaisePropertyChanged();
}
catch
{
}
}
}
public MyModel Model { get; private set; }
public string Error { get => null; }
public string this[string columnName]
{
get
{
switch (columnName)
{
case "IntermediateText":
if (!string.IsNullOrEmpty(this.IntermediateText) && FormatErrors(this.IntermediateText))
return "Format errors";
break;
}
return string.Empty;
}
}
/// <summary>
/// Only allow numbers to be \d+, or \d+\.\d+
/// For Example: 1, 1.0, 11.23, etc.
/// Anything else is a format violation.
/// </summary>
/// <param name="numberText"></param>
/// <returns></returns>
private bool FormatErrors(string numberText)
{
var valid = (Regex.IsMatch(numberText, @"^(\d+|\d+\.\d+)$"));
return !valid;
}
}
public class MyModel
{
public double SomeNumber { get; set; }
}
保持UpdateSourceTrigger=PropertyChanged,并添加
Delay=300
(它是毫秒)@ASh从可用性的角度来看这不是很尴尬吗?用户可能会键入“1”,如果他们输入“.2”的速度不够快,他们会看到文本变为“1.0”,我认为这会很烦人。此外,验证不会立即进行,但也会延迟(因为它发生在绑定时)。在我看来,延迟验证是一个很好的副作用(我个人不喜欢在仍然键入时使用红色边框/感叹号)。可能使用StringFormat进行格式化?我可以问一下您正在应用哪种验证吗?总的来说,我觉得有这样的要求有点奇怪。让视图模型控制文本的格式是不好的。此逻辑应在控件中实现。从数据的角度来看,数字1被视为1或1.0并不重要。总是1。在你的情况下,这是关于展示。您希望将1显示为1.0。这完全独立于将1推送到模型的视图模型。很明显,它甚至独立于验证,验证证明十进制要求对于数据的有效性不是强制性的。因此,要强制十进制数字,您应该覆盖UIElement。OnLostFocus
或处理元素的路由事件LostFocus
,以便在输入完成后应用格式规则。让视图模型规范化用于表示的数据是可以的:当从模型中读取1时,视图模型会在将数据公开给视图之前将其规范化为1.0。但是视图模型不应该直接调整用户输入。您应该将此类化妆品委托给收集输入并了解相关输入事件的控件。这也可以解决您的问题。我认为这是一个很好的解决方案,不过我会尝试使逻辑更可重用,这样它就可以应用于其他文本框,而无需重复代码。@redcurry明白了。这是第一步的解决方案,不是一个稳健的解决方案。