ReactiveUI.Validation: this.IsValid() 在调用 ReactiveCommand 后更新
ReactiveUI.Validation: this.IsValid() updated after ReactiveCommand is invoked
我开始使用 ReactiveUI.Validation。当在 ReactiveCommand.Create() 中使用 this.IsValid() 作为 CanExecute 的参数时,IsValid 是一个用户输入“迟到”。
这是一个 ViewModel.cs 重现问题的方法,为了测试,我将它绑定到 WPF 文本框:
public class ViewModel : ReactiveValidationObject<ViewModel>
{
private readonly ReactiveCommand<string, Unit> Command;
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set => this.RaiseAndSetIfChanged(ref _myProperty, value);
}
public ViewModel()
{
Command = ReactiveCommand.Create<string>(x => SetMyProperty(x), this.IsValid());
this
.WhenAnyValue(x => x.MyProperty)
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
this.ValidationRule(
viewModel => viewModel.MyProperty,
x => !string.IsNullOrEmpty(x) && x.Length > 3,
"Enter a value");
this.IsValid()
.Subscribe(x => Console.WriteLine("IsValid = " + x));
}
private Unit SetMyProperty(string value)
{
Console.WriteLine("Entered method");
return Unit.Default;
}
}
这是我得到的控制台输出(执行命令时注意):
Property value = 1
Property value = 12
Property value = 123
Property value = 1234
IsValid = True
Property value = 12345
Entered method
Property value = 1234
Entered method
Property value = 123
Entered method
IsValid = False
Property value = 12
Property value = 1
这是我期望的控制台输出:
Property value = 1
Property value = 12
Property value = 123
Property value = 1234
IsValid = True
Entered method
Property value = 12345
Entered method
Property value = 1234
Entered method
Property value = 123
IsValid = False
Property value = 12
Property value = 1
我使用正确吗?有没有办法在 InvokeCommand 之前强制进行验证?
非常感谢您的帮助!
有几种解决方案可以摆脱这种情况:
解决方案 1:不要使用 InvokeCommand
如果您想在任何情况下绕过验证,显而易见的是不要使用 InvokeCommand
。检查 documentation of InvokeCommand()
:
Hint InvokeCommand
respects the command's executability. That is, if the command's CanExecute
method returns false
, InvokeCommand
will not execute the command when the source observable ticks.
因此您可能希望通过命令直接调用 SetMyProperty()
方法。
解决方案 2:延迟 MyProperty
的订阅
您可以使用 Observable.Delay()
扩展程序等待一小段时间,然后再对 属性 更改做出反应。这样你就可以“给足够的时间”让 IsValid()
检查在进行你的 InvokeCommand
调用之前进行切换。代码可能如下所示:
this.WhenAnyValue(x => x.MyProperty)
.Delay(TimeSpan.FromMilliseconds(100))
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
由 GitHub 的好心人解决:https://github.com/reactiveui/ReactiveUI.Validation/issues/92
post 在 GitHub 上的副本:
If you place a call to this.WhenAnyValue below a call to this.ValidationRule, you'll get the following behavior:
IsValid = False
Property value = 1
Property value = 12
Property value = 123
IsValid = True
Property value = 1234
Entered method
Property value = 12345
Entered method
Property value = 1234
Entered method
IsValid = False
Property value = 123
Property value = 12
Property value = 1
The modified code looks as such:
public class ViewModel : ReactiveValidationObject<ViewModel>
{
private readonly ReactiveCommand<string, Unit> Command;
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set => this.RaiseAndSetIfChanged(ref _myProperty, value);
}
public ViewModel()
{
Command = ReactiveCommand.Create<string>(x => SetMyProperty(x), this.IsValid());
this.ValidationRule(
viewModel => viewModel.MyProperty,
x => !string.IsNullOrEmpty(x) && x.Length > 3,
"Enter a value");
// The call to WhenAny is now placed below a call to ValidationRule.
this.WhenAnyValue(x => x.MyProperty)
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
this.IsValid()
.Subscribe(x => Console.WriteLine("IsValid = " + x));
}
private Unit SetMyProperty(string value)
{
Console.WriteLine("Entered method");
return Unit.Default;
}
}
Both WhenAny and ValidationRule are using CurrentThreadScheduler since #97, so the call order matters now. Hopefully we will deploy the new version of the package to NuGet soon.
它适用于 ReactiveUI.Validation
的新版本 1.6.4
我开始使用 ReactiveUI.Validation。当在 ReactiveCommand.Create() 中使用 this.IsValid() 作为 CanExecute 的参数时,IsValid 是一个用户输入“迟到”。
这是一个 ViewModel.cs 重现问题的方法,为了测试,我将它绑定到 WPF 文本框:
public class ViewModel : ReactiveValidationObject<ViewModel>
{
private readonly ReactiveCommand<string, Unit> Command;
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set => this.RaiseAndSetIfChanged(ref _myProperty, value);
}
public ViewModel()
{
Command = ReactiveCommand.Create<string>(x => SetMyProperty(x), this.IsValid());
this
.WhenAnyValue(x => x.MyProperty)
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
this.ValidationRule(
viewModel => viewModel.MyProperty,
x => !string.IsNullOrEmpty(x) && x.Length > 3,
"Enter a value");
this.IsValid()
.Subscribe(x => Console.WriteLine("IsValid = " + x));
}
private Unit SetMyProperty(string value)
{
Console.WriteLine("Entered method");
return Unit.Default;
}
}
这是我得到的控制台输出(执行命令时注意):
Property value = 1
Property value = 12
Property value = 123
Property value = 1234
IsValid = True
Property value = 12345
Entered method
Property value = 1234
Entered method
Property value = 123
Entered method
IsValid = False
Property value = 12
Property value = 1
这是我期望的控制台输出:
Property value = 1
Property value = 12
Property value = 123
Property value = 1234
IsValid = True
Entered method
Property value = 12345
Entered method
Property value = 1234
Entered method
Property value = 123
IsValid = False
Property value = 12
Property value = 1
我使用正确吗?有没有办法在 InvokeCommand 之前强制进行验证?
非常感谢您的帮助!
有几种解决方案可以摆脱这种情况:
解决方案 1:不要使用 InvokeCommand
如果您想在任何情况下绕过验证,显而易见的是不要使用 InvokeCommand
。检查 documentation of InvokeCommand()
:
Hint
InvokeCommand
respects the command's executability. That is, if the command'sCanExecute
method returnsfalse
,InvokeCommand
will not execute the command when the source observable ticks.
因此您可能希望通过命令直接调用 SetMyProperty()
方法。
解决方案 2:延迟 MyProperty
的订阅
您可以使用 Observable.Delay()
扩展程序等待一小段时间,然后再对 属性 更改做出反应。这样你就可以“给足够的时间”让 IsValid()
检查在进行你的 InvokeCommand
调用之前进行切换。代码可能如下所示:
this.WhenAnyValue(x => x.MyProperty)
.Delay(TimeSpan.FromMilliseconds(100))
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
由 GitHub 的好心人解决:https://github.com/reactiveui/ReactiveUI.Validation/issues/92
post 在 GitHub 上的副本:
If you place a call to this.WhenAnyValue below a call to this.ValidationRule, you'll get the following behavior:
IsValid = False
Property value = 1
Property value = 12
Property value = 123
IsValid = True
Property value = 1234
Entered method
Property value = 12345
Entered method
Property value = 1234
Entered method
IsValid = False
Property value = 123
Property value = 12
Property value = 1The modified code looks as such:
public class ViewModel : ReactiveValidationObject<ViewModel>
{
private readonly ReactiveCommand<string, Unit> Command;
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set => this.RaiseAndSetIfChanged(ref _myProperty, value);
}
public ViewModel()
{
Command = ReactiveCommand.Create<string>(x => SetMyProperty(x), this.IsValid());
this.ValidationRule(
viewModel => viewModel.MyProperty,
x => !string.IsNullOrEmpty(x) && x.Length > 3,
"Enter a value");
// The call to WhenAny is now placed below a call to ValidationRule.
this.WhenAnyValue(x => x.MyProperty)
.Do(x => Console.WriteLine("Property value = " + x))
.InvokeCommand(Command);
this.IsValid()
.Subscribe(x => Console.WriteLine("IsValid = " + x));
}
private Unit SetMyProperty(string value)
{
Console.WriteLine("Entered method");
return Unit.Default;
}
}
Both WhenAny and ValidationRule are using CurrentThreadScheduler since #97, so the call order matters now. Hopefully we will deploy the new version of the package to NuGet soon.
它适用于 ReactiveUI.Validation
的新版本 1.6.4