Tôi đã xác định rằng có ít nhất hai lỗi trong ContextMenu khiến các cuộc gọi CanExecute của nó không đáng tin cậy trong các trường hợp khác nhau. Nó gọi CanExecute ngay lập tức khi Command được thiết lập. Các cuộc gọi sau này là không thể đoán trước và chắc chắn không đáng tin cậy.
Tôi đã dành cả đêm khi cố gắng theo dõi các điều kiện chính xác mà theo đó nó sẽ không thành công và tìm cách giải quyết. Cuối cùng tôi đã từ bỏ và chuyển sang Trình xử lý nhấp chuột đã kích hoạt các lệnh mong muốn.
Tôi đã xác định rằng một trong những vấn đề của tôi là việc thay đổi DataContext của ContextMenu có thể làm cho CanExecute được gọi trước khi Command hoặc CommandParameter mới bị ràng buộc.
Giải pháp tốt nhất mà tôi biết cho vấn đề này là sử dụng tài sản gắn liền của riêng bạn cho chỉ huy và CommandBinding thay vì sử dụng được xây dựng trong những:
Khi thuộc tính chỉ huy kết nối của bạn được thiết lập, đăng ký với Các sự kiện Click và DataContextChanged trên MenuItem, và cũng đăng ký CommandManager.RequerySuggested.
Khi DataContext thay đổi, RequerySuggested xuất hiện hoặc một trong hai thuộc tính đính kèm của bạn thay đổi, lên lịch điều phối bằng Dispatcher.BeginInvoke sẽ gọi CanExecute() và cập nhật IsEnabled trên MenuItem.
Khi sự kiện Nhấp chuột kích hoạt, thực hiện điều CanExecute và nếu nó trôi qua, hãy gọi Execute().
Cách sử dụng cũng giống như lệnh thường xuyên và CommandParameter, nhưng sử dụng tài sản gắn liền thay vì:
<Setter Property="my:ContexrMenuFixer.Command" Value="{Binding}" />
<Setter Property="my:ContextMenuFixer.CommandParameter" Value="{Binding Source=... }" />
giải pháp này hoạt động và bỏ qua tất cả các vấn đề với các lỗi trong việc xử lý CanExecute ContextMenu của.
Hy vọng một ngày nào đó Microsoft sẽ khắc phục sự cố với ContextMenu và cách giải quyết này sẽ không còn cần thiết nữa. Tôi có một trường hợp repro ngồi quanh đây ở đâu đó mà tôi định gửi đến Connect. Có lẽ tôi nên vào bóng và thực sự làm điều đó.
RequerySuggested là gì và tại sao lại sử dụng nó?
Cơ chế RequerySuggested là cách của RoutedCommand xử lý hiệu quả ICommand.CanExecuteChanged.Trong thế giới phi RoutedCommand, mỗi ICommand có danh sách các thuê bao riêng của mình để CanExecuteChanged, nhưng đối với RoutedCommand, bất kỳ khách hàng nào đăng ký vào ICommand.CanExecuteChanged sẽ thực sự đăng ký CommandManager.RequerySuggested. Mô hình đơn giản này có nghĩa là bất kỳ thời gian nào mà CanExecute của RoutedCommand có thể thay đổi, tất cả những gì cần thiết là gọi CommandManager.InvalidateRequerySuggested(), sẽ thực hiện những việc tương tự như bắn ICommand.CanExecuteChanged nhưng thực hiện nó cho tất cả RoutedCommands cùng một lúc và trên một luồng nền. Ngoài ra, các lời gọi RequerySuggested được kết hợp với nhau để nếu có nhiều thay đổi xảy ra thì CanExecute chỉ cần được gọi một lần.
Lý do tôi đề nghị bạn đăng ký CommandManager.RequerySuggested thay vì ICommand.CanExecuteChanged là: 1. Bạn không cần mã để xóa đăng ký cũ và thêm mã mới mỗi khi giá trị của thuộc tính Command của bạn thay đổi thay đổi , và 2. CommandManager.RequerySuggested có một tính năng tham chiếu yếu được xây dựng trong đó cho phép bạn thiết lập xử lý sự kiện của bạn và vẫn được thu gom rác thải. Làm tương tự với ICommand yêu cầu bạn phải thực hiện cơ chế tham chiếu yếu của riêng bạn.
Mặt trái của điều này là nếu bạn đăng ký CommandManager.RequerySuggested thay vì ICommand.CanExecuteChanged là bạn sẽ chỉ nhận được bản cập nhật cho RoutedCommands. Tôi sử dụng RoutedCommands độc quyền vì vậy đây không phải là một vấn đề đối với tôi, nhưng tôi nên đã đề cập rằng nếu bạn sử dụng ICommands thường xuyên đôi khi bạn nên xem xét làm công việc phụ của yếu đăng ký vào ICommand.CanExecutedChanged. Lưu ý rằng nếu bạn làm điều này, bạn không cần phải đăng ký vào RequerySuggested là tốt, kể từ RoutedCommand.add_CanExecutedChanged đã làm điều này cho bạn.
tôi đã có vấn đề chính xác như vậy. Giải pháp của tôi là liên kết lệnh sau tham số lệnh bằng cách đặt setter của tham số lệnh trước khi setter của lệnh và đột nhiên paramter bị ràng buộc được chuyển tới cuộc gọi đầu tiên của 'CanExecute'. – Cubinator73