WPF:跨ViewModel更新ICommand的CanExecute状态的MVVM合规实现
Core Solution for Command State Update
To update SearchCommand's CanExecute state without tight coupling between ViewModels, use a shared settings service (your SettingsRepository) with event-based communication:
- Encapsulate all access to
Properties.Settings.DefaultinSettingsRepository. - Add an event (e.g.,
SettingsSaved) to the repository that fires when settings are successfully saved. - Inject the same
SettingsRepositoryinstance into bothMainWindowViewModelandPropertiesViewModel(via dependency injection or a singleton pattern). - Have
MainWindowViewModelsubscribe to theSettingsSavedevent, and trigger a command re-evaluation (e.g.,CommandManager.InvalidateRequerySuggested()) when the event fires. PropertiesViewModeluses the shared repository to save settings, which raises the event and triggers the command update inMainWindowViewModel.
1. Is instantiating SettingsRepository in both ViewModels reasonable?
No, this design has critical flaws:
- Separate repository instances mean the
SaveCompletedevent raised byPropertiesViewModelwon't reachMainWindowViewModel, breaking the command update flow. - It introduces unnecessary duplication and risks inconsistent state if the repository ever adds cached data or instance-specific logic.
Fix: Use a single instance of SettingsRepository for both ViewModels. Options include:
- Implementing the repository as a singleton (simple but less testable).
- Using dependency injection to inject the same instance into both ViewModels (preferred for testability and loose coupling).
2. Why does CanExecuteSearchDataCommand get called twice when MessageBox is present?
This is normal WPF behavior. When a MessageBox opens, it takes focus away from the main window. When it closes, the main window regains focus, which triggers WPF to automatically re-evaluate all bound commands' CanExecute states.
Your explicit call to InvalidateCommand adds a second re-evaluation. Without the MessageBox, there's no focus change event, so only your explicit call runs. This double call is harmless in most cases; if you want to avoid it, use a command implementation that supports explicit state updates (like RelayCommand with a RaiseCanExecuteChanged method) instead of relying on CommandManager.InvalidateRequerySuggested().
3. Resources to learn about InvalidateCommand and related mechanisms
Besides MSDN, these resources offer practical, in-depth guidance:
- Books:
- Pro WPF in C# by Matthew MacDonald: Covers command binding, MVVM patterns, and UI state management in detail.
- WPF 4.5 Unleashed by Adam Nathan: Explains WPF's command system and
CommandManagerbehavior thoroughly.
- Community Content:
- Josh Smith's classic MVVM articles: Focus on practical implementations of commands and ViewModel communication (look for "WPF Apps With The Model-View-ViewModel Design Pattern").
- Stack Overflow discussions: Search for topics like "ICommand CanExecute re-evaluation" or "CommandManager.InvalidateRequerySuggested" to find real-world solutions and explanations.
- MVVM Frameworks:
- Examine source code from frameworks like Prism, MvvmLight, or Caliburn.Micro. These implement robust command systems that handle
CanExecuteupdates efficiently, demonstrating best practices for invalidation.
- Examine source code from frameworks like Prism, MvvmLight, or Caliburn.Micro. These implement robust command systems that handle
内容的提问来源于stack exchange,提问作者Dragon

