2013-10-30 36 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();