WPF - Bagaimana cara memaksa Command untuk mengevaluasi kembali 'CanExecute' melalui CommandBindings-nya

130

Saya memiliki di Menumana masing-masing MenuItemdalam hierarki memiliki Commandproperti yang ditetapkan ke yang RoutedCommandtelah saya tentukan. Yang terkait CommandBindingmemberikan panggilan balik untuk evaluasi CanExecuteyang mengontrol keadaan masing-masing yang diaktifkan MenuItem.

Ini hampir berhasil. Item menu pada awalnya muncul dengan status diaktifkan dan dinonaktifkan yang benar. Namun ketika data bahwa CanExecutepanggilan balik saya menggunakan perubahan, saya memerlukan perintah untuk meminta kembali hasil dari panggilan balik saya agar keadaan baru ini tercermin di UI.

Tampaknya tidak ada metode publik untuk RoutedCommandatau CommandBindinguntuk ini.

Perhatikan bahwa callback digunakan lagi ketika saya mengklik atau mengetik ke kontrol (saya kira itu dipicu pada input karena mouse-over tidak menyebabkan penyegaran).

Drew Noakes
sumber

Jawaban:

172

Bukan yang tercantik di buku ini, tetapi Anda bisa menggunakan CommandManager untuk membatalkan semua perintah-perintah:

CommandManager.InvalidateRequerySuggested();

Lihat info lebih lanjut tentang MSDN

Arcturus
sumber
1
Terima kasih ini bekerja dengan baik. Ada sedikit keterlambatan di UI, tapi saya tidak terlalu khawatir tentang itu. Juga, saya langsung memilih jawaban Anda, lalu mengambil kembali untuk melihat apakah itu berhasil. Sekarang sudah berfungsi, saya tidak bisa mendaftar ulang lagi. Tidak yakin mengapa SO menerapkan aturan itu.
Drew Noakes
5
Saya mengedit jawaban Anda untuk menerapkan kembali suara saya. Saya tidak mengubah apa pun dalam suntingan. Terima kasih lagi.
Drew Noakes
Saya memiliki masalah yang sama terjadi ketika saya mengubah konten Texbox dari kode di belakang. Jika Anda mengeditnya dengan tangan, itu akan berhasil. Di aplikasi ini, mereka memiliki texbox yang sedang diedit oleh kontrol yang akan muncul, dan ketika Anda menyimpan popup, itu akan mengubah properti Texbox.Text. Ini memecahkan masalah! Terima kasih @Arcturus
Dzyann
10
Catat saja dari jawaban lain ( stackoverflow.com/questions/783104/refresh-wpf-command ) "harus dipanggil di utas UI"
Samvel Siradeghyan
84

Untuk siapa saja yang menemukan ini nanti; Jika Anda menggunakan MVVM dan Prism, maka DelegateCommandimplementasi Prism ICommandmenyediakan .RaiseCanExecuteChanged()metode untuk melakukan ini.

CodingWithSpike
sumber
12
Pola ini juga ditemukan di pustaka MVVM lainnya, misalnya MVVM Light.
Peter Lillevold
2
Tidak seperti Prism, kode sumber MVVM Light v5 menunjukkan RaiseCanExecuteChanged() panggilan sederhana CommandManager.InvalidateRequerySuggested().
Peter
4
catatan untuk MVVM Light di WPF, Anda perlu menggunakan namespace GalaSoft.MvvmLight.CommandWpf sejak GalaSoft.MvvmLight.Command akan menyebabkan masalah mvvmlight.net/installing/changes#v5_0_2
fuchs777
((RelayCommand)MyCommand).RaiseCanExecuteChanged();bekerja untuk saya, menggunakan GalaSoft.MvvmLight.Command - NAMUN setelah berubah menjadi CommandWPF, itu bekerja tanpa perlu memanggil apa pun. Terima kasih @ fuchs777
Robin Bennett
1
Bagaimana jika Anda tidak menggunakan perpustakaan pihak ke-3?
Vidar
28

Saya tidak bisa menggunakan CommandManager.InvalidateRequerySuggested();karena saya mendapatkan kinerja yang baik.

Saya telah menggunakan perintah Delegating MVVM Helper , yang terlihat seperti di bawah ini (saya telah men-tweak sedikit untuk req kami). Anda harus menelepon command.RaiseCanExecuteChanged()dari VM

public event EventHandler CanExecuteChanged
{
    add
    {
        _internalCanExecuteChanged += value;
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        _internalCanExecuteChanged -= value;
        CommandManager.RequerySuggested -= value;
    }
}

/// <summary>
/// This method can be used to raise the CanExecuteChanged handler.
/// This will force WPF to re-query the status of this command directly.
/// </summary>
public void RaiseCanExecuteChanged()
{
    if (canExecute != null)
        OnCanExecuteChanged();
}

/// <summary>
/// This method is used to walk the delegate chain and well WPF that
/// our command execution status has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
    if (eCanExecuteChanged != null)
        eCanExecuteChanged(this, EventArgs.Empty);
}
Bek Raupov
sumber
3
Hanya FYI saya berkomentar CommandManager.RequerySuggested + = value; Saya mendapatkan evaluasi hampir konstan / berulang dari kode CanExecute saya untuk beberapa alasan. Kalau tidak, solusinya bekerja seperti yang diharapkan. Terima kasih!
robaudas
15

Jika Anda telah menggulir kelas Anda sendiri yang mengimplementasikannya, ICommandAnda dapat kehilangan banyak pembaruan status otomatis yang memaksa Anda untuk mengandalkan penyegaran manual lebih dari yang seharusnya diperlukan. Itu juga bisa pecah InvalidateRequerySuggested(). Masalahnya adalah ICommandimplementasi sederhana gagal untuk menghubungkan perintah baru ke CommandManager.

Solusinya adalah menggunakan yang berikut ini:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

Dengan cara ini pelanggan melampirkan CommandManagerdaripada kelas Anda dan dapat berpartisipasi dengan baik dalam perubahan status perintah.

Andrue Cope
sumber
2
Terus terang, to the point, dan memungkinkan orang untuk memiliki kontrol atas implementasi ICommand mereka.
Akoi Meexx
2

Saya telah menerapkan solusi untuk menangani ketergantungan properti pada perintah, di sini tautan https://stackoverflow.com/a/30394333/1716620

terima kasih untuk itu Anda akan akhirnya memiliki perintah seperti ini:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
    //execute
    () => {
      Console.Write("EXECUTED");
    },
    //can execute
    () => {
      Console.Write("Checking Validity");
       return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
    },
    //properties to watch
    (p) => new { p.PropertyX, p.PropertyY }
 );
Tidak penting
sumber
-3

Inilah yang berhasil bagi saya: Letakkan CanExecute di depan Command di XAML.

rmustakos
sumber