首页 > 解决方案 > 从视图模型中的按钮单击调用第二个视图

问题描述

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

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

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

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

视图模型项目

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
}

标签: wpfmvvmcastle-windsor

解决方案


您当前使用的方式Castle.Windsor更像是 ServiceLocator,而不是 IOC 容器。
我建议您使用类型化工厂来解析您的类。对于工厂,您不需要 a public static WindsorContainer,因为您只调用Resolve一次(另请参阅Three-Calls-Pattern)。

首先使用Bootstrapper您调用IWindsorInstallers 的 -Class 并注册使用TypedFactoryFacility

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

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

您可以BootstrapContainer()从您的MainView或从您的App.xaml.cs.

现在为您创建一个工厂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(). 有关详细信息,请参阅此链接

要创建你的ViewModel,你可以Resolve在你打电话给你的位置打电话Bootstrapper并获得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现在打开另一个窗口,你可以很容易地做到这一点。
首先为您的第二个视图模型创建一个工厂:

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现在可以将new设置为您的构造函数的参数ViewModel

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

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

温莎ISecondViewModelFactory为您解决了问题,因此您不必关心它。
然后,您可以轻松地创建一个SecondViewModel新窗口并显示一个新窗口,并将其SecondViewModel作为您的DataContext

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

推荐阅读