Исключение конструкторов внедрения зависимостей WPF MVVM - PullRequest
0 голосов
/ 20 января 2020

Я разрабатываю собственное приложение, чтобы узнать что-то новое. Это WPF. net Приложение Core 3.1, использующее шаблон MVVM.

Недавно я решил включить библиотеку Microsoft DependencyInjection.

Я удалил StartupUri и изменил iec app.xaml.cs:

        public IServiceProvider ServiceProvider { get; private set; }
        public IConfiguration Configuration { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
            InitializeCefSharp();

            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

            Configuration = builder.Build();

            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            ServiceProvider = serviceCollection.BuildServiceProvider();

            var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
            mainWindow.Show();
        }

        private void ConfigureServices(IServiceCollection services)
        {
            services.Configure<AppSettings>
                (Configuration.GetSection(nameof(AppSettings)));

            // Views
            services.AddSingleton<MainWindow>();
            services.AddSingleton<SettingsView>();
            services.AddSingleton<WwwView>();
            services.AddSingleton<BuildingView>();
            services.AddSingleton<TroopsMovementsView>();

            // ViewModels
            services.AddSingleton<MainWindowViewModel>();
            services.AddSingleton<SettingsViewModel>();
            services.AddSingleton<WwwViewModel>();
            services.AddSingleton<DomainViewModel>();
            services.AddSingleton<WorldViewModel>();
            services.AddSingleton<RegisterViewModel>();
        }

Я устанавливаю DataContext внутри конструкторов Views. Все представления, кроме MainWindow, имеют тип UserControl.

        private readonly MainWindowViewModel _mainWindowViewModel;

        public MainWindow(MainWindowViewModel mainWindowViewModel)
        {
            _mainWindowViewModel = mainWindowViewModel;

            DataContext = _mainWindowViewModel;

            InitializeComponent();
        }
        private readonly SettingsViewModel _settingsViewModel;

        public SettingsView(SettingsViewModel settingsViewModel)
        {
            _settingsViewModel = settingsViewModel;

            DataContext = settingsViewModel;

            InitializeComponent();
        }

Все представления встроены в MainWindow следующим образом:

        <dz:DockControl Grid.Row="3" Loaded="FrameworkElement_OnLoaded">
            <dz:DockItem x:Name="Settings" TabText="Settings" ShowAction="{dz:ShowAsDockPositionAction DockPosition=RightAutoHide}">
                <views:SettingsView/>
            </dz:DockItem>
            <dz:DockItem x:Name="WWW" TabText="WWW" ShowAction="{dz:ShowAsDockPositionAction DockPosition=Document}" DefaultDockPosition="Document" >
                <views:WwwView/>
            </dz:DockItem>
        </dz:DockControl>

Это визуальная студийная док-библиотека.

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

Когда я создал 2-й конструктор без параметров, происходят странные вещи - Views не загружается внутри MainWindow и не загружается, но не использует ViewModels.

Если я устанавливаю DataContext в xaml-файле, Views получает конструкторы без параметров, но тогда ViewModels должен иметь конструкторы без параметров ...

    <UserControl.DataContext>
        <vm:SettingsViewModel/>
    </UserControl.DataContext>

Как правильно использовать внедрение зависимостей в моем случае?


1 Ответ

0 голосов
/ 21 января 2020

WPF требует объектов, которые создаются в XAML для определения конструктора без параметров.

Существует много способов достижения sh Внедрения зависимостей в WPF с использованием MVVM. При поиске в inte rnet наиболее распространенным решением кажется ViewModelLocator, еще одна реализация локатора служб, которая широко рассматривается как анти-шаблон (например, печально известный контейнер stati c Singleton Io C).

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

MainViewModel.cs

class MainViewModel
{
  public MainViewModel(IFirstControlViewModel firstControlViewModel ,
    ISecondControlViewModel secondControlViewModel)
  { 
    this.FirstControlViewModel = firstControlViewModel;
    this.SecondControlViewModel = secondControlViewModel;
  }  

  public IFirstControlViewModel FirstControlViewModel { get; }
  public ISecondControlViewModel SecondViewModel { get; }
}

FirstViewModel .cs

class FirstViewModel : IFirstViewModel
{      
}

SecondViewModel.cs

class SecondViewModel : ISecondViewModel
{
  public SecondVieModel(IThirdViewModel thirdViewModel) => this.ThirdViewModel = thirdViewModel;

  public IThirdViewModel ThirdViewModel { get; } 
}

MainWindow.xaml

<Window>
  <StackPanel>
    <FirstUserControl DataContext="{Binding FirstViewModel}" />
    <SecondUserControl DataCOntext="{Binding SecondViewModel}" />
  </StackPanel>
</Window>

SecondUserControlxaml

<UserControl>
  <Grid>
    <ThirdUserControl DataContext="{Binding ThirdViewModel}" />
  </Grid>
</UserControl>

App.xaml.cs

private void Run(StartupEventArgs e)
{
  IMainViewModel viewModel = container.GetExportedValue<IMainViewModel>();
  var mainWindow = new MainWindow { DataContext = viewModel };
  mainWindow.Show();
}

Или используйте только композицию верхнего уровня:

MainViewModel.cs

class MainViewModel
{
  public MainViewModel(IFirstControlViewModel firstControlViewModel ,
    ISecondControlViewModel secondControlViewModel,
    IThirdViewModel thirdViewModel)
  { 
    this.FirstControlViewModel = firstControlViewModel;
    this.SecondControlViewModel = secondControlViewModel;
    this.ThirdViewModel = thirdViewModel;
  }  

  public IFirstControlViewModel FirstControlViewModel { get; }
  public ISecondControlViewModel SecondViewModel { get; }
  public IThirdViewModel ThirdViewModel { get; } 
}

App.xaml.cs

private void Run(StartupEventArgs e)
{
  IMainViewModel viewModel = container.GetExportedValue<IMainViewModel>();

  // For simplicity, you can add the view model to the globally accessible App.xaml ResourceDictionary
  this.Resources.Add("MainViewModel", viewModel);

  var mainWindow = new MainWindow { DataContext = viewModel };
  mainWindow.Show();
}

SecondUserControlxaml

<UserControl>
  <Grid>
    <ThirdUserControl DataContext="{Binding Source="{StaticResource MainViewModel}", Path=ThirdViewModel}" />
  </Grid>
</UserControl>

Композиция - очень простое решение для использования Dependency Injection с представлениями. Если производительность, например, классы с большим деревом зависимостей, является проблемой, многие структуры DI, такие как MEF, поддерживают Lazy<T> export.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...