Как обрабатывать внедрение зависимостей в приложении WPF / MVVM


104

Я запускаю новое настольное приложение и хочу создать его с помощью MVVM и WPF.

Я также собираюсь использовать TDD.

Проблема в том, что я не знаю, как мне использовать контейнер IoC для внедрения моих зависимостей в мой производственный код.

Предположим, у меня есть следующий класс и интерфейс:

public interface IStorage
{
    bool SaveFile(string content);
}

public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

И затем у меня есть еще один класс, который имеет IStorageзависимость, предположим также, что этот класс является ViewModel или бизнес-классом ...

public class SomeViewModel
{
    private IStorage _storage;

    public SomeViewModel(IStorage storage){
        _storage = storage;
    }
}

Благодаря этому я могу легко писать модульные тесты, чтобы убедиться, что они работают должным образом, используя макеты и т. Д.

Проблема в том, когда дело доходит до использования его в реальном приложении. Я знаю, что у меня должен быть контейнер IoC, который связывает реализацию IStorageинтерфейса по умолчанию , но как мне это сделать?

Например, как бы было, если бы у меня был следующий xaml:

<Window 
    ... xmlns definitions ...
>
   <Window.DataContext>
        <local:SomeViewModel />
   </Window.DataContext>
</Window>

Как я могу правильно «сказать» WPF внедрять зависимости в этом случае?

Кроме того, предположим, что мне нужен экземпляр SomeViewModelиз моего кода C #, как мне это сделать?

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

Я знаком со StructureMap, но не эксперт. Кроме того, дайте мне знать, если есть более эффективный / простой / нестандартный фреймворк.


Предварительная версия .net core 3.0 позволяет сделать это с некоторыми пакетами Microsoft nuget.
Bailey Miller

Ответы:


88

Я использовал Ninject и обнаружил, что с ним приятно работать. Все настроено в коде, синтаксис довольно прост и имеет хорошую документацию (и множество ответов по SO).

В основном это выглядит так:

Создайте модель представления и возьмите IStorageинтерфейс в качестве параметра конструктора:

class UserControlViewModel
{
    public UserControlViewModel(IStorage storage)
    {

    }
}

Создайте ViewModelLocatorсо свойством get для модели представления, которое загружает модель представления из Ninject:

class ViewModelLocator
{
    public UserControlViewModel UserControlViewModel
    {
        get { return IocKernel.Get<UserControlViewModel>();} // Loading UserControlViewModel will automatically load the binding for IStorage
    }
}

Сделайте ViewModelLocatorресурс приложения в App.xaml:

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

Свяжите DataContextиз UserControlк соответствующему свойству в ViewModelLocator.

<UserControl ...
             DataContext="{Binding UserControlViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
    </Grid>
</UserControl>

Создайте класс, наследующий NinjectModule, который установит необходимые привязки ( IStorageи модель просмотра):

class IocConfiguration : NinjectModule
{
    public override void Load()
    {
        Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

        Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
    }
}

Инициализируйте ядро ​​IoC при запуске приложения с необходимыми модулями Ninject (пока что выше):

public partial class App : Application
{       
    protected override void OnStartup(StartupEventArgs e)
    {
        IocKernel.Initialize(new IocConfiguration());

        base.OnStartup(e);
    }
}

Я использовал статический IocKernelкласс для хранения экземпляра ядра IoC для всего приложения, поэтому при необходимости могу легко получить к нему доступ:

public static class IocKernel
{
    private static StandardKernel _kernel;

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static void Initialize(params INinjectModule[] modules)
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(modules);
        }
    }
}

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

Если у кого-то есть способ получше, поделитесь, пожалуйста.

РЕДАКТИРОВАТЬ: Lucky Likey предоставил ответ, чтобы избавиться от статического локатора сервисов, позволив Ninject создавать экземпляры классов пользовательского интерфейса. Подробности ответа можно увидеть здесь


13
Я новичок в внедрении зависимостей, но, по сути, ваше решение объединяет антипаттерн Service Locator с Ninject, поскольку вы используете статический ViewModel Locator. Можно утверждать, что инъекция выполняется в файле Xaml, который с меньшей вероятностью будет протестирован. У меня нет лучшего решения, и я, вероятно, воспользуюсь вашим, но я думаю, что было бы полезно упомянуть и об этом в ответе.
user3141326

