WPF + Замок Виндзор + MVVM: Локатор-DataContext - PullRequest
1 голос
/ 05 мая 2011

Редактировать:
Я нашел один способ сделать это, но я не уверен, что это лучший способ.
При WindsorContainer инициализации, сначала я регистрирую viewmodel:

container.Register(Component.For<CentrosViewModel>().LifeStyle.Transient);

и позже я регистрирую View

        container.Register(Component.For<CentrosAdminView>().LifeStyle.Transient.DependsOn(Property.ForKey("DataContext")
            .Eq(ViewModelLocator.Centrosviewmodel)));

И определение свойства ViewModelLocator.Centrosviewmodel:

    public static CentrosModel Centrosviewmodel
    {
        get
        {
            return App.container.Resolve<CentrosViewModel>();
        }
    }

End Edit

Я пытаюсь создать приложение Wpf, используя Castle Windsor и Mvvm Toolkit (galasoft), но я думаю, что моя проблема будет одинаковой с любым инструментарием MVVM.

С MVVM вы должны установить DataContextВид на вашу ViewModel.Обычно это делается примерно так в объявлении представления

DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}}

Локатор ресурса определяется в App.xaml следующим образом:

<Application.Resources>
    <!--Global View Model Locator-->
    <vm:ViewModelLocator x:Key="Locator" />
</Application.Resources>

Если я устанавливаю StartupURI в App.xamlНа мой взгляд, все в порядке.Но если я оставляю StartupUri пустым и пытаюсь получить экземпляр моего представления через замок, используя следующий синтаксис:

container.Resolve<CentrosAdminView>().Show();

, я получаю исключение: "Cannot Find Resource with Name '{Locator}'

Я предполагаю, что Initial DataContextотличается при работе напрямую, чем при работе через Castle Windsor, и по этой причине он не может найти ресурс.

У меня два вопроса:

  • Нужно ли иметь ViewModelLocator, когдаиспользуя замок Виндзор?В
  • в случае Да: как правильно настроить DataContext of Views с
  • Windsor?В случае «Нет»: Как бы вы поступили правильно?

Я оставляю свою Конфигурацию Замка.Любая помощь могла бы быть полезна.

Моя конфигурация Windsor выглядит следующим образом:

<castle>
    <properties>
      <!-- SQL Server settings -->
      <connectionString>Server=192.168.69.69;Database=GIOFACTMVVM;user id=sa;password=22336655</connectionString>
      <nhibernateDriver>NHibernate.Driver.SqlClientDriver</nhibernateDriver>
      <nhibernateDialect>NHibernate.Dialect.MsSql2005Dialect</nhibernateDialect>
    </properties>

    <facilities>

      <facility id="nhibernatefacility"
                type="Repository.Infrastructure.ContextualNHibernateFacility, Repository">

        <factory id="sessionFactory1">
          <settings>
            <item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
            <item key="connection.driver_class">#{nhibernateDriver}</item>
            <item key="connection.connection_string">#{connectionString}</item>
            <item key="dialect">#{nhibernateDialect}</item>
            <item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
          </settings>
          <assemblies>
            <assembly>Domain</assembly>
            <assembly>ObservableCollections</assembly>
          </assemblies>
        </factory>

      </facility>
    </facilities>
  </castle>

и по коду:

    public static IWindsorContainer Start()
    {
        var container = new WindsorContainer(new XmlInterpreter());

        container.AddFacility<TransactionFacility>();

        container.Register(
            Component.For<HistoriasAdminView>().LifeStyle.Transient,
            Component.For<HistoriasModel>().LifeStyle.Transient,
            Component.For<CentrosModel>().LifeStyle.Transient,
            Component.For<CentrosAdminView>().LifeStyle.Transient, 
            Component.For<MainViewModel>().LifeStyle.Transient,
            Component.For<MainWindow>().LifeStyle.Transient, 

            Component.For<IRepository<Historias>>().ImplementedBy<Repository<Historias>>().LifeStyle.Transient, 
            Component.For<IRepository<Centros>>().ImplementedBy<Repository<Centros>>().LifeStyle.Transient, 
            Component.For<IUnitOfWork>().ImplementedBy<NHUnitOfWork>().LifeStyle.Transient 
            );

        return container;
    }

1 Ответ

9 голосов
/ 10 мая 2011

Вы используете шаблон Service Locator, в котором вы регистрируете сервисы, передаете ссылку на контейнер и явно вызываете resol для всего вашего кода.Если вы совершите быструю экскурсию по Castle Windsor wiki , они не рекомендуют использовать этот контейнер.

