Внедрение зависимостей в.NET Core 3.0 для WPF

Я хорошо знаком с ASP.NET Core и поддержкой внедрения зависимостей из коробки. Контроллеры могут требовать зависимости, добавив параметр в их конструктор. Как получить зависимости в WPF UserControls? Я попытался добавить параметр в конструктор, но это не сработало. Мне нравится концепция МОК, и я бы предпочел перенести ее в WPF.

6 ответов

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

Для внедрения зависимостей в.NET Core 3.0 для WPF. После того, как вы создадите проект WPF core 3 в своем решении, вам необходимо установить / добавить пакеты nuget:

Microsoft.Extensions.DependencyInjection

В моем случае я создал класс, который хочу использовать для ведения журнала, назвал его LogBase, поэтому в вашем классе приложения добавьте следующее, и да, это всего лишь базовый пример:

private readonly ServiceProvider _serviceProvider;

public App()
{
    var serviceCollection = new ServiceCollection();
    ConfigureServices(serviceCollection);
    _serviceProvider = serviceCollection.BuildServiceProvider();
}

private void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ILogBase>(new LogBase(new FileInfo($@"C:\temp\log.txt")));
    services.AddSingleton<MainWindow>();
}

private void OnStartup(object sender, StartupEventArgs e)
{
    var mainWindow = _serviceProvider.GetService<MainWindow>();
    mainWindow.Show();
}

В свой App.xaml добавьте Startup="OnStartup", чтобы он выглядел так:

<Application x:Class="VaultDataStore.Wpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:VaultDataStore.Wpf"
             Startup="OnStartup">
    <Application.Resources>

    </Application.Resources>
</Application>

Итак, в вашем MainWindow.xaml.cs вы вставляете ILogBase в конструктор следующим образом:

private readonly ILogBase _log;

public MainWindow(ILogBase log)
{
    _log = log;

    ...etc.. you can use _log over all in this class

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

Я добавил все это вместе в репозиторий github.


Между тем меня спросили, как использовать инъекцию в пользовательском элементе управления, я предлагаю это решение, если кто-то извлекает из него пользу. Проверьте это здесь.

Ответ maytham-ɯɐɥʇʎɐɯ для настройки внедрения зависимостей из класса приложения и метода запуска хорош, за исключением того, что я реализую IServiceProvider как свойство

              public IServiceProvider ServiceProvider { get; private set; }

Следующий шаг - «как внедрить модели просмотра»?

Мне не нравится внедрение модели представления в конструкторе окна (в коде позади). Мое более чистое решение заключалось в создании поставщика модели представления для использования в xaml. Для этого вы можете реализовать его как MarkupExtension.

      public class ViewModelProvider : MarkupExtension
{
    #region ctor
    public ViewModelProvider()
    {
    }

    public ViewModelProvider(Type viewModelType)
    {
        ViewModelType = viewModelType;
    }
    #endregion ctor

    #region properties
    public Type ViewModelType { get; set; }
    #endregion properties

    #region methods
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return ((App)Application.Current).ServiceProvider.GetService(ViewModelType);
    }
    #endregion methods
}

Затем в xaml вы можете спросить тип модели просмотра, который вы хотите ViewModelProvider

      DataContext="{local:ViewModelProvider local:MainViewModel}"

В WPF вы используете шаблон под названием Model-View-ViewModel (MVVM для краткости). Ваши зависимости внедряются в модель представления (используя те же структуры IoC, которые вы используете в ASP.NET, например, AutoFac), а ваши представления (UserControls) регистрируются в качестве шаблонов данных для ваших моделей представления.

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

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

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

Это не значит, что это невозможно сделать. Например, MVVM Light создает класс инжектора, а затем объявляет его экземпляр в App.xaml, что почти совпадает с объявлением глобальной переменной:

<Application.Resources>
    <ResourceDictionary>
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:MyMvvmProject.ViewModel" />
    </ResourceDictionary>
</Application.Resources>

Окна и пользовательские элементы управления, которые являются частью визуального дерева, могут связываться с ресурсами приложения, поэтому в этой структуре основное окно обычно связывается с моделью представления следующим образом:

<Window x:Class="MyMvvmProject.MainWindow"
    ...
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

...где Main является свойством класса локатора:

public MainViewModel Main
{
    get
    {
        return ServiceLocator.Current.GetInstance<MainViewModel>();
    }
}

Это не очень хороший IoC, потому что он помещает все ваши инъекционные препараты в один класс, на практике вы бы разбили его на специализированные фабрики и т. Д. На разных уровнях.

А если серьезно, не делай ничего из этого. Используйте DI на уровне модели представления и используйте свободную привязку данных, чтобы связать ее с вашим представлением. Это позволит вам использовать всю мощь внедрения зависимостей, частично отсоединяя его от слоя, в котором он не нужен, и частично, предоставляя гибкую возможность переконфигурировать ваши зависимости для различных сред, таких как веб, настольное, мобильное и особенно модульное тестирование. где представления не создаются вообще.

(Отказ от ответственности: я еще не использовал.NET Core для WPF, поэтому то, что я здесь представил, может быть немного специфичным для.NET, но общие принципы остаются).

Вы должны использовать код в этом случае. Но подумайте дважды, действительно ли необходимо иметь зависимость в UserControl или любом другом классе, созданном из xaml.

Другие вопросы по тегам