WPF + Castle Windsor + MVVM: Locator-DataContext

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

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>();
        }
    }

Конец редактирования

Я пытаюсь создать приложение 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 при использовании Castle Windsor? В
  • случай Да: как я могу правильно настроить DataContext of Views с помощью
  • Виндзор? В случае «Нет»: Каким будет правильный путь?

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

Моя конфигурация 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;
    }

person user740155    schedule 05.05.2011    source источник


Ответы (1)


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

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

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

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

MVVM с Windsor:

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

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

// 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();
}

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

person Merlyn Morgan-Graham    schedule 10.05.2011
comment
В вашем конкретном случае вы можете использовать ссылки на свои Model компоненты в качестве параметров или свойств конструктора для каждого ViewModel. Тогда вам не нужен этот класс локатора. - person Merlyn Morgan-Graham; 10.05.2011
comment
Вы не можете начинать имя своих фабричных методов с Get. См. groups.google.com/forum/#!topic. / castle-project-users / - person reggaeguitar; 20.06.2014
comment
@reggaeguitar: Кажется, это новое требование (?) В любом случае, если у вас есть исправления для кода в моем ответе, не стесняйтесь редактировать его. - person Merlyn Morgan-Graham; 20.06.2014
comment
Я разместил здесь вопрос stackoverflow.com/questions/24318313/, если бы вы могли ответить на него, я был бы очень благодарен - person reggaeguitar; 20.06.2014
comment
@reggaeguitar - не добрался до редактирования достаточно быстро, чтобы его не отклонили другие. FWIW, я не согласен с людьми, которые его отвергли: P - person Merlyn Morgan-Graham; 20.06.2014
comment
Вы должны его отредактировать. Castle Windsor не дает вам дружественного сообщения об ошибке, я возился около часа, прежде чем я наконец погуглил и понял это - person reggaeguitar; 21.06.2014