Человек ваше решение просто замечательно, есть только одна «проблема» с помощью следующей строки: DataContext="{Binding [...]}". Это заставляет VS-Designer выполнять весь программный код в конструкторе ViewModel. В моем случае Window выполняется и модально блокирует любое взаимодействие с VS. Возможно, следует изменить ViewModelLocator, чтобы не находить «настоящие» модели представления во время разработки. - Еще одно решение - «Отключить код проекта», что также предотвратит отображение всего остального. Возможно, вы уже нашли изящное решение этой проблемы. В таком случае я прошу вас показать это.
LuckyLikey

@LuckyLikey Вы можете попробовать использовать d: DataContext = "{d: DesignInstance vm: UserControlViewModel, IsDesignTimeCreatable = True}", но я не уверен, что это имеет значение. Но почему / как конструктор виртуальной машины запускает модальное окно? А какое окно?
sondergard

@son На самом деле я не знаю, почему и как, но когда я открываю конструктор окон из обозревателя решений, когда открывается новая вкладка, окно отображается дизайнером, и такое же окно появляется, как если бы модальный режим отладки, размещается в новом процессе вне VS "Micorosoft Visual Studio XAML Designer". Если процесс завершен, VS-Designer также откажет из-за вышеупомянутого исключения. Я собираюсь попробовать ваш обходной путь. Я
сообщу

1
@sondergard Я опубликовал улучшение вашего ответа, избегая антипаттерна ServiceLocator. Не стесняйтесь проверить это.
LuckyLikey

52

В своем вопросе вы устанавливаете значение DataContextсвойства представления в XAML. Для этого требуется, чтобы ваша модель представления имела конструктор по умолчанию. Однако, как вы заметили, это не работает с внедрением зависимостей, когда вы хотите внедрить зависимости в конструктор.

Таким образом, вы не можете установить DataContextсвойство в XAML . Вместо этого у вас есть другие альтернативы.

Если ваше приложение основано на простой иерархической модели представления, вы можете построить всю иерархию модели представления при запуске приложения (вам нужно будет удалить StartupUriсвойство из App.xamlфайла):

public partial class App {

  protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);
    var container = CreateContainer();
    var viewModel = container.Resolve<RootViewModel>();
    var window = new MainWindow { DataContext = viewModel };
    window.Show();
  }

}

Это основано на графе объектов моделей представлений с корнем, RootViewModelно вы можете внедрить некоторые фабрики моделей представления в родительские модели представления, позволяя им создавать новые дочерние модели представления, поэтому граф объекта не нужно фиксировать. Это также, надеюсь, ответит на ваш вопрос. Предположим, мне нужен экземпляр SomeViewModelиз моего csкода, как мне это сделать?

class ParentViewModel {

  public ParentViewModel(ChildViewModelFactory childViewModelFactory) {
    _childViewModelFactory = childViewModelFactory;
  }

  public void AddChild() {
    Children.Add(_childViewModelFactory.Create());
  }

  ObservableCollection<ChildViewModel> Children { get; private set; }

 }

class ChildViewModelFactory {

  public ChildViewModelFactory(/* ChildViewModel dependencies */) {
    // Store dependencies.
  }

  public ChildViewModel Create() {
    return new ChildViewModel(/* Use stored dependencies */);
  }

}

Если ваше приложение более динамично по своей природе и, возможно, основано на навигации, вам придется подключиться к коду, который выполняет навигацию. Каждый раз, когда вы переходите к новому представлению, вам необходимо создать модель представления (из контейнера DI), само представление и установить DataContextдля представления модель представления. Сначала вы можете сделать это представление, где вы выбираете модель представления на основе представления, или вы можете сначала сделать это модель представлениягде модель просмотра определяет, какой вид использовать. Платформа MVVM предоставляет эту ключевую функциональность с помощью некоторого способа подключить контейнер DI к созданию моделей представления, но вы также можете реализовать его самостоятельно. Я здесь немного расплывчат, потому что в зависимости от ваших потребностей эта функция может стать довольно сложной. Это одна из основных функций, которые вы получаете от фреймворка MVVM, но использование собственной в простом приложении даст вам хорошее представление о том, что фреймворки MVVM предоставляют под капотом.

