首页 > 解决方案 > 为什么 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(只是另一个视图)。

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

应用程序.xaml

<Application x:Class="MyApp.Desktop.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:views="clr-namespace:MyApp.Desktop.Views">
    <Application.Resources>
        <DataTemplate DataType="{x:Type views:SimulationCaseViewModel}">
            <views:SimulationCaseView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type views:StreamsViewModel}">
            <views:StreamsView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type views:UserControl3ViewModel}">
            <views:UserControl3 />
        </DataTemplate>
    </Application.Resources>
</Application>

应用程序.xaml.cs

namespace MyApp.Desktop
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        App()
        {
            InitializeComponent();
        }

        [STAThread]
        static void Main()
        {
            var container = Bootstrap();

            // Any additional other configuration, e.g. of your desired MVVM toolkit.

            RunApplication(container);
        }


        private static Container Bootstrap()
        {
            // Create the container as usual.
            var container = new Container();

            // Register your types, for instance:
            container.Register<IPreferencesRepository, PreferencesRepository>(Lifestyle.Singleton);
            container.Register<IStreamRepository, StreamRepository>(Lifestyle.Singleton);

            // Register your windows and view models:
            container.Register<MainWindow>();
            container.Register<MainWindowViewModel>();

            container.Verify();

            return container;
        }

        private static void RunApplication(Container container)
        {
            try
            {
                var app = new App();
                var mainWindow = container.GetInstance<MainWindow>();
                MainWindowViewModel viewModel = container.GetInstance<MainWindowViewModel>();
                mainWindow.DataContext = viewModel;
                app.Run(mainWindow);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

主窗口.xaml

<Window x:Class="MyApp.Desktop.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyApp.Desktop"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="350" Width="525"
        xmlns:views="clr-namespace:MyApp.Desktop.Views">
    <Grid>
        <ContentControl Content="{Binding CurrentPageViewModel}" />
    </Grid>
</Window>

MainWindowViewModel.cs

namespace MyApp.Desktop.Views
{
    public class MainWindowViewModel : BaseViewModel
    {
        private IPageViewModel _currentPageViewModel;
        private List<IPageViewModel> _pageViewModels;

        public List<IPageViewModel> PageViewModels
        {
            get
            {
                if (_pageViewModels == null)
                    _pageViewModels = new List<IPageViewModel>();

                return _pageViewModels;
            }
        }

        public IPageViewModel CurrentPageViewModel
        {
            get
            {
                return _currentPageViewModel;
            }
            set
            {
                _currentPageViewModel = value;
                OnPropertyChanged("CurrentPageViewModel");
            }
        }

        private void ChangeViewModel(IPageViewModel viewModel)
        {
            if (!PageViewModels.Contains(viewModel))
                PageViewModels.Add(viewModel);

            CurrentPageViewModel = PageViewModels
                .FirstOrDefault(vm => vm == viewModel);
        }

        private void OnGo1Screen(object obj)
        {
            ChangeViewModel(PageViewModels[0]);
        }

        private void OnGo2Screen(object obj)
        {
            ChangeViewModel(PageViewModels[1]);
        }
        private void OnGo3Screen(object obj)
        {
            ChangeViewModel(PageViewModels[2]);
        }

        public MainWindowViewModel(IStreamRepository streamRepository)
        {
            // Add available pages and set page
            PageViewModels.Add(new SimulationCaseViewModel(streamRepository));
            PageViewModels.Add(new StreamsViewModel());
            PageViewModels.Add(new UserControl3ViewModel());

            CurrentPageViewModel = PageViewModels[0];

            Mediator.Subscribe("GoTo1Screen", OnGo1Screen);
            Mediator.Subscribe("GoTo2Screen", OnGo2Screen);
            Mediator.Subscribe("GoTo3Screen", OnGo3Screen);
        }
    }
}

SimulationCaseView.xaml

<UserControl x:Class="MyApp.Desktop.Views.SimulationCaseView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:MyApp.Desktop"
             mc:Ignorable="d"
             d:DesignHeight="280" d:DesignWidth="280">
    <Grid>
        <Button
            Content="Go to Streams"
            Command="{Binding GoTo2}"
            Width="90" Height="30" Margin="166,220,24,30">
        </Button>
    </Grid>
</UserControl>

SimulationCaseViewModel.cs

namespace MyApp.Desktop.Views
{
    public class SimulationCaseViewModel : BaseViewModel, IPageViewModel
    {
        private ICommand _goTo2;
        private readonly IStreamRepository _repo;
        public SimulationCaseViewModel(IStreamRepository repo)
        {
            _repo = repo;
            Application application = _repo.GetApplicationReference();
            CurrentSimulationCases = new ObservableCollection<SimulationCase>();
            Streams = new ObservableCollection<object>();
            foreach (SimulationCase simulationCase in application.SimulationCases)
            {
                CurrentSimulationCases.Add(simulationCase);
            }
            //FetchStreams = new RelayCommand(OnFetch);
        }

        public ObservableCollection<SimulationCase> CurrentSimulationCases { get; set; }
        public ObservableCollection<object> Streams { get; private set; }

        public ICommand GoTo2
        {
            get
            {
                return _goTo2 ?? (_goTo2 = new RelayCommand(x =>
                {
                    Mediator.Notify("GoTo2Screen", "");
                }));
            }
        }
    }
}

任何有关按钮为什么不起作用的帮助表示赞赏。谢谢。

标签: c#wpfxamlmvvmsimple-injector

解决方案


如何解决?

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

CommandManager.InvalidateRequerySuggested();

为什么不更新?

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

  • KeyUp
  • MouseUp
  • GotKeyboardFocus
  • LostKeyboardFocus

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

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

  • 长按时重复增加RepeatButton
  • DataGrid...
  • SinglePageViewer...

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

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

其他信息

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

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


推荐阅读