본문 바로가기
.NET/WPF

[WPF] 의존성 주입 예제 (1) - Microsoft.Extensions.DependencyInjection 사용

by elenakim97 2024. 11. 18.
[WPF] 의존성 주입(Dependency Injection)

 

이전 글에서는 WPF의 의존성 주입에 대해 알아보았고, 이번엔 직접 예제를 구현해 보는 시간!

.NET의 IoC 컨테이너인 `Microsoft.Extensions.DependencyInjection` 누겟을 사용한 예제이다.

 

IDateTimeService.cs 

DateTime 관련 서비스의 인터페이스로, 구상 클래스에서 구현해야 할 메서드를 정의했다

namespace DotNetDependencyInjection.Services
{
    public interface IDateTimeService
    {
        string GetDateTimeString();
    }
}

 

DateTimeService.cs 

IDateTimeService 인터페이스를 구현하는 클래스로, 현재 시간을 String값으로 리턴하는 메서드를 작성했다

namespace DotNetDependencyInjection.Services
{
    public class DateTimeService : IDateTimeService
    {
        public string GetDateTimeString()
        {
            return DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
        }
    }
}

 


App.xaml
 

WPF 프로젝트를 생성하면 App.xaml에 정의되어 있는 `StartupUri`를 지워준다

(`StartupUri="MainWindow.xaml"` → 삭제)

<Application
    x:Class="DotNetDependencyInjection.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources />
</Application>


 App.xaml.cs

`OnStartUp` 메서드를 오버라이드하여 필요한 서비스들을 등록하고,

`MainWindow`를 찾아 Show 메서드로 호출해준다. (StartupUri와 유사한 역할)

using System.Windows;
using DotNetDependencyInjection.ViewModels;
using DotNetDependencyInjection.Services;
using DotNetDependencyInjection.Views;
using Microsoft.Extensions.DependencyInjection;

namespace DotNetDependencyInjection
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public IServiceProvider ServiceProvider { get; set; }

        /// <summary>
        /// OnStartup
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStartup(StartupEventArgs e)
        {
            var services = ConfigureServices();
            ServiceProvider = services.BuildServiceProvider();

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

        /// <summary>
        /// Configure Services
        /// </summary>
        /// <returns></returns>
        private ServiceCollection ConfigureServices()
        {
            ServiceCollection serviceCollection = new();

            serviceCollection.AddTransient<IDateTimeService, DateTimeService>();
            serviceCollection.AddSingleton<MainWindowViewModel>();
            serviceCollection.AddSingleton(s => new MainWindow()
            {
                DataContext = s.GetRequiredService<MainWindowViewModel>()
            });

            return serviceCollection;
        }
    }
}

등록할 서비스들은 각 서비스 성격에 따라 생명주기(Singleton, Scoped, Transient)를 설정한다.

 

또한 `MainWindow.xaml` 또는 `MainWindow.xaml.cs`에서 DataContext를 설정하지 않고, `GetRequiredService`를 사용해서 의존성이 주입된 `MainWindowViewModel`을 DataContext로 설정하는 것이 핵심!

 

MainWindow.xaml 

확인 버튼과 결과 텍스트가 있는 간단한 화면이다

<Window
    x:Class="DotNetDependencyInjection.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    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:vm="clr-namespace:DotNetDependencyInjection.ViewModels"
    Title="MainWindow"
    Width="400"
    Height="350"
    d:DataContext="{d:DesignInstance Type=vm:MainWindowViewModel,
                                     IsDesignTimeCreatable=False}"
    mc:Ignorable="d">
    <Grid Margin="30">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="현재 시간" />
            <Button
                Margin="5,0"
                Command="{Binding ButtonClickCommand}"
                Content="확인" />
        </StackPanel>
        <TextBlock
            Grid.Row="1"
            Margin="0,10"
            FontSize="20"
            Text="{Binding DateTimeString, Mode=TwoWay}" />
    </Grid>
</Window>


 MainWindowViewModel.cs
 

결과값으로 보여줄 string 값과 Command가 정의된 뷰모델이고, 생성자 주입을 통한 의존성 주입을 구현했다.

`BindableBase`는 `Prism.Core` 누겟을 설치하면 상속받을 수 있다.

using DotNetDependencyInjection.Services;
using System.Windows.Input;

namespace DotNetDependencyInjection.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private readonly IDateTimeService _dateTimeService;

        private string _dateTimeString = string.Empty;
        /// <summary>
        /// 시간 문자열
        /// </summary>
        public string DateTimeString
        {
            get => _dateTimeString;
            set => SetProperty(ref _dateTimeString, value);
        }

        /// <summary>
        /// Button Click Command
        /// </summary>
        public ICommand ButtonClickCommand { get; set; }

        public MainWindowViewModel()
        {

        }

        public MainWindowViewModel(IDateTimeService dateTimeService)
        {
            _dateTimeService = dateTimeService;
            ButtonClickCommand = new DelegateCommand(OnButtonClick);
        }

        /// <summary>
        /// Button Click Command Event
        /// </summary>
        private void OnButtonClick()
        {
            DateTimeString = _dateTimeService.GetDateTimeString();
        }
    }
}

 

실행 결과

확인 버튼 클릭 시 `DateTimeService`의 `GetDateTimeString`메서드가 호출되고, 현재 시간이 표시되는 것을 확인할 수 있다.


💡 예제 소스

https://github.com/elena-kim/wpf-study/tree/main/WpfStudy/DotNetDependencyInjection

 

댓글