Не имея возможности объявить DataContextв XAML, вы теряете некоторую поддержку во время разработки. Если ваша модель представления содержит некоторые данные, они появятся во время разработки, что может быть очень полезно. К счастью, вы можете использовать атрибуты времени разработки и в WPF. Один из способов сделать это - добавить следующие атрибуты к <Window>элементу или <UserControl>в XAML:

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MyViewModel, IsDesignTimeCreatable=True}"

Тип модели представления должен иметь два конструктора: по умолчанию для данных времени разработки и еще один для внедрения зависимости:

class MyViewModel : INotifyPropertyChanged {

  public MyViewModel() {
    // Create some design-time data.
  }

  public MyViewModel(/* Dependencies */) {
    // Store dependencies.
  }

}

Таким образом вы можете использовать внедрение зависимостей и сохранить хорошую поддержку во время разработки.


13
Это ИМЕННО то, что я искал. Меня расстраивает, сколько раз я читаю ответы, в которых говорится: «Просто используйте фреймворк [ yadde-ya ]». Это все хорошо, но я хочу точно знать, как сначала сделать это сам, а затем я смогу узнать, какой вид фреймворка может мне действительно пригодиться. Спасибо, что так ясно это изложили.
kmote

28

То, что я публикую здесь, является улучшением ответа sondergard, потому что то, что я собираюсь рассказать, не вписывается в комментарий :)

Фактически, я представляю изящное решение, которое позволяет избежать использования ServiceLocator и оболочки для StandardKernelэкземпляра -Instance, который вызывается в решении sondergard IocContainer. Зачем? Как уже упоминалось, это антипаттерны.

Сделать StandardKernelдоступным везде

Ключ к магии Ninject - это StandardKernel-Instance, который необходим для использования .Get<T>()-Method.

В качестве альтернативы sondergard IocContainerвы можете создать StandardKernelвнутри класса -Class App.

Просто удалите StartUpUri из вашего App.xaml

<Application x:Class="Namespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
             ... 
</Application>

Это CodeBehind приложения внутри App.xaml.cs

public partial class App
{
    private IKernel _iocKernel;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        _iocKernel = new StandardKernel();
        _iocKernel.Load(new YourModule());

        Current.MainWindow = _iocKernel.Get<MainWindow>();
        Current.MainWindow.Show();
    }
}

Отныне Ninject жив и готов к бою :)

Введение вашего DataContext

Пока Ninject жив, вы можете выполнять все виды инъекций, например, Property Setter Injection или наиболее распространенный Constructor Injection .

Это, как Вы вводите свой ViewModel в ваши WindowDataContext

public partial class MainWindow : Window
{
    public MainWindow(MainWindowViewModel vm)
    {
        DataContext = vm;
        InitializeComponent();
    }
}

Конечно, вы также можете Inject, IViewModelесли выполняете правильные привязки, но это не часть этого ответа.

Доступ к ядру напрямую

Если вам нужно вызвать методы ядра напрямую (например, .Get<T>()-Method), вы можете позволить ядру внедрить себя.

    private void DoStuffWithKernel(IKernel kernel)
    {
        kernel.Get<Something>();
        kernel.Whatever();
    }

Если вам понадобится локальный экземпляр ядра, вы можете ввести его как свойство.

    [Inject]
    public IKernel Kernel { private get; set; }

Хотя это может быть очень полезно, я бы не рекомендовал вам это делать. Просто обратите внимание, что объекты, введенные таким образом, не будут доступны внутри Конструктора, потому что он будет введен позже.

Согласно этой ссылке вы должны использовать factory-Extension вместо того, чтобы вводить IKernel(DI Container).

Рекомендуемый подход к использованию контейнера DI в программной системе заключается в том, что корневой компонент приложения должен быть единственным местом, где происходит непосредственное прикосновение к контейнеру.

