Як поводитися з ін'єкцією залежності у програмі WPF / MVVM


102

Я запускаю нову програму для настільних ПК, і хочу створити її за допомогою 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.
Бейлі Міллер

Відповіді:


87

Я використовував Ninject і виявив, що з цим приємно працювати. Все налаштовано в коді, синтаксис досить простий і має хорошу документацію (і багато відповідей на ТА).

Так що в основному все йде так:

Створіть модель перегляду та візьміть 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та viewmodel):

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(the IocKernel), який, як правило, розглядається як антидіапазон, оскільки він приховує залежності класу. Однак дуже важко уникнути ручного пошуку служб для класів інтерфейсу, оскільки вони повинні мати конструктор без параметрів, і ви не можете керувати інстанцією в будь-якому випадку, тому ви не можете вводити VM. Принаймні, цей спосіб дозволяє перевірити VM ізольовано, саме там і є вся бізнес-логіка.

Якщо хтось має кращий спосіб, будь ласка, поділіться.

EDIT: Лакі Лайкі дав відповідь, щоб позбутися статичного локатора сервісу, дозволивши Ninject інстанціювати класи інтерфейсу користувача. Подробиці відповіді можна побачити тут


13
Я новачок в ін'єкційних залежностях, але, по суті, ваше рішення поєднує антидіапазон службового локатора з Ninject, оскільки ви використовуєте статичний локатор ViewModel. Можна стверджувати, що ін'єкція робиться у файлі Xaml, який є менш імовірним для тестування. Я не маю кращого рішення і, ймовірно, буду використовувати ваше - все ж я думаю, було б корисно згадати про це і у відповіді.
користувач3141326

Людина ваше рішення просто чудово, є тільки одна «проблема» за допомогою наступного рядка: DataContext="{Binding [...]}". Це змушує VS-дизайнера виконувати весь програмний код у конструкторі ViewModel. У моєму випадку Вікно виконується і блокує будь-яку взаємодію з VS. Можливо, слід змінити ViewModelLocator, щоб не знаходити "справжніх" ViewModels в Design-Time. - Ще одне рішення - "Вимкнути кодекс проекту", який також не дасть показати всім іншим. Можливо, ви вже знайшли акуратне рішення цього питання. У цьому випадку я б просив вас це показати.
LuckyLikey

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

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

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тип подання на модель view. Ви можете зробити цей погляд спочатку там, де ви обрали модель перегляду на основі подання, або ви можете зробити це переглядати модель спочаткуде модель перегляду визначає, який вид використовувати. Рамка MVVM надає цій ключовій функціональності певний спосіб підключити свій контейнер DI до створення моделей перегляду, але ви також можете їх реалізувати самостійно. Я тут трохи розпливчастий, тому що залежно від ваших потреб ця функціональність може стати досить складною. Це одна з основних функцій, яку ви отримуєте з MVVM-рамки, але прокат власних програм у простому додатку дасть вам хороше розуміння, що передбачено рамками MVVM під кришкою.

Не маючи можливості оголосити DataContextXAML, ви втрачаєте деяку підтримку дизайну. Якщо ваша модель перегляду містить деякі дані, вони з’являться під час проектування, що може бути дуже корисно. На щастя, ви можете використовувати атрибути часу проектування також у 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.
  }

}

Роблячи це, ви можете використовувати ін'єкцію залежності та зберігати хорошу підтримку дизайну та часу.


12
Це ТОЧНО те, що я шукав. Мене засмучує, скільки разів я читаю відповіді, які говорять "Просто використовуйте рамку [ yadde-ya ]". Це все добре і добре, але я хочу точно знати, як спершу це зробити, а потім я можу знати, які рамки можуть бути корисні для мене. Дякуємо, що виписали це так чітко.
kmote

28

Що я публікую тут, - це вдосконалення відповіді Сондергарда, тому що те, що я хочу сказати, не входить у коментар :)

У Факт я ввожу акуратне рішення, що дозволяє уникнути необхідності ServiceLocator та обгортки для StandardKernel-Instance, що в Рішенні sondergard називається Рішенням IocContainer. Чому? Як уже згадувалося, це антимоделі.

Створення StandardKernelдоступних скрізь

Ключ до магії StandardKernelNinject - це той, який потрібен для використання .Get<T>()методу.

Крім того, для sondergard IocContainerможна створити StandardKernelвсередині 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 живий, ви можете виконувати всі види ін'єкцій, наприклад, ін'єкція власника властивостей або найпоширеніша інжекція конструктора .

Це, як Ви вводите свій ViewModel в ваші WindowхDataContext

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

Звичайно, ви можете також зробити ін'єкцію, IViewModelякщо ви зробите правильні прив'язки, але це не є частиною цієї відповіді.

Доступ до ядра безпосередньо

Якщо вам потрібно викликати Методи на ядрі безпосередньо (наприклад, .Get<T>()-Метод), ви можете дозволити Ядру вводити себе.

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

