1

(最も近い質問)

LogInRequest()which の呼び出しを実行します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

2 に答える 2

4

RelayCommandMvvmLight からを使用している場合、CanExecuteChangedサブスクリプションを に転送することでイベントを実装しCommandManager.RequerySuggestedます。これにより、が WPF で行うのと同じRelayCommandように、 が自身のステータスを効果的に更新できるようになります。RoutedCommandこのRequerySuggestedイベントは、フォーカスが変更されたときやウィンドウがアクティブ化 (非アクティブ化) されたときなど、特定の条件で発生します。このRequerySuggestedイベントは弱いイベント ハンドラーを使用してリークされたサブスクリプションを軽減しますが、WPF で使用される弱いイベントの実装は、それ自体のクリーンアップにそれほど熱心ではないため、サブスクリプションはしばらく (おそらく無期限に) アクティブなままになる可能性があります。

ブレークポイントに到達するたびに Visual Studio がアプリケーションからフォーカスを奪い、「再開」を押すとアプリケーションが再アクティブ化され、イベントがトリガーされて再評価されるため、CanExecuteコールバックはノンストップで再評価しているように見えます。これにより、ブレークポイントが再びトリガーされ、ブレークポイントを無効にするまでサイクルに巻き込まれます。RequerySuggestedCanExecute

ビュー モデルが閉じた状態を認識している場合は、VerifyLogInCanExecuteプロパティを次のように変更します。

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

少なくともそうすれば、必要以上の仕事をすることはなくなります。nullもう 1 つのオプションは、View Model が閉じられている (そして適切なPropertyChangedイベントが発生している) ときに、ログイン コマンドを (または空/no-op コマンド) に設定することです。CanExecuteChangedこれにより、コマンドにバインドされたすべてのボタンがそのイベントからサブスクライブ解除されます。

于 2013-10-30T15:52:24.140 に答える
1

Mike Storbl が使用する Mvvm light の 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(); 
于 2013-10-30T16:15:57.353 に答える