Как Ninject.Extensions.Factory будет использоваться также может быть красным здесь .


Хороший подход. Никогда не исследовал Ninject до этого уровня, но я вижу, что
упускаю

@son спасибо. В конце вашего ответа вы заявили, что если у кого-то есть способ получше, поделитесь, пожалуйста. Не могли бы вы добавить ссылку на это?
LuckyLikey

если кому-то интересно, как это использовать Ninject.Extensions.Factory, укажите это здесь, в комментариях, и я добавлю дополнительную информацию.
LuckyLikey

1
@LuckyLikey: Как я могу добавить ViewModel в текст окна данных через XAML, у которого нет конструктора без параметров? С решением от sondergard с ServiceLocator такая ситуация возможна.
Thomas Geulen 01

Скажите, пожалуйста, как получить нужные мне услуги в прикрепленных свойствах? Они всегда статичны, как DependencyPropertyполе поддержки, так и его методы Get и Set.
springy76

12

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

public class SomeView
{
    public SomeView(SomeViewModel viewModel)
    {
        InitializeComponent();

        DataContext = viewModel;
    }
}

Это заменяет ваш подход на основе XAML.

Я использую платформу Prism для обработки навигации - когда некоторый код запрашивает отображение определенного представления (путем «перехода» к нему), Prism разрешает это представление (внутренне, используя структуру DI приложения); каркас DI, в свою очередь, разрешит любые зависимости, которые имеет представление (модель представления в моем примере), затем разрешит его зависимости и так далее.

Выбор структуры DI в значительной степени не имеет значения, поскольку все они, по сути, делают одно и то же, то есть вы регистрируете интерфейс (или тип) вместе с конкретным типом, который вы хотите, чтобы структура создавала экземпляр, когда обнаруживает зависимость от этого интерфейса. Для записи я использую Castle Windsor.

Навигация с помощью Prism требует некоторого привыкания, но она довольно хороша, когда вы начинаете разбираться в ней, позволяя вам составлять свое приложение с использованием различных представлений. Например, вы можете создать «область» Prism в своем главном окне, а затем, используя навигацию Prism, переключаться с одного представления на другое в этой области, например, когда пользователь выбирает пункты меню или что-то еще.

В качестве альтернативы взгляните на одну из фреймворков MVVM, например MVVM Light. У меня нет опыта в этом, поэтому не могу комментировать, как они используются.


1
Как передать аргументы конструктора дочерним представлениям? Я пробовал этот подход, но получаю исключения в родительском представлении, говорящие мне, что дочернее представление не имеет конструктора без параметров по умолчанию
Доктор Джонс,

10

Установите MVVM Light.

Частью установки является создание локатора модели вида. Это класс, который представляет ваши модели просмотра как свойства. Затем получатель этих свойств может возвращать экземпляры из вашего механизма IOC. К счастью, MVVM light также включает фреймворк SimpleIOC, но вы можете подключиться к другим, если хотите.

С помощью простого IOC вы регистрируете реализацию для типа ...

SimpleIOC.Default.Register<MyViewModel>(()=> new MyViewModel(new ServiceProvider()), true);

В этом примере ваша модель представления создается и передается объект поставщика услуг в соответствии с его конструктором.

Затем вы создаете свойство, которое возвращает экземпляр из IOC.

public MyViewModel
{
    get { return SimpleIOC.Default.GetInstance<MyViewModel>; }
}

Умная часть состоит в том, что локатор модели представления затем создается в app.xaml или его эквиваленте в качестве источника данных.

<local:ViewModelLocator x:key="Vml" />

Теперь вы можете привязать его к свойству MyViewModel, чтобы получить модель просмотра с внедренной службой.

Надеюсь, это поможет. Приносим извинения за любые неточности кода, закодированные из памяти на iPad.


У вас не должно быть начальной загрузки приложения GetInstanceили resolveза ее пределами. В этом суть DI!
Soleil - Матье Прево

Я согласен, что вы можете установить значение свойства во время запуска, но предполагать, что использование ленивого создания экземпляров противоречит DI, неправильно.
kidshaw