Якщо вам знадобиться локальний екземпляр ядра, ви можете ввести його як властивість.

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

Хоча це може бути досить корисним, я б не рекомендував це робити. Просто зауважте, що об'єкти, введені таким чином, не будуть доступні всередині Конструктора, оскільки він буде введений пізніше.

Відповідно до цього посилання, ви повинні використовувати заводське розширення, а не вводити IKernel(DI контейнер).

Рекомендований підхід до використання контейнера DI в програмній системі полягає в тому, щоб коріння композиції програми було єдиним місцем, де контейнер безпосередньо торкався.

Як Ninject.Extensions.Factory буде використовуватися також може бути корисним тут .


Гарний підхід. Ніколи не досліджував Ninject до цього рівня, але я бачу, що я пропускаю :)
sondergard

@son thx. Наприкінці своєї відповіді ви заявили, що якщо хтось має кращий спосіб, будь ласка, поділіться. Чи можете ви додати це посилання?
LuckyLikey

якщо когось цікавить, як користуватися Ninject.Extensions.Factoryцим, викладіть це тут у коментарях, і я додам ще трохи інформації.
LuckyLikey

1
@LuckyLikey: Як я можу додати ViewModel до віконного контексту даних через XAML, який не має конструктора без параметрів? З рішенням Sondergard з ServiceLocator така ситуація стане можливою.
Томас Гелен

Тож скажіть, будь ласка, як отримати послуги, які мені потрібні у доданих властивостях? Вони завжди статичні, як DependencyPropertyполе резервного копіювання, так і його методи Get і Set.
springy76

12

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

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

        DataContext = viewModel;
    }
}

Це замінює ваш підхід на основі XAML.

Я використовую рамку Prism для обробки навігації - коли якийсь код вимагає відображення певного виду (шляхом "навігації" до нього), Prism вирішить це уявлення (внутрішньо, використовуючи рамки DI програми); Структура DI в свою чергу вирішить будь-які залежності, які має погляд (модель перегляду в моєму прикладі), потім вирішить його залежності тощо.

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

До навігації по призмі потрібно звикнути, але досить добре, як тільки ти обернешся, що дозволяє складати свою програму, використовуючи різні погляди. Наприклад, ви можете створити призму "регіону" у своєму головному вікні, а потім за допомогою навігації по призмі ви переключитеся з одного перегляду на інший у цьому регіоні, наприклад, коли користувач вибирає пункти меню чи будь-що інше.

Крім того, погляньте на одну з рам MVVM, таку як MVVM Light. У мене немає такого досвіду, тому не можу коментувати, що вони хочуть використовувати.


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

10

Встановіть MVVM Light.

Частина інсталяції полягає у створенні локатора перегляду моделі. Це клас, який розкриває ваші моделі перегляду як властивості. Потім отримати ці властивості можна повернути екземпляри з вашого двигуна IOC. На щастя, світло MVVM також включає рамку 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 - Mathieu Prévot

Я погоджуюся, що ви можете встановити значення властивості під час запуску, але припускати, що використання лінивої інстанції проти DI невірно.
kidshaw

@kishaw Я цього не зробив.
Soleil - Mathieu Prévot

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 - Mathieu Prévot

0

Я б запропонував використовувати ViewModel - Перший підхід https://github.com/Caliburn-Micro/Caliburn.Micro

дивіться: https://caliburnmicro.codeplex.com/wikipage?title=All%20 About%20Conventions

використовувати Castle Windsorяк контейнер МОК.

Все про конвенції

Однією з головних особливостей 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);
};

Давайте спочатку ігноруємо змінну "контекст". Для отримання подання ми робимо припущення, що ви використовуєте текст "ViewModel" для іменування своїх віртуальних машин, тому ми просто змінюємо це на "Перегляд" скрізь, де ми його знаходимо, видаляючи слово "Модель". Це впливає на зміну імен типів, і просторів імен. Отже ViewModels.CustomerViewModel стане Views.CustomerView. Або якщо ви організовуєте свою заявку за функцією: CustomerManagement.CustomerViewModel стає CustomerManagement.CustomerView. Сподіваємось, це досить прямо вперед. Після того, як у нас з'явиться ім'я, ми шукаємо типи з цим ім'ям. Ми шукаємо будь-яку збірку, на яку ви потрапили в CM, як пошукову за допомогою AssemblySource.Instance.2 Якщо ми знайдемо тип, ми створимо екземпляр (або отримаємо його з контейнера IoC, якщо він зареєстрований) і повернемо його до абонента. Якщо ми не знайдемо тип,

Тепер повернемось до цього значення "контексту". Ось як CM підтримує кілька переглядів в одному ViewModel. Якщо надається контекст (як правило, рядок або перерахунок), ми робимо подальше перетворення імені, виходячи з цього значення. Це перетворення передбачає, що у вас є папка (простір імен) для різних представлень, видаляючи слово "Вид" з кінця та додаючи контекст замість цього. Отже, з огляду на контекст "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 - Mathieu Prévot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.