关闭 ShowDialog 后,视图上的中继命令仍在评估中
本文关键字:命令 评估 ShowDialog 视图 关闭 | 更新日期: 2023-09-27 18:37:25
(最接近的相关问题)
我执行调用LogInView.ShowDialog()
LogInRequest()
。该视图有一个名为 VerifyLogInCommand
的命令。执行命令时,它完成并显示关闭对话框的this.CloseAction()
。但是,该视图命令的 CanExecute 方法VerifyLogInCanExecute
中的断点在对话框关闭(不间断)后仍然被命中。我尝试在调用 ShowDialog 后将视图设置为 null,但没有更改。
为什么当窗口关闭/null时,仍在评估命令/CanExecute?
LogInView.xaml.cs
public LogInOutView()
{
InitializeComponent();
// Data context
IModule existingVM = SessionViewModel.Instance.ModulesOpen.Single(mod => mod.ModuleName == "LogIn");
LogInViewModel livm = (LogInViewModel)existingVM;
this.DataContext = livm;
// Cancel Handler
livm.CloseAction = new Action(() => this.Close());
}
登录视图模型.cs
public Action CloseAction { get; set; }
private RelayCommand verifyLogInCommand;
public RelayCommand VerifyLogInCommand
{
get
{
if (verifyLogInCommand == null)
{
verifyLogInCommand = new RelayCommand(
param => VerifyLogInExecute(),
param => VerifyLogInCanExecute);
}
return verifyLogInCommand;
}
}
public void VerifyLogInExecute()
{
// Validate Login
Employee employee = ValidateLogin(Password);
// Clear password field
ResetExecute();
// Return false if invalid login
if (employee == null)
{
Result = LogInOutDialogResults.Cancel;
ConfirmationView c = new ConfirmationView("Invalid Login!");
c.ShowDialog();
return;
}
// Set Result to LogIn status
Result = LogInOutDialogResults.LogIn;
// Set LastAuthorizedEmployee
SessionViewModel.Instance.LastAuthorizedEmployee = employee;
// Close View to go back where it was called
this.CloseAction();
}
public bool VerifyLogInCanExecute
{
get
{
// Password length restrictions
if (!CheckRequiredPasswordLength(Password)) { return false; }
return true;
}
}
public static LogInOutDialogResults LogInRequest()
{
// Show Login View
LogInOutDialogResults LogInOutResult = LogInOutDialogResults.Cancel;
LogInOutView LogInOutView = new LogInOutView()
{
Title = "Log In",
ShowInTaskbar = false,
Topmost = true,
ResizeMode = ResizeMode.NoResize,
Owner = SessionViewModel.Instance.ProfitPOSView
};
LogInOutView.ShowDialog();
LogInOutResult = ((LogInViewModel)LogInOutView.DataContext).Result;
// LogIn
if (LogInOutResult == LogInOutDialogResults.LogIn)
{
LogInOutView = null;
return LogInOutDialogResults.LogIn;
}
}
如果您使用的是 MvvmLight 中的RelayCommand
,它会通过将订阅转发到 CommandManager.RequerySuggested
来实现其CanExecuteChanged
事件。 这有效地允许RelayCommand
像RoutedCommand
在 WPF 中那样更新自己的状态;RequerySuggested
事件在特定条件下触发,包括每次焦点更改或窗口被(取消)激活时。 RequerySuggested
事件使用弱事件处理程序来缓解泄漏的订阅,但 WPF 使用的弱事件实现在清理自身之后并不是非常勤奋,因此订阅可能仍保持活动状态一段时间(甚至可能无限期)。
CanExecute
回调似乎正在不间断地重新评估,因为每次遇到断点时,Visual Studio 都会从应用程序中窃取焦点,当您点击"恢复"时,应用程序将重新激活,从而触发RequerySuggested
事件并导致重新评估CanExecute
。 反过来,这会再次触发断点,并且您将陷入一个循环,直到禁用断点。
如果您的视图模型知道其关闭状态,我会将您的VerifyLogInCanExecute
属性更改为以下内容:
public bool VerifyLogInCanExecute
{
get { return !IsClosed && CheckRequiredPasswordLength(Password); }
}
至少那时你不会做超过必要的工作。 另一种选择是在视图模型关闭时将登录命令设置为 null
(或空/无操作命令)(并引发相应的PropertyChanged
事件)。 这将导致绑定到命令的任何按钮取消订阅其CanExecuteChanged
事件。
关于 Mike Storbl 指出的关于 Mvvm light 的 RelayCommand 使用
CommandManager.RequerySuggested
这是非常扩展的性能明智,并且容易分配错误,因为它会引发
CanExecuteChangedEvent
每次可视化树聚焦时。
你应该这样实现你自己的:
public class RelayCommand : ICommand
{
private Func<bool> _canExecute;
private Action _execute;
public RelayCommand(Action execute , Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
temp(this, new EventArgs());
}
public void Execute(object parameter)
{
_execute();
}
}
并在需要时从您的代码中提出它,例如当密码被更改时。
verifyLogInCommand.RaiseCanExecuteChanged();