为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
烙印99 2022-11-13 13:55:15
我正在使用以下文章作为入门代码: 在 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(只是另一个视图)。下面是我的代码。请参阅为任何剩余代码提供的两个链接,因为我的很多功能都基于此。
查看完整描述

2 回答

?
一只斗牛犬

TA贡献1784条经验 获得超2个赞

如何解决?

在更新命令状态后调用此方法:

CommandManager.InvalidateRequerySuggested();

为什么不更新?

命令仅在这些一般事件发生时更新:

  • KeyUp

  • MouseUp

  • GotKeyboardFocus

  • LostKeyboardFocus

有关详细信息,请参阅此源代码:CommandDevice.cs

对于其他控件,它有更多的事件需要刷新:

  • 长按时重复增加RepeatButton

  • DataGrid...

  • SinglePageViewer...

您可以双击此链接CommandManager.InvalidateRequerySuggested()的方法查看其他刷新命令状态的事件。

因此,如果您的更新不在这些事件中发生,您的命令状态将不会更新。

其他信息

您说在使用 Visual Studio 并使用断点进行调试时,代码似乎在CanExecuteRelayCommand.cs 的函数中永远卡在一个循环中。

这不是 for 的循环CanExecute,而是活动窗口在应用程序和 Visual Studio 之间切换时的GotKeyboardFocusand事件。LostKeyboardFocus


查看完整回答
反对 回复 2022-11-13
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

简答

问题在于Lifestyle您的 ViewModel 必须设置为 aSingleton而不是 default Transient。


    private static Container Bootstrap()

    {

        // Create the container as usual.

        var container = new Container();


        // Register your types, for instance:



        // Register your windows and view models:

        //container.Register<MainWindow>(Lifestyle.Singleton); //not needed

        container.Register<MainWindowViewModel>(Lifestyle.Singleton);


        container.Verify();


        return container;

    }

然后你可以通过简单的方式启动应用程序


    private static void RunApplication(Container container)

    {

        try

        {

            var mainWindow = container.GetInstance<MainWindow>();

            var app = new App();

            app.InitializeComponent();

            app.Run(mainWindow);

        }

        catch (Exception ex)

        {

            //Log the exception and exit

            Debug.WriteLine(ex.Message);

        }

    }

完整代码在github 上。


长答案 - TL; DR

当您调用container.Verifyin 时,Bootstrap您将创建一个实例MainWindowViewModel来验证其实例化,并创建另一个实例来验证MainWindow类。


顺便说一句,您可以通过不验证容器来解决您的问题!


所以第二个解决方案是


        //container.Register<MainWindow>(); // => Lifestyle.Transient;

        container.Register<MainWindowViewModel>(); // => Lifestyle.Transient;


        //container.Verify();

现在,请注意您在c.tor中有Mediator订阅。MainWindowViewModel


    public static void Subscribe(string token, Action<object> callback)

    {

        if (!pl_dict.ContainsKey(token))

        {

            var list = new List<Action<object>>();

            list.Add(callback);

            pl_dict.Add(token, list);

        }

        else

        {

            bool found = false;

            //foreach (var item in pl_dict[token])

            //    if (item.Method.ToString() == callback.Method.ToString())

            //        found = true;

            if (!found)

                pl_dict[token].Add(callback);

        }

    }

foreach循环——我只在上面评论过(它是解决你的问题的第三个替代选项) ——会让你跳过对第二个正确的 ViewModel 方法的调用,并会让你留下第一个错误的方法(记住Bootstrap验证创建了它两次)。如果你想要第四种替代解决方案,使用中介者模式IComponent的经典界面


public interface IComponent

{

     void OnGo1Screen(object obj);

     void OnGo2Screen(object obj);

}

public class MainWindowViewModel : BaseViewModel, IComponent

您还可以将订阅移出 c.tor


  public MainWindowViewModel()

  {

     // Add available pages and set page

     PageViewModels.Add(new UserControl1ViewModel());

     PageViewModels.Add(new UserControl2ViewModel());


     CurrentPageViewModel = PageViewModels[0];


     //Mediator.Subscribe("GoTo1Screen", OnGo1Screen);

     //Mediator.Subscribe("GoTo2Screen", OnGo2Screen);

  }

进入你的Program:


            var context = mainWindow.DataContext as IComponent;

            Mediator.Subscribe("GoTo1Screen", context.OnGo1Screen);

            Mediator.Subscribe("GoTo2Screen", context.OnGo2Screen);


查看完整回答
反对 回复 2022-11-13
  • 2 回答
  • 0 关注
  • 114 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信