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

时间:2019-04-16 04:42:03

标签: wpf mvvm castle-windsor

尝试使用WPF和MVVM设计模式遍历温莎城堡。我的解决方案中有三个项目,即MainView,ViewModel和IoCInstaller项目。

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

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

阅读文档并通过网络搜索后,我完成了基本的城堡实现工作,但在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
}

1 个答案:

答案 0 :(得分:2)

您当前使用Castle.Windsor的方式更像是ServiceLocator,而不是IOC容器。
我建议您使用typed factories来解决您的课程。在工厂中,您不需要public static WindsorContainer,因为您只需调用Resolve一次(另请参阅Three-Calls-Pattern)。

首先使用Bootstrapper类,在其中调用IWindsorInstaller,并注册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);
}

您不需要实现这个工厂,温莎会为您实现。
您唯一需要做的就是更改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,您可以在致电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设置为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();
}