从视图模型中的按钮单击调用第二个视图

Invoking second view from button click in view model

尝试使用 WPF 和 MVVM 设计模式来完成 Castle Windsor。我的解决方案中有三个项目,即 MainView、ViewModel 和 IoCInstaller 项目。

MainView 依赖于 IoCInstaller 和 ViewModel。 IoCInstaller 依赖于 ViewModel。

现在我想从 ViewModel 中的按钮单击操作调用 SecondView,但基于依赖关系,我发现这很难。我知道我的 castle windsor 实现并不理想,但我没有找到关于它的理想实现方式的任何好的答案。任何建议都会很有帮助。

阅读文档并通过网络搜索后,我完成了基本的城堡实现工作,但是当涉及到 WPF 和 MVVM 时,我被卡住了。

ViewModel 项目

public partial class MainView : Window
{
    public MainView()
    {
        InitializeComponent();

        var iocContainer = IoCInstaller.WindsorContainer;
        iocContainer.Install(new IoCInstaller());

        var mainWindowViewModel = iocContainer.Resolve<IMainWindowViewModel>();
        mainWindowViewModel.Initialize(iocContainer);
        DataContext = mainWindowViewModel;
    }
}

IoCInstaller 项目

public class IoCInstaller : IWindsorInstaller
{
    private static WindsorContainer _windsorContainer;
    public static WindsorContainer WindsorContainer
    {
        get
        {
            if (_windsorContainer == null)
            {
                _windsorContainer = new WindsorContainer();
                return _windsorContainer;
            }
            else
            {
                return _windsorContainer;
            }
        }
    }

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Castle.MicroKernel.Registration.Component.For<IViewModel>());
    }
}

最后是 ViewModel 项目

public class ViewModel : INotifyPropertyChanged, IViewModel 
{
    public ViewModel()
    {
    }

    public void Initialize(WindsorContainer windsorContainer)
    {
        Logger.Debug("Initializing main view model ");
        iocContainer = windsorContainer;
    }

    public void MyActionForBttnClicked()
    {
        //invoke second view
    }

    //Other stuff
}

您当前使用的方式 Castle.Windsor 更像是一个 ServiceLocator 而不是 IOC 容器。
我建议您使用 typed factories to resolve your classes. With factories you don't need a public static WindsorContainer because you only call Resolve once (also see the Three-Calls-Pattern).

首先使用 Bootstrapper-Class 调用 IWindsorInstaller 并注册 TypedFactoryFacility:

public class Bootstrapper
{
    public IWindsorContainer BootstrapContainer()
    {
        IWindsorContainer container = new WindsorContainer();
        container.AddFacility<TypedFactoryFacility>();

        return container.Install(new IocInstaller());
    }
}

您可以通过 MainViewApp.xaml.cs 调用 BootstrapContainer()

现在为您的 ViewModel 创建一个工厂:

public interface IViewModelFactory
{
    IViewModel Create();

    void Release(IViewModel viewModel);
}

您不需要实现这个工厂,Windsor 会为您完成。
您唯一需要做的就是更改您的 IocInstaller 并注册您的 IViewModelFactory:

public class IocInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<IViewModel>().ImplementedBy<MainWindowViewModel>().LifestyleTransient());

        container.Register(Component.For<IViewModelFactory>().AsFactory());
    }
}

确保您熟悉可用的 Lifestyles。默认值为 LifestyleSingleton(),而您可能想使用 LifestyleTransient()。有关详细信息,请参阅 this link

要创建您的 ViewModel,您可以在调用 Bootstrapper 的位置调用 Resolve 并获得 IViewModelFactory:

public MainView()
{
    //other stuff

    Bootstrapper bootstrapper = new Bootstrapper();
    IWindsorContainer container = bootstrapper.BootstrapContainer();

    IViewModelFactory viewModelFactory = container.Resolve<IViewModelFactory>();
    IViewModel viewModel = viewModelFactory.Create();

    DataContext = viewModel;
}

如果你现在想从你的 ViewModel 打开另一个 window,你可以很容易地做到这一点。
首先为你的第二个视图模型创建一个工厂:

public interface ISecondViewModelFactory
{
    ISecondViewModel Create();

    void Release(ISecondViewModel secondViewModel);
}

然后在你的IocInstaller中注册新的类:

public class IocInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        //registration for ViewModel

        container.Register(Component.For<ISecondViewModel>().ImplementedBy<SecondViewModel>().LifestyleTransient());

        container.Register(Component.For<ISecondViewModelFactory>().AsFactory());
    }
}

现在可以将新的 ISecondViewModelFactory 设置为 ViewModel 的构造函数的参数:

public class ViewModel : INotifyPropertyChanged, IViewModel 
{
    private readonly ISecondViewModelFactory _secondViewModelFactory;

    public ViewModel(ISecondViewModelFactory secondViewModelFactory)
    {
        _secondViewModelFactory = secondViewModelFactory;
    }
}

ISecondViewModelFactory由温莎为您解决,所以您不必关心它。
然后,您可以轻松地创建一个新的 SecondViewModel 并显示一个新的 window,其中 SecondViewModel 作为您的 DataContext:

public void MyActionForBttnClicked()
{
    //invoke second view
    ISecondViewModel secondViewModel = _secondViewModelFactory.Create();
    SecondWindow secondWindow = new SecondWindow();
    secondWindow.DataContext = secondViewModel;
    secondWindow.ShowDialog();
}