为什么 C# WPF 按钮绑定命令在使用简单注入器后不会更改视图?

我正在使用以下文章作为入门代码: 在 WPF MVVM简单注入器 WPF 集成
中的视图之间导航

目标:
尝试使用按钮绑定命令和简单注入器以 WPF 形式从视图 1 转到视图 2,以将依赖项注入视图。注意:这些依赖项是保存来自外部来源的数据的存储库。

问题:
使用 Simple Injector 将依赖项注入到我的 MainWindow 和 MainWindowViewModel 后,我的按钮不再更改我的当前视图(到我的另一个视图)。当使用 Visual Studio 并使用断点进行调试时,代码似乎永远停留在CanExecuteRelayCommand.cs 函数中的循环中(请参阅在 WPF MVVM 中的视图之间导航),其中某些东西一遍又一遍地调用它。我无法对CanExecute函数进行更多调试,因为有很多代码被传递(来自 DLL 等)。当不使用断点时,它就好像我的按钮什么也没做一样。

我在输出窗口中没有看到按钮错误,也没有抛出异常。命令绑定有效,因为我可以看到OnGo2Screen在调试时调用了 MainWindowViewModel.cs 中的函数。调用后OnGo2Screen,它按预期移动代码,直到卡在CanExecute.

我试过
的我检查了我的 MainWindow 的数据上下文,我可以看到它具有所有正确的功能。

我为“在 WPF MVVM 中的视图之间导航”一文创建了一个单独的项目,我能够很好地更改视图。但是每当我尝试使用 Simple Injector 时,我的按钮就会坏掉。

我注意到,当不使用 Simple Injector 时,代码从CanExecute函数移动到CanExecuteChangedEventHandler 并执行删除和添加修改器,然后按预期更改视图。但是,当使用 Simple Injector 时,它不会这样做。


我使用我的 App.xaml.cs 作为启动程序的代码,其中我的 App.xaml 具有“页面”的构建操作。

SimulationCaseView 是视图 1(默认起始视图)。
StreamsView 是视图 2(只是另一个视图)。
UserControl3 是视图 3(只是另一个视图)。

下面是我的代码。请参阅为任何剩余代码提供的两个链接,因为我的很多功能都基于此。


烙印99
浏览 127回答 2
2回答

一只斗牛犬

如何解决?在更新命令状态后调用此方法:CommandManager.InvalidateRequerySuggested();为什么不更新?命令仅在这些一般事件发生时更新:KeyUpMouseUpGotKeyboardFocusLostKeyboardFocus有关详细信息,请参阅此源代码:CommandDevice.cs对于其他控件,它有更多的事件需要刷新:长按时重复增加RepeatButtonDataGrid...SinglePageViewer...您可以双击此链接CommandManager.InvalidateRequerySuggested()的方法查看其他刷新命令状态的事件。因此,如果您的更新不在这些事件中发生,您的命令状态将不会更新。其他信息您说在使用 Visual Studio 并使用断点进行调试时,代码似乎在CanExecuteRelayCommand.cs 的函数中永远卡在一个循环中。这不是 for 的循环CanExecute,而是活动窗口在应用程序和 Visual Studio 之间切换时的GotKeyboardFocusand事件。LostKeyboardFocus

叮当猫咪

简答问题在于Lifestyle您的 ViewModel 必须设置为 aSingleton而不是 default Transient。&nbsp; &nbsp; private static Container Bootstrap()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Create the container as usual.&nbsp; &nbsp; &nbsp; &nbsp; var container = new Container();&nbsp; &nbsp; &nbsp; &nbsp; // Register your types, for instance:&nbsp; &nbsp; &nbsp; &nbsp; // Register your windows and view models:&nbsp; &nbsp; &nbsp; &nbsp; //container.Register<MainWindow>(Lifestyle.Singleton); //not needed&nbsp; &nbsp; &nbsp; &nbsp; container.Register<MainWindowViewModel>(Lifestyle.Singleton);&nbsp; &nbsp; &nbsp; &nbsp; container.Verify();&nbsp; &nbsp; &nbsp; &nbsp; return container;&nbsp; &nbsp; }然后你可以通过简单的方式启动应用程序&nbsp; &nbsp; private static void RunApplication(Container container)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; try&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var mainWindow = container.GetInstance<MainWindow>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var app = new App();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; app.InitializeComponent();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; app.Run(mainWindow);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; catch (Exception ex)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Log the exception and exit&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Debug.WriteLine(ex.Message);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }完整代码在github 上。长答案 - TL; DR当您调用container.Verifyin 时,Bootstrap您将创建一个实例MainWindowViewModel来验证其实例化,并创建另一个实例来验证MainWindow类。顺便说一句,您可以通过不验证容器来解决您的问题!所以第二个解决方案是&nbsp; &nbsp; &nbsp; &nbsp; //container.Register<MainWindow>(); // => Lifestyle.Transient;&nbsp; &nbsp; &nbsp; &nbsp; container.Register<MainWindowViewModel>(); // => Lifestyle.Transient;&nbsp; &nbsp; &nbsp; &nbsp; //container.Verify();现在,请注意您在c.tor中有Mediator订阅。MainWindowViewModel&nbsp; &nbsp; public static void Subscribe(string token, Action<object> callback)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (!pl_dict.ContainsKey(token))&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var list = new List<Action<object>>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list.Add(callback);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pl_dict.Add(token, list);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bool found = false;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //foreach (var item in pl_dict[token])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp; if (item.Method.ToString() == callback.Method.ToString())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp; &nbsp; &nbsp; found = true;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!found)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pl_dict[token].Add(callback);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }foreach循环——我只在上面评论过(它是解决你的问题的第三个替代选项) ——会让你跳过对第二个正确的 ViewModel 方法的调用,并会让你留下第一个错误的方法(记住Bootstrap验证创建了它两次)。如果你想要第四种替代解决方案,使用中介者模式IComponent的经典界面public interface IComponent{&nbsp; &nbsp; &nbsp;void OnGo1Screen(object obj);&nbsp; &nbsp; &nbsp;void OnGo2Screen(object obj);}public class MainWindowViewModel : BaseViewModel, IComponent您还可以将订阅移出 c.tor&nbsp; public MainWindowViewModel()&nbsp; {&nbsp; &nbsp; &nbsp;// Add available pages and set page&nbsp; &nbsp; &nbsp;PageViewModels.Add(new UserControl1ViewModel());&nbsp; &nbsp; &nbsp;PageViewModels.Add(new UserControl2ViewModel());&nbsp; &nbsp; &nbsp;CurrentPageViewModel = PageViewModels[0];&nbsp; &nbsp; &nbsp;//Mediator.Subscribe("GoTo1Screen", OnGo1Screen);&nbsp; &nbsp; &nbsp;//Mediator.Subscribe("GoTo2Screen", OnGo2Screen);&nbsp; }进入你的Program:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var context = mainWindow.DataContext as IComponent;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Mediator.Subscribe("GoTo1Screen", context.OnGo1Screen);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Mediator.Subscribe("GoTo2Screen", context.OnGo2Screen);
打开App,查看更多内容
随时随地看视频慕课网APP