Как правило, вы должны зарегистрировать все типы (через установщики), разрешить только один корневой объект.(может быть, ваш основной вид, может быть, какой-то код стиля запуска / контроллера MVC), и пусть все остальное будет разрешено контейнером.В следующий раз, когда вы вызовете контейнер, почти всегда будет container.Dispose, когда ваше приложение завершится.

См. Вики-страницу Windsor, касающуюся шаблона трех вызовов .

Если вы обнаружите случаи, когда вам нужно извлечь из контейнера во время выполнения для создания конкретных экземпляров (где вы должны передать определенные параметры для создания этого экземпляра), используйте Типизированное средство фабрики вместо непосредственного разрешения зависимостей.

MVVM с Windsor:

В моем первом приложении MVVM, которое я написал с помощью Windsor (только что закончил первую версию), я просто зарегистрировал свой основной вид и модель вида без указанияобраз жизни.По умолчанию они были одиночными.

Представление взяло экземпляр модели представления в качестве обязательной зависимости (в конструкторе) и использовало его для установки контекста данных.Я сделал это с помощью code-behind, потому что это было ненавязчиво и безболезненно.

// In my program I used interfaces for everything.  You don't actually have to...
public interface IMainView
{
    void Show();
}

public class MainView : Window, IMainView
{
    public MainView(IMainViewModel viewModel)
    {
        Initialize();
        this.DataContext = viewModel;
    }
}

public interface IMainViewModel
{
    int SomeProperty { get; set; }
    ICommand ShowSubViewCommand { get; }
    // ...
}

public class MainViewModel : IMainViewModel
{
    public MainViewModel(SomeOtherSubComponent subComponent)
    {
        this.subComponent = subComponent;
        // ...
    }

    // ...
}

Там, где у меня были подвиги, для которых я хотел создать несколько экземпляров, я регистрировал их с переходным жизненным циклом.Затем я создал фабрику представлений и фабрику моделей представлений и использовал их для получения экземпляров вложенного представления и вспомогательной модели представления из родительского представления.Я зарегистрировал обработчик событий для события закрытия представления и вызвал метод Release на фабричных классах.

public interface ISubView
{
    void Show();
    event Action OnDismissed;
}

public class SubView : Window, ISubView
{
    public SubView(ISubViewModel viewModel)
    {
        Initialize();
        this.DataContext = viewModel;
        // Would do this in the view model,
        // but it is a pain to get Window.Close to call an ICommand, ala MVVM
        this.OnClose += (s, a) => RaiseDismissed();
    }

    public event Action OnDismissed;

    private void RaiseDismissed()
    {
        if(OnDismissed != null)
            OnDismissed();
    }
}

public interface ISubViewModel
{
    string SomeProperty { get; }
    // ...
}

// Need to create instances on the fly, so using Typed Factory facility.
// The facility implements them, so we don't have to :)
public interface IViewFactory
{
    ISubView GetSubView(ISubViewModel viewModel);
    void Release(ISubView view);
}

public interface IViewModelFactory
{
    ISubViewModel GetSubViewModel();
    void Release(ISubViewModel viewModel);
}

// Editing the earlier class for an example...
public class MainViewModel : IMainViewModel
{
    public MainViewModel(IViewFactory viewFactory, IViewModelFactory viewModelFactory)
    {
        this.viewFactory = viewFactory;
        this.viewModelFactory = viewModelFactory;
        // Todo: Wire up ShowSubViewCommand to call CreateSubView here in ctor
    }

    public ICommand ShowSubViewCommand { get; private set; }

    private void CreateSubView()
    {
        var viewModel = viewModelFactory.GetSubViewModel();
        var view = viewFactory.GetSubView(viewModel);

        view.OnDismissed += () =>
        {
            viewModelFactory.Release(viewModel);
            viewFactory.Release(view);
        };

        view.Show();
    }

    // Other code, private state, etc...
}

В конце всего этого единственные вызовы контейнера:

void App_Startup()
{
    this.container = new WindsorContainer();
    container.Install(Configuration.FromAppConfig());

    var mainView = container.Resolve<IMainView>();
    mainView.Show();
}

public override OnExit()
{
    container.Dispose();
}

Преимущество всей этой оснастки заключается в том, что она не зависит от контейнера (и может использоваться без контейнера), очевидно, какие зависимости принимает каждый из моих компонентов, а большая часть моего кода никогда не используется.должны спросить о его зависимостях.Зависимости просто передаются каждому компоненту по мере необходимости.

...