@kishaw Я не делал.
Солей - Матье Прево

3

Кейс Canonic DryIoc

Отвечаю на старый пост, но делать это DryIocи делать то, что я считаю хорошим использованием DI и интерфейсов (минимальное использование конкретных классов).

  1. Отправной точкой приложения WPF является App.xaml, и там мы говорим, какое исходное представление использовать; мы делаем это с помощью кода вместо стандартного xaml:
  2. удалить StartupUri="MainWindow.xaml"в App.xaml
  3. в выделенном коде (App.xaml.cs) добавьте следующее override OnStartup:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        DryContainer.Resolve<MainWindow>().Show();
    }

это точка запуска; это также единственное место, куда resolveследует звонить.

  1. корень конфигурации (согласно книге Марка Симана «Внедрение зависимостей в .NET; единственное место, где следует упомянуть конкретные классы») будет находиться в том же самом коде, в конструкторе:

    public Container DryContainer { get; private set; }
    
    public App()
    {
        DryContainer = new Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient());
        DryContainer.Register<IDatabaseManager, DatabaseManager>();
        DryContainer.Register<IJConfigReader, JConfigReader>();
        DryContainer.Register<IMainWindowViewModel, MainWindowViewModel>(
            Made.Of(() => new MainWindowViewModel(Arg.Of<IDatabaseManager>(), Arg.Of<IJConfigReader>())));
        DryContainer.Register<MainWindow>();
    }

Замечания и еще несколько деталей

  • Я использовал конкретный класс только с представлением MainWindow;
  • Мне пришлось указать, какой конструктор использовать (нам нужно сделать это с DryIoc) для ViewModel, потому что конструктор по умолчанию должен существовать для конструктора XAML, а конструктор с внедрением является фактическим, используемым для приложения.

Конструктор ViewModel с DI:

public MainWindowViewModel(IDatabaseManager dbmgr, IJConfigReader jconfigReader)
{
    _dbMgr = dbmgr;
    _jconfigReader = jconfigReader;
}

Конструктор ViewModel по умолчанию для дизайна:

public MainWindowViewModel()
{
}

Программный код представления:

public partial class MainWindow
{
    public MainWindow(IMainWindowViewModel vm)
    {
        InitializeComponent();
        ViewModel = vm;
    }

    public IViewModel ViewModel
    {
        get { return (IViewModel)DataContext; }
        set { DataContext = value; }
    }
}

и что нужно в представлении (MainWindow.xaml), чтобы получить экземпляр проекта с ViewModel:

d:DataContext="{d:DesignInstance local:MainWindowViewModel, IsDesignTimeCreatable=True}"

Вывод

Таким образом, мы получили очень чистую и минимальную реализацию приложения WPF с контейнером DryIoc и DI, сохраняя при этом возможность создания экземпляров представлений и моделей представления.


2

Используйте платформу управляемой расширяемости .

