Вызов второго вида из нажатия кнопки в представлении модели - PullRequest
2 голосов
/ 16 апреля 2019

Попытка работать через Castle Windsor с шаблоном проектирования WPF и MVVM. У меня есть три проекта в моем решении, то есть MainView, ViewModel и IoCInstaller Project.

MainView зависит от IoCInstaller и ViewModel. IoCInstaller зависит от ViewModel.

Теперь я хочу вызвать SecondView из действия, связанного с нажатием кнопки, во ViewModel, но, основываясь на зависимостях, мне сложно. Я знаю, что моя реализация в Виндзоре не идеальна, но я не нахожу никакого хорошего ответа на идеальный способ ее реализации. Любые предложения будут действительно полезны.

После прочтения документации и поиска в Интернете я сделал базовую работу по созданию замка, но когда дело доходит до 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 Ответ

2 голосов
/ 16 апреля 2019

То, как вы используете Castle.Windsor, больше похоже на ServiceLocator, чем на контейнер IOC.
Я бы посоветовал вам использовать типизированные фабрики для разрешения ваших классов. На фабриках вам не нужно public static WindsorContainer, потому что вы звоните Resolve только один раз (см. Также шаблон Three-Calls ).

Начните с использования класса Bootstrapper, где вы звоните своим IWindsorInstaller s, и зарегистрируйте использование 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(). Для получения дополнительной информации см. эту ссылку .

Чтобы создать 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();
}
...