WPF - Cách buộc một Lệnh đánh giá lại 'CanExecute' thông qua CommandBindings của nó


130

Tôi có một Menunơi mà mỗi MenuItemthứ bậc trong cấu trúc phân cấp có thuộc Commandtính được đặt thành RoutedCommandtôi đã xác định. Liên kết CommandBindingcung cấp một cuộc gọi lại để đánh giá CanExecutekiểm soát trạng thái kích hoạt của từng trạng thái MenuItem.

Điều này gần như hoạt động. Các mục menu ban đầu đi kèm với trạng thái được bật và tắt chính xác. Tuy nhiên, khi dữ liệu mà CanExecutecuộc gọi lại của tôi sử dụng thay đổi, tôi cần lệnh để yêu cầu lại kết quả từ cuộc gọi lại của mình để trạng thái mới này được phản ánh trong UI.

Dường như không có bất kỳ phương pháp công khai nào trên RoutedCommandhoặc CommandBindingcho việc này.

Lưu ý rằng cuộc gọi lại được sử dụng lại khi tôi nhấp hoặc nhập vào điều khiển (tôi đoán nó được kích hoạt khi nhập vì quá trình di chuột qua không gây ra làm mới).

Câu trả lời:


172

Không phải là đẹp nhất trong cuốn sách, nhưng bạn có thể sử dụng CommandManager để vô hiệu hóa tất cả các lệnh:

CommandManager.InvalidateRequerySuggested();

Xem thêm thông tin về MSDN


1
Cảm ơn điều này làm việc tốt. Có một chút chậm trễ trong giao diện người dùng, nhưng tôi không quá lo lắng về điều đó. Ngoài ra, tôi đã bỏ phiếu cho câu trả lời của bạn ngay lập tức, sau đó lấy lại phiếu để xem nó có hiệu quả không. Bây giờ nó hoạt động, tôi không thể áp dụng lại phiếu bầu. Không chắc chắn tại sao SO có quy tắc đó tại chỗ.
Drew Noakes

5
Tôi đã chỉnh sửa câu trả lời của bạn để áp dụng lại phiếu bầu của tôi. Tôi đã không thay đổi bất cứ điều gì trong chỉnh sửa. Cảm ơn một lần nữa.
Drew Noakes

Tôi đã gặp vấn đề tương tự khi tôi thay đổi nội dung của Texbox từ mã phía sau. Nếu bạn chỉnh sửa nó bằng tay, nó sẽ hoạt động. Trong ứng dụng này, họ có texbox được chỉnh sửa bởi một điều khiển sẽ bật lên và khi bạn lưu cửa sổ bật lên, nó sẽ thay đổi thuộc tính Texbox.Text. Điều này đã giải quyết vấn đề! Cảm ơn @Arcturus
Dzyann

10
Chỉ cần lưu ý từ câu trả lời khác ( stackoverflow.com/questions/783104/refresh-wpf-command ) "nó phải được gọi trên chuỗi UI"
Samvel Siradeghyan

84

Đối với bất cứ ai đi qua điều này sau; Nếu bạn tình cờ sử dụng MVVM và Prism, thì DelegateCommandviệc triển khai Prism ICommandcung cấp một .RaiseCanExecuteChanged()phương thức để làm điều này.


12
Mẫu này cũng được tìm thấy trong các thư viện MVVM khác, ví dụ MVVM Light.
Peter Lillevold

2
Không giống như Prism, mã nguồn của MVVM Light v5 chỉ ra các RaiseCanExecuteChanged() cuộc gọi đơn giản của nó CommandManager.InvalidateRequerySuggested().
Peter

4
một mặt lưu ý để MVVM Light trong WPF, bạn cần sử dụng không gian tên GalaSoft.MvvmLight.CommandWpf từ GalaSoft.MvvmLight.Command sẽ gây ra rắc rối mvvmlight.net/installing/changes#v5_0_2
fuchs777

((RelayCommand)MyCommand).RaiseCanExecuteChanged();làm việc cho tôi, sử dụng GalaSoft.MvvmLight.Command - NHƯNG sau khi đổi thành CommandWPF, nó hoạt động mà không cần phải gọi bất cứ điều gì. Cảm ơn @ fuchs777
Robin Bennett

1
Nếu bạn không sử dụng thư viện của bên thứ 3 thì sao?
Vidar

28

Tôi không thể sử dụng CommandManager.InvalidateRequerySuggested();vì tôi đã đạt được hiệu suất.

Tôi đã sử dụng lệnh Delegating của MVVM Helper , trông giống như bên dưới (tôi đã điều chỉnh nó một chút cho req của chúng tôi). bạn phải gọi command.RaiseCanExecuteChanged()từ 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);
}

3
Chỉ là một FYI tôi đã nhận xét CommandManager.RequerySuggested + = value; Tôi đã nhận được một đánh giá gần như liên tục / lặp của mã CanExecute của tôi vì một số lý do. Nếu không, giải pháp làm việc như mong đợi. Cảm ơn!
robaudas

15

Nếu bạn đã thực hiện lớp học của riêng mình mà thực hiện, ICommandbạn có thể mất rất nhiều cập nhật trạng thái tự động buộc bạn phải dựa vào làm mới thủ công nhiều hơn mức cần thiết. Nó cũng có thể phá vỡ InvalidateRequerySuggested(). Vấn đề là một ICommandtriển khai đơn giản không liên kết được lệnh mới với CommandManager.

Giải pháp là sử dụng như sau:

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

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

Bằng cách này, người đăng ký gắn vào CommandManagerchứ không phải lớp của bạn và có thể tham gia đúng vào các thay đổi trạng thái lệnh.


2
Nói thẳng ra, cho phép mọi người có quyền kiểm soát việc triển khai ICommand của họ.
Akoi Meexx

2

Tôi đã triển khai một giải pháp để xử lý sự phụ thuộc thuộc tính vào các lệnh, ở đây liên kết https://stackoverflow.com/a/30394333/1716620

nhờ đó bạn sẽ có một lệnh như thế này:

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

-3

Đây là những gì làm việc cho tôi: Đặt CanExecute trước Lệnh trong XAML.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.