Table of Contents

MVVM dependency injection into child ViewModels

This setup allows ViewModels to receive repositories or other dependencies through constructor injection.

Pattern Overview

The child control’s ViewModel is supplied as a property in the MainWindowViewModel. In XAML, the child control’s DataContext is bound to this property using DataContext="{Binding ChildControlViewModel}". This allows the child ViewModel to be injected via constructor, while the view itself remains parameterless.

Note: While this example uses Avalonia, the same pattern applies to WPF, .NET MAUI, Uno, and other .NET MVVM frameworks. The key idea—injecting dependencies into child ViewModels via the parent and binding the child control’s DataContext—is framework-agnostic.

Implementation Steps

  1. Register the services and ViewModels.

    App.axaml.cs

    public class App : Application
    {
        public static IServiceProvider Services { get; private set; } = default!;
    
        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);
        }
    
        public override void OnFrameworkInitializationCompleted()
        {
            ServiceCollection services = new();
            services.AddTransient<IRepository, Repository>();
            services.AddTransient<ChildControlViewModel>();
            services.AddTransient<MainWindowViewModel>();
            Services = services.BuildServiceProvider();        
    
            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
            {
                desktop.MainWindow = new MainWindow
                {
                    DataContext = Services.GetRequiredService<MainWindowViewModel>(),
                    Topmost = true
                };
            }
    
            base.OnFrameworkInitializationCompleted();
        }
    }
  2. Create the child viewmodel with constructor injection

    ChildControlViewModel.cs

    public class ChildControlViewModel(IRepository repository)
    {
        public ObservableCollection<string> Items { get; } = new ObservableCollection<string>(repository.GetItems());
    }
  3. Constructor injection of ChildControlViewModel in the MainViewModel

    MainWindowViewModel.cs

    public class MainWindowViewModel(ChildControlViewModel childControlViewModel) : ViewModelBase
    {
        public ChildControlViewModel ChildControlViewModel { get; } = childControlViewModel;        
    }
  4. Bind the child ViewModel in Xaml.

    MainWindow.xaml

    <Window
        xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        
        xmlns:vlc="clr-namespace:LibVLCSharp.Avalonia;assembly=LibVLCSharp.Avalonia"
        xmlns:exampleApp="using:ExampleApp"
        xmlns:vm="using:ExampleApp.ViewModels"        
        x:Class="ExampleApp.Views.MainWindow"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        Icon="/Assets/avalonia-logo.ico"
        Title="Example App">
    
        <Design.DataContext>
            <vm:MainWindowViewModel />
        </Design.DataContext>
    
        <exampleApp:ChildControl
            DataContext="{Binding ChildControlViewModel}"
            Grid.Row="1" />
    
    </Window>
© 2024 Rob van der Velden. All rights reserved.