Внедрение зависимостей в Page ViewModels в WPF - PullRequest
0 голосов
/ 10 марта 2020

Я создаю приложение WPF с использованием NET core 3.1. В прошлом я разрабатывал ASP. Net приложений, и я был рад использовать его в WPF. Я провел некоторый поиск и понял, что DI в WPF не так прост, как в ASP. Net, что означает, что вам нужно зарегистрировать Views и ViewModels.

Моя структура такая

MainWindow
|---BalanceIntegrationPage
   |---BalanceIntegrationViewModel

Все обрабатывается в XAML с MainWindow.xaml.cs, имеющим только сгенерированный код, а BalanceIntegrationPage.xaml.cs добавляет в него одну строку в конструкторе

DataContext = new ScaleIntegrationViewModel();  

Это может не обрабатывается в xaml, потому что DI требует параметры в конструкторе.

Вот мой app.xaml.cs:

protected override async void OnStartup(StartupEventArgs startupEventArgs)
        {
            base.OnStartup(startupEventArgs);
            ServiceCollection services = new ServiceCollection();
            services.AddScoped<MainWindow>();
            services.AddScoped<ScaleInterfacePage>();
            services.AddScoped<ScaleIntegrationViewModel>();
            services.AddScoped<IScale>(provider => new Scale("1234"));

            ServiceProvider serviceProvider = services.BuildServiceProvider();
            MainWindow mainWindow = serviceProvider.GetService<MainWindow>();
            mainWindow.Show();

        }

Мой ScaleIntegrationViewModel выглядит так:

public ScaleIntegrationViewModel(IJMDataIntegration jmContext = null, IBalanceIntegrationContext localContext = null, IScale scale = null)
        {
            _jmContext = jmContext ?? new JMDataIntegration();
            _localContext = localContext ?? new BalanceIntegrationContext();
            _scale = scale ?? new Scale("1234");
            //JK read from config
            _commPort = "1234";
        }

Я также пытался использовать описанный шаблон здесь

Когда я выполняю код, мой объект IScale в конструкторе ViewModel всегда имеет значение null.

Любые предложения? ?

edit:

Основываясь на комментарии, я удалил вызов ViewModel в конструкторе страниц и вместо этого назначил его в .xaml. Это заставило меня создать конструктор по умолчанию без параметров который затем ломает DI.

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

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Вам не хватает конфигурации для некоторых зависимостей. Из кода, который вы опубликовали, вы пропустили настройку IJMDataIntegration и IBalanceIntegrationContext:

protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
  base.OnStartup(startupEventArgs);
  ServiceCollection services = new ServiceCollection();
  services.AddScoped<MainWindow>();
  services.AddScoped<ScaleInterfacePage>();
  services.AddScoped<IJMDataIntegration, JMDataIntegration>();
  services.AddScoped<IBalanceIntegrationContext, BalanceIntegrationContext>();
  services.AddScoped<IScale>(provider => new Scale("1234"));
  services.AddScoped<ScaleIntegrationViewModel>();

  ServiceProvider serviceProvider = services.BuildServiceProvider();
  MainWindow mainWindow = serviceProvider.GetService<MainWindow>();
  mainWindow.Show();

}

Кроме того, как уже упоминалось, вы должны также вставить модель представления в MainWindow. Здесь, где начинается график зависимостей, root приложения:

partial class MainWindow : Window
{
  public MainWindow(ScaleIntegrationViewModel viewModel)
  {
    this.DataContext = viewModel;
  }
}

Чтобы включить полную мощность Dependency Injection (и сделать насмешку проще), вы должны использовать Dependency Inversion во всем приложении. Это означает, что вы должны зависеть только от интерфейсов и, следовательно, иметь только типы интерфейсов в своих конструкторах:

partial class MainWindow : Window
{
  public MainWindow(IScaleIntegrationViewModel viewModel)
  {
    this.DataContext = viewModel;
  }
}

Элементы управления, подобные страницам, должны генерироваться с помощью DataTemplate, а не создаваться непосредственно в XAML. Все, что вам нужно сделать, это вставить модели просмотра страницы, например, в другую модель представления. Свяжите их с ContentPresenter и определите неявный DataTemplate, который нацелен на тип модели представления страницы. Этот шаблон содержит актуальную страницу. Посмотрите этот пример .

Найдите шаблон View-Model-First, если вам нужна дополнительная информация. По сути, представление может быть определено как шаблон данных и связано с типом модели представления. Шаблоны данных могут быть определены как ресурсы, или они могут быть определены внутри элемента управления, который будет отображать модель представления. Содержимое элемента управления является экземпляром модели представления, а шаблон данных используется для его визуального представления. Этот метод является примером ситуации, в которой сначала создается экземпляр модели представления, а затем создается представление.
Это предпочтительный способ, особенно в сочетании с внедрением зависимости.

1 голос
/ 10 марта 2020

Внедрение зависимостей означает, что вы внедряете зависимости, но не создаете их самостоятельно.

В вашем примере вы создаете модель представления вручную внутри твоя страница. Это не DI. Вы должны внедрить экземпляр модели представления на страницу.

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

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

public ScaleInterfacePage(ScaleIntegrationViewModel vm)
{
    InitializeComponent();
    DataContext = vm;
}

Вот и все, все готово.

...