[Export(typeof(IViewModel)]
public class SomeViewModel : IViewModel
{
    private IStorage _storage;

    [ImportingConstructor]
    public SomeViewModel(IStorage storage){
        _storage = storage;
    }

    public bool ProperlyInitialized { get { return _storage != null; } }
}

[Export(typeof(IStorage)]
public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

//Somewhere in your application bootstrapping...
public GetViewModel() {
     //Search all assemblies in the same directory where our dll/exe is
     string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
     var catalog = new DirectoryCatalog(currentPath);
     var container = new CompositionContainer(catalog);
     var viewModel = container.GetExport<IViewModel>();
     //Assert that MEF did as advertised
     Debug.Assert(viewModel is SomViewModel); 
     Debug.Assert(viewModel.ProperlyInitialized);
}

В общем, вам нужно иметь статический класс и использовать Factory Pattern, чтобы предоставить вам глобальный контейнер (кэшированный, natch).

Что касается того, как вводить модели представления, вы вводите их так же, как и все остальное. Создайте конструктор импорта (или поместите оператор импорта в свойство / поле) в коде программной части XAML-файла и скажите ему импортировать модель представления. Затем связать ваш Window«S DataContextк этому свойству. Ваши корневые объекты, которые вы фактически сами извлекаете из контейнера, обычно являются составными Windowобъектами. Просто добавьте интерфейсы в классы окон и экспортируйте их, затем возьмите из каталога, как указано выше (в App.xaml.cs ... это файл начальной загрузки WPF).


Вам не хватает одного важного момента DI, который заключается в том, чтобы избежать создания экземпляров с помощью new.
Soleil - Матье Прево

0

Я бы предложил использовать ViewModel - первый подход https://github.com/Caliburn-Micro/Caliburn.Micro

см. https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions

использовать Castle Windsorкак контейнер IOC.

Все о соглашениях

Одна из основных особенностей Caliburn.Micro проявляется в его способности устранять необходимость в стандартном коде, действуя в соответствии с рядом соглашений. Некоторые люди любят условности, а некоторые их ненавидят. Вот почему соглашения CM полностью настраиваются и даже могут быть полностью отключены при нежелании. Если вы собираетесь использовать соглашения и поскольку они включены по умолчанию, полезно знать, что это за соглашения и как они работают. Это тема данной статьи. Разрешение просмотра (ViewModel-First)

Основы

Первое соглашение, с которым вы, вероятно, столкнетесь при использовании CM, связано с разрешением просмотра. Это соглашение влияет на все области приложения ViewModel-First. В ViewModel-First у нас есть существующая ViewModel, которую нам нужно отобразить на экране. Для этого CM использует простой шаблон именования, чтобы найти UserControl1, который он должен привязать к ViewModel и отобразить. Итак, что это за образец? Давайте просто взглянем на ViewLocator.LocateForModelType, чтобы узнать:

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>{
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    {
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    }

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
        : GetOrCreateViewType(viewType);
};

Давайте сначала проигнорируем переменную «context». Чтобы получить представление, мы предполагаем, что вы используете текст «ViewModel» в именах своих виртуальных машин, поэтому мы просто меняем его на «View» везде, где мы его находим, удаляя слово «Model». В результате меняются как имена типов, так и пространства имен. Таким образом, ViewModels.CustomerViewModel станет Views.CustomerView. Или, если вы организуете свое приложение по функциям: CustomerManagement.CustomerViewModel становится CustomerManagement.CustomerView. Надеюсь, это довольно просто. Получив имя, мы ищем типы с этим именем. Мы ищем любую сборку, которую вы предоставили CM как доступную для поиска через AssemblySource.Instance.2 Если мы находим тип, мы создаем экземпляр (или получаем его из контейнера IoC, если он зарегистрирован) и возвращаем его вызывающей стороне. Если мы не найдем тип,

Теперь вернемся к этому «контекстному» значению. Вот как CM поддерживает несколько представлений по одной и той же ViewModel. Если предоставляется контекст (обычно строка или перечисление), мы выполняем дальнейшее преобразование имени на основе этого значения. Это преобразование фактически предполагает, что у вас есть папка (пространство имен) для различных представлений, путем удаления слова «View» в конце и добавления вместо этого контекста. Итак, с учетом контекста «Master» наша ViewModels.CustomerViewModel станет Views.Customer.Master.


2
Весь ваш пост - это мнение.
Джон Питерс

-1

Удалите URI запуска из вашего app.xaml.

App.xaml.cs

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        IoC.Configure(true);

        StartupUri = new Uri("Views/MainWindowView.xaml", UriKind.Relative);

        base.OnStartup(e);
    }
}

Теперь вы можете использовать свой класс IoC для создания экземпляров.

MainWindowView.xaml.cs

public partial class MainWindowView
{
    public MainWindowView()
    {
        var mainWindowViewModel = IoC.GetInstance<IMainWindowViewModel>();

        //Do other configuration            

        DataContext = mainWindowViewModel;

        InitializeComponent();
    }

}

У вас не должно быть контейнера GetInstanceдля resolveвнешнего файла app.xaml.cs, вы теряете смысл DI. Кроме того, упоминание представления xaml в выделенном коде представления выглядит запутанным. Просто вызовите представление на чистом C # и сделайте это с контейнером.
Soleil - Матье Прево
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.