2013-10-30 35 views
1

Closest related questionRelayCommand闭合

我执行LogInRequest()它调用LogInView.ShowDialog()。该视图有一个叫做VerifyLogInCommand的命令。执行命令后,它会以this.CloseAction()完成,它似乎关闭了对话框。但是,在该视图的命令的CanExecute方法VerifyLogInCanExecute中,我的断点在关闭对话框(不间断)后仍然被触发。我试过在调用ShowDialog后将视图设置为null,但没有改变。

为什么当窗口关闭/ null时Command/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()); 
} 

LogInViewModel.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; 
    } 
} 

回答

4

如果您正在使用从MvvmLight的RelayCommand,它实现了CanExecuteChanged事件通过转发订阅CommandManager.RequerySuggested。这有效地允许RelayCommand按照RoutedCommand在WPF中的做法更新自己的状态;在某些情况下会触发RequerySuggested事件,包括每次焦点更改或窗口被激活时都会触发。 RequerySuggested事件使用弱事件处理程序来缓解泄漏的订阅,但WPF使用的弱事件实现并不是非常努力地清理自己,所以订阅可能仍然保持活动一段时间(甚至可能无限期)。

CanExecute回调出现进行重新评估不停,因为你每次遇到断点时,Visual Studio抢断注意力从你的应用程序,当你点击“继续”,您的应用程序被重新激活,从而引发RequerySuggested事件并导致CanExecute被重新评估。这反过来会再次触发断点,并且在循环中被捕获,直到您禁用断点。

如果您的视图模型知道其关闭状态的,我想你VerifyLogInCanExecute属性更改为类似:

public bool VerifyLogInCanExecute 
{ 
    get { return !IsClosed && CheckRequiredPasswordLength(Password); } 
} 

至少您不会做更多的工作比是必要的。另一种选择是在查看模型关闭时(并且提高相应的PropertyChanged事件)将登录命令设置为null(或空的/不可操作的命令)。这将导致绑定到该命令的任何按钮取消订阅其CanExecuteChanged事件。

+0

感谢您的好解释。我刚刚发现类似的解释设置命令为null时完成[这里也是](http://stackoverflow.com/a/6695530/1992193) –

+0

我有同样的问题,但它发生在我的项目中,即使在我将命令设置为空,任何想法? – Chen

+0

@Chen当你将你的命令清空时,你是在提升'PropertyChanged'吗?如果没有,绑定控件不知道该命令已被替换,并且将继续查询旧的。 –

1

在迈克Storbl指出,关于MVVM光的RelayCommand它采用

CommandManager.RequerySuggested 

这是非常广阔的性能明智的,而且很容易出现分配错误的,因为它提出了

CanExecuteChangedEvent 
每次

VisualTree的重点。

你应该实现自己的这样:

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();