programing

WPF/MVVM 애플리케이션에서 종속성 주입을 처리하는 방법

powerit 2023. 5. 28. 21:03
반응형

WPF/MVVM 애플리케이션에서 종속성 주입을 처리하는 방법

새로운 데스크톱 애플리케이션을 시작하려고 하는데 MVVM과 WPF를 사용하여 구축하려고 합니다.

저도 TDD를 이용할 생각입니다.

문제는 생산 코드에 종속성을 주입하기 위해 IoC 컨테이너를 어떻게 사용해야 하는지 모른다는 것입니다.

다음 클래스와 인터페이스가 있다고 가정합니다.

public interface IStorage
{
    bool SaveFile(string content);
}

public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

또 요.IStorage종속성으로 이 클래스가 ViewModel 또는 비즈니스 클래스라고 가정합니다.

public class SomeViewModel
{
    private IStorage _storage;

    public SomeViewModel(IStorage storage){
        _storage = storage;
    }
}

이를 통해 유닛 테스트를 쉽게 작성하여 모크 등을 사용하여 올바르게 작동하는지 확인할 수 있습니다.

문제는 실제 애플리케이션에서 사용하는 경우입니다.IoC 는 기본 구현을 .IStorage인터페이스, 하지만 어떻게 해야 할까요?

예를 들어, 다음과 같은 xaml이 있다면 어떨까요?

<Window 
    ... xmlns definitions ...
>
   <Window.DataContext>
        <local:SomeViewModel />
   </Window.DataContext>
</Window>

이 경우 WPF가 종속성을 주입하도록 올바르게 '지시'하려면 어떻게 해야 합니까?

또한, 예를 들어, 제가 필요하다고 가정해 보겠습니다.SomeViewModelC# 코드에서 어떻게 해야 합니까?

저는 이 문제에 완전히 빠져 있다고 생각합니다. 어떻게 대처하는 것이 가장 좋은 방법인지에 대한 예나 지침을 주시면 감사하겠습니다.

StructureMap은 잘 알고 있지만 전문가는 아닙니다.또한, 더 나은/쉬운/즉시적인 프레임워크가 있다면 알려주시기 바랍니다.

저는 닌젯을 사용해왔고, 함께 일하는 것이 즐겁다는 것을 알게 되었습니다.모든 것이 코드로 설정되어 있고, 구문은 상당히 간단하고 문서화가 잘 되어 있습니다(SO에 대한 답변도 많습니다).

기본적으로 다음과 같습니다.

뷰 모델을 만들고 다음을 수행합니다.IStorage생성자 매개 변수로서의 인터페이스:

class UserControlViewModel
{
    public UserControlViewModel(IStorage storage)
    {

    }
}

성을 합니다.ViewModelLocator모델을 하는 뷰 을 사용하여 "get"에서 뷰 합니다.

class ViewModelLocator
{
    public UserControlViewModel UserControlViewModel
    {
        get { return IocKernel.Get<UserControlViewModel>();} // Loading UserControlViewModel will automatically load the binding for IStorage
    }
}

를 .ViewModelLocator 응용 .xaml):

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

을 .DataContext의 시대의UserControlViewModelLocator에서 해당 속성으로 이동합니다.

<UserControl ...
             DataContext="{Binding UserControlViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
    </Grid>
</UserControl>

NinjectModule을합니다("InnjectModule"은 "InnjectModule"입니다.IStorage 뷰 모델 예를 들어 다음과 같습니다.

class IocConfiguration : NinjectModule
{
    public override void Load()
    {
        Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

        Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
    }
}

필요한 Ninject 모듈(현재는 위의 모듈)을 사용하여 애플리케이션 시작 시 IoC 커널을 초기화합니다.

public partial class App : Application
{       
    protected override void OnStartup(StartupEventArgs e)
    {
        IocKernel.Initialize(new IocConfiguration());

        base.OnStartup(e);
    }
}

인 정기를니다습했을 .IocKernel필요할 때 쉽게 액세스할 수 있도록 IoC 커널의 애플리케이션 전체 인스턴스를 보유하는 클래스:

public static class IocKernel
{
    private static StandardKernel _kernel;

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static void Initialize(params INinjectModule[] modules)
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(modules);
        }
    }
}

인 " 솔션은정기사다니합용을능적이루다니"를 사용합니다.ServiceLocator (그)IocKernel 클래스의 종속성을 숨기기 때문에 일반적으로 안티바이러스로 간주됩니다.그러나 UI 클래스에는 매개 변수 없는 생성자가 있어야 하고 인스턴스화를 제어할 수 없으므로 VM을 주입할 수 없기 때문에 UI 클래스에 대한 일종의 수동 서비스 조회를 피하기가 매우 어렵습니다.적어도 이 방법을 사용하면 모든 비즈니스 로직이 있는 VM을 분리하여 테스트할 수 있습니다.

더 좋은 방법이 있으면 공유해 주세요.

편집: Lucky Likey는 Ninject가 UI 클래스를 인스턴스화하도록 함으로써 정적 서비스 로케이터를 제거하기 위한 답변을 제공했습니다.답변에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

▁▁value▁the다▁inDataContext의 뷰 AML에 있는 뷰의 속성입니다.이렇게 하려면 뷰 모델에 기본 생성자가 있어야 합니다.그러나 앞서 언급한 것처럼 생성자에 종속성을 주입하려는 종속성 주입에서는 잘 작동하지 않습니다.

따라서 XAML에서 속성을 설정할 수 없으며 종속성 주입도 수행할 수 없습니다.대신 다른 대안이 있습니다.

뷰 경우 시작될 때 뷰 할 수 .StartupUri App.xaml으):

public partial class App {

  protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);
    var container = CreateContainer();
    var viewModel = container.Resolve<RootViewModel>();
    var window = new MainWindow { DataContext = viewModel };
    window.Show();
  }

}

이것은 다음에 뿌리를 둔 뷰 모델의 객체 그래프를 기반으로 합니다.RootViewModel그러나 일부 뷰 모델 팩토리를 부모 뷰 팩토리에 삽입하여 새 자식 뷰 팩토리를 만들 수 있으므로 객체 그래프를 수정할 필요가 없습니다.이것은 또한 제 코드의 인스턴스가 필요하다고 가정했을 때 당신의 질문에 답할 수 있기를 바랍니다. 어떻게 해야 할까요?

class ParentViewModel {

  public ParentViewModel(ChildViewModelFactory childViewModelFactory) {
    _childViewModelFactory = childViewModelFactory;
  }

  public void AddChild() {
    Children.Add(_childViewModelFactory.Create());
  }

  ObservableCollection<ChildViewModel> Children { get; private set; }

 }

class ChildViewModelFactory {

  public ChildViewModelFactory(/* ChildViewModel dependencies */) {
    // Store dependencies.
  }

  public ChildViewModel Create() {
    return new ChildViewModel(/* Use stored dependencies */);
  }

}

응용프로그램이 보다 동적이고 탐색을 기반으로 하는 경우 탐색을 수행하는 코드에 연결해야 합니다.마다 (하고 뷰 .DataContext뷰 모델에 대한 뷰입니다.뷰를 기반으로 뷰 모델을 선택한 경우 이 를 먼저 수행하거나 뷰 모델이 사용할 뷰를 결정하는 경우 뷰 모델을 먼저 수행할 수 있습니다.MVVM 프레임워크는 DI 컨테이너를 뷰 모델 생성에 연결할 수 있는 몇 가지 방법과 함께 이 주요 기능을 제공하지만 사용자가 직접 구현할 수도 있습니다.필요에 따라 이 기능이 상당히 복잡해질 수 있기 때문에 여기서는 좀 모호합니다.이 기능은 MVVM 프레임워크에서 얻을 수 있는 핵심 기능 중 하나이지만 간단한 애플리케이션에서 직접 롤링하면 MVVM 프레임워크가 후드 아래에서 제공하는 기능을 잘 이해할 수 있습니다.

다을선수없음을 할 수 으로써.DataContextXAML에서는 디자인 타임 지원이 일부 손실됩니다.뷰 모델에 일부 데이터가 포함된 경우 뷰 모델은 설계 시간 동안 표시되므로 매우 유용할 수 있습니다.WPF에서도 디자인 타임 속성을 사용할 수 있습니다.이 작업을 수행하는 한 가지 방법은 다음 속성을 다음에 추가하는 것입니다.<Window> 또는 요소또<UserControl>XAML 형식:

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MyViewModel, IsDesignTimeCreatable=True}"

뷰 모델 유형에는 설계 시간 데이터의 기본값과 종속성 주입의 기본값인 두 개의 생성자가 있어야 합니다.

class MyViewModel : INotifyPropertyChanged {

  public MyViewModel() {
    // Create some design-time data.
  }

  public MyViewModel(/* Dependencies */) {
    // Store dependencies.
  }

}

이렇게 하면 종속성 주입을 사용할 수 있고 설계 시간 지원을 잘 유지할 수 있습니다.

제가 여기에 게시하는 것은 손더가드의 답변에 대한 개선 사항입니다. 왜냐하면 제가 말할 내용이 댓글에 맞지 않기 때문입니다. :)

사실 저는 ServiceLocator와 래퍼가 필요하지 않은 깔끔한 솔루션을 소개하고 있습니다.StandardKernel에서 -인턴스스, 인드의솔은루션라고 합니다.IocContainer왜죠? 언급했듯이, 그것들은 반패턴입니다.

만들기StandardKernel서나 이용 합니다.

닌젝트의 마법의 열쇠는StandardKernel- 를 하기 필요한 입니다..Get<T>()-방법.

손더가르드의 것 대신에.IocContainer생할수있다니를 할 수 .StandardKernel에의 에.App -반.

앱에서 StartUpUri를 제거하기만 하면 됩니다.

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

이것은 App.xaml.cs 에 있는 앱의 코드입니다.

public partial class App
{
    private IKernel _iocKernel;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        _iocKernel = new StandardKernel();
        _iocKernel.Load(new YourModule());

        Current.MainWindow = _iocKernel.Get<MainWindow>();
        Current.MainWindow.Show();
    }
}

지금부터 닌젝트는 살아있고 싸울 준비가 되어있습니다 :)

DataContext

Ninject가 활성 상태이므로 속성 설정자 주입 또는 가장 일반적인 생성자 주입 등 모든 종류의 주입을 수행할 수 있습니다.

Model을 의 View Model에 입니다.WindowDataContext

public partial class MainWindow : Window
{
    public MainWindow(MainWindowViewModel vm)
    {
        DataContext = vm;
        InitializeComponent();
    }
}

주사를 놓을 .IViewModel만약 당신이 올바른 바인딩을 한다면, 하지만 그것은 이 대답의 일부가 아닙니다.

커널에 직접 액세스

Methods on the Kernel)을 직접 예:.Get<T>()-Method), 커널 자체를 주입할 수 있습니다.

    private void DoStuffWithKernel(IKernel kernel)
    {
        kernel.Get<Something>();
        kernel.Whatever();
    }

커널의 로컬 인스턴스가 필요한 경우 속성으로 삽입할 수 있습니다.

    [Inject]
    public IKernel Kernel { private get; set; }

이것이 꽤 유용할 수 있지만, 저는 당신에게 그렇게 하는 것을 추천하지 않습니다.이러한 방식으로 주입된 개체는 나중에 주입되기 때문에 생성자 내부에서 사용할 수 없습니다.

링크에 따라 다음을 주입하는 대신 공장 확장 기능을 사용해야 합니다.IKernel(DI 컨테이너).

소프트웨어 시스템에서 DI 컨테이너를 사용하는 권장 접근 방식은 응용 프로그램의 합성 루트가 컨테이너가 직접 닿는 단일 장소가 되는 것입니다.

닌젯이 어떻게.내선 번호.사용할 공장도 여기에 빨간색으로 표시될 수 있습니다.

저는 뷰 모델을 (코드 뒤에 있는) 뷰의 생성자에게 전달하는 "뷰 퍼스트" 접근 방식을 택합니다. 여기서 뷰 모델은 데이터 컨텍스트에 할당됩니다.

public class SomeView
{
    public SomeView(SomeViewModel viewModel)
    {
        InitializeComponent();

        DataContext = viewModel;
    }
}

이는 XAML 기반 접근 방식을 대체합니다.

저는 탐색을 처리하기 위해 프리즘 프레임워크를 사용합니다. 일부 코드가 특정 보기를 표시하도록 요청하면 프리즘은 해당 보기를 (앱의 DI 프레임워크를 사용하여) 해결합니다. DI 프레임워크는 보기에 포함된 모든 종속성을 해결한 다음(예: 보기 모델), 그 종속성을 해결합니다.

DI 프레임워크를 선택하는 것은 모두 기본적으로 동일한 작업을 수행하기 때문에 거의 관련이 없습니다. 즉, 프레임워크가 인터페이스에 종속성을 발견할 때 인스턴스화할 구체적인 유형과 함께 인터페이스(또는 유형)를 등록합니다.참고로 저는 캐슬 윈저를 사용합니다.

프리즘 탐색은 익숙해지는 데 다소 시간이 걸리지만, 일단 머리를 굴리면 다른 뷰를 사용하여 응용프로그램을 구성할 수 있어 매우 유용합니다.예를 들어 메인 창에 프리즘 "영역"을 만든 다음 프리즘 탐색을 사용하여 이 영역 내의 한 보기에서 다른 보기로 전환할 수 있습니다(예: 사용자가 메뉴 항목 또는 기타 항목을 선택할 때).

또는 MVVM Light와 같은 MVVM 프레임워크 중 하나를 살펴봅니다.저는 이것들에 대한 경험이 없어서 그것들이 무엇을 사용하는지에 대해 언급할 수 없습니다.

MVVM Light를 설치합니다.

설치의 일부는 뷰 모델 로케이터를 만드는 것입니다.뷰 모델을 속성으로 표시하는 클래스입니다.그런 다음 IOC 엔진에서 이러한 속성의 게터를 인스턴스로 반환할 수 있습니다.다행히도 MVVM 라이트에는 Simple도 포함되어 있습니다.IOC 프레임워크, 하지만 원한다면 다른 사람들을 연결할 수 있습니다.

간단한 IOC를 사용하여 유형에 대한 구현을 등록합니다.

SimpleIOC.Default.Register<MyViewModel>(()=> new MyViewModel(new ServiceProvider()), true);

이 예에서 뷰 모델은 생성자에 따라 서비스 제공자 개체를 생성하고 전달합니다.

그런 다음 IOC에서 인스턴스를 반환하는 속성을 생성합니다.

public MyViewModel
{
    get { return SimpleIOC.Default.GetInstance<MyViewModel>; }
}

유용한 부분은 뷰 모델 로케이터가 app.xaml 또는 데이터 소스와 동등한 수준으로 생성된다는 것입니다.

<local:ViewModelLocator x:key="Vml" />

이제 'MyViewModel' 속성에 바인딩하여 삽입된 서비스로 뷰 모델을 가져올 수 있습니다.

도움이 되길 바랍니다.iPad의 메모리에서 코드화된 코드 오류에 대해 사과드립니다.

캐노닉 드라이 아이코 케이스

이전 게시물에 응답하지만 이 작업을 사용하여DryIoc그리고 DI와 인터페이스(구체적인 수업의 최소 사용)를 잘 사용하는 것이 좋다고 생각합니다.

  1. 은 WPF입니다.App.xaml사용할 초기 보기가 무엇인지 알려줍니다. 기본 xaml이 아닌 코드 뒤에 있는 상태에서 이 작업을 수행합니다.
  2. 거한제를 합니다.StartupUri="MainWindow.xaml"xaml 부일에파에 있습니다.
  3. 뒤(App.xaml.cs 에 이 App.xaml.cs 을 합니다.override OnStartup:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        DryContainer.Resolve<MainWindow>().Show();
    }
    

그것이 시작점이다; 그것은 또한 유일한 장소이다.resolve호출해야 합니다.

  1. 구성 루트(.NET의 Mark Seeman의 책 Dependency injection에 따르면, 구체적인 클래스가 언급되어야 하는 유일한 위치)는 생성자에서 동일한 코드 뒤에 있을 것입니다.

    public Container DryContainer { get; private set; }
    
    public App()
    {
        DryContainer = new Container(rules => rules.WithoutThrowOnRegisteringDisposableTransient());
        DryContainer.Register<IDatabaseManager, DatabaseManager>();
        DryContainer.Register<IJConfigReader, JConfigReader>();
        DryContainer.Register<IMainWindowViewModel, MainWindowViewModel>(
            Made.Of(() => new MainWindowViewModel(Arg.Of<IDatabaseManager>(), Arg.Of<IJConfigReader>())));
        DryContainer.Register<MainWindow>();
    }
    

비고 및 몇 가지 세부 정보

  • 만 사용했습니다.MainWindow;
  • 기본 생성자가 XAML 디자이너를 위해 존재해야 하고 주입이 있는 생성자가 애플리케이션에 사용되는 실제 생성자이기 때문에 View Model에 사용할 생성자를 지정해야 했습니다.

DI가 있는 View Model 생성자:

public MainWindowViewModel(IDatabaseManager dbmgr, IJConfigReader jconfigReader)
{
    _dbMgr = dbmgr;
    _jconfigReader = jconfigReader;
}

설계에 대한 View Model 기본 생성자:

public MainWindowViewModel()
{
}

보기 뒤의 코드:

public partial class MainWindow
{
    public MainWindow(IMainWindowViewModel vm)
    {
        InitializeComponent();
        ViewModel = vm;
    }

    public IViewModel ViewModel
    {
        get { return (IViewModel)DataContext; }
        set { DataContext = value; }
    }
}

View Model을 사용하여 설계 인스턴스를 가져오려면 뷰(MainWindow.xaml)에 필요한 항목:

d:DataContext="{d:DesignInstance local:MainWindowViewModel, IsDesignTimeCreatable=True}"

결론

따라서 뷰 및 뷰 모델의 설계 인스턴스를 가능하게 유지하면서 DryIoc 컨테이너와 DI를 사용하여 WPF 애플리케이션을 매우 깨끗하고 최소한으로 구현할 수 있었습니다.

관리되는 확장성 프레임워크를 사용합니다.

[Export(typeof(IViewModel)]
public class SomeViewModel : IViewModel
{
    private IStorage _storage;

    [ImportingConstructor]
    public SomeViewModel(IStorage storage){
        _storage = storage;
    }

    public bool ProperlyInitialized { get { return _storage != null; } }
}

[Export(typeof(IStorage)]
public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

//Somewhere in your application bootstrapping...
public GetViewModel() {
     //Search all assemblies in the same directory where our dll/exe is
     string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
     var catalog = new DirectoryCatalog(currentPath);
     var container = new CompositionContainer(catalog);
     var viewModel = container.GetExport<IViewModel>();
     //Assert that MEF did as advertised
     Debug.Assert(viewModel is SomViewModel); 
     Debug.Assert(viewModel.ProperlyInitialized);
}

일반적으로 정적 클래스를 사용하고 공장 패턴을 사용하여 전역 컨테이너(캐시됨, 스냅)를 제공합니다.

뷰 모델을 주입하는 방법은 다른 모든 것을 주입하는 것과 동일한 방식으로 주입합니다.XAML 파일의 코드 뒤에 가져오기 생성자를 만들고(또는 속성/필드에 가져오기 문을 삽입) 뷰 모델을 가져오라고 합니다.그럼 당신의 것을 묶으세요.WindowDataContext그 재산에.당신이 실제로 컨테이너에서 꺼내는 루트 객체는 보통 구성됩니다.Window위와 App..cs 에 파일입니다).창 클래스에 인터페이스를 추가하고 내보낸 다음 위와 같이 카탈로그에서 가져옵니다(App.xaml.cs 에서... 이것이 WPF 부트스트랩 파일입니다).

View Model - First 접근법 https://github.com/Caliburn-Micro/Caliburn.Micro 을 사용할 것을 제안합니다.

참조: https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions

사용하다Castle WindsorIOC 컨테이너로서

컨벤션에 대한 모든 것

Caliburn의 주요 기능 중 하나입니다.Micro는 일련의 규칙에 따라 작동함으로써 보일러 플레이트 코드의 필요성을 제거하는 기능을 가지고 있습니다.어떤 사람들은 관습을 사랑하고 어떤 사람들은 관습을 싫어합니다.그렇기 때문에 CM의 규약은 완전히 사용자 지정할 수 있으며 원하지 않으면 완전히 해제할 수도 있습니다.규칙을 사용할 경우 기본적으로 설정되어 있으므로 이러한 규칙이 무엇이고 어떻게 작동하는지를 아는 것이 좋습니다.그것이 이 기사의 주제입니다.해상도 보기(모델 먼저 보기)

기본 사항

CM을 사용할 때 발생할 수 있는 첫 번째 규칙은 보기 해상도와 관련이 있습니다.이 규칙은 응용 프로그램의 View Model-First 영역에 적용됩니다.View Model-First에는 화면에 렌더링해야 하는 기존 View Model이 있습니다.이를 위해 CM은 간단한 이름 지정 패턴을 사용하여 View Model에 바인딩하고 표시해야 하는 UserControl1을 찾습니다.그렇다면, 그 패턴은 무엇일까요?ViewLocator를 살펴보겠습니다.다음을 확인할 모델 유형을 찾습니다.

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>{
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    {
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    }

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
        : GetOrCreateViewType(viewType);
};

처음에는 "context" 변수를 무시해 보겠습니다.보기를 유도하기 위해 VM 이름에 "ViewModel"이라는 텍스트를 사용하고 있다고 가정합니다. 따라서 "Model"이라는 단어를 제거하여 찾을 수 있는 모든 곳에서 "View"로 변경합니다.이렇게 하면 유형 이름과 네임스페이스를 모두 변경할 수 있습니다.모델을 봅니다.CustomerView Model이 View가 됩니다.고객 보기.또는 기능별로 응용프로그램을 구성하는 경우:고객 관리.고객 뷰 모델이 고객 관리가 됩니다.고객 보기.바라건대, 그것이 꽤 솔직하기를 바랍니다.이름을 알고 나면 해당 이름을 가진 유형을 검색합니다.우리는 당신이 CM에 노출한 어셈블리를 어셈블리를 통해 검색 가능한 것으로 검색합니다.출처.인스턴스.2유형을 찾으면 인스턴스를 만들고(또는 등록된 경우 IoC 컨테이너에서 가져옵니다) 호출자에게 반환합니다.유형을 찾을 수 없는 경우 적절한 "찾을 수 없음" 메시지가 있는 보기를 생성합니다.

이제 다시 그 "상황" 값으로 돌아갑니다.CM이 동일한 View Model에 대해 여러 View를 지원하는 방법입니다.컨텍스트(일반적으로 문자열 또는 열거형)가 제공되면 해당 값을 기준으로 이름을 추가로 변환합니다.이 변환은 끝에서 "보기"라는 단어를 제거하고 대신 컨텍스트를 추가하여 다양한 보기에 대한 폴더(이름 공간)가 있다고 가정합니다.따라서 View 모델을 "마스터"하는 컨텍스트가 주어집니다.CustomerView Model이 View가 됩니다.고객.마스터.

또 다른 간단한 솔루션은 뷰 모델을 유형별로 확인하는 머크업 확장을 만드는 것입니다.

public class DISource : MarkupExtension {
    public static Func<Type, object, string, object> Resolver { get; set; }

    public Type Type { get; set; }
    public object Key { get; set; }
    public string Name { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider) => Resolver?.Invoke(Type, Key, Name);
}

다음과 같은 방법으로 이 확장을 모든 DI 컨테이너로 조정할 수 있습니다.

protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);
    DISource.Resolver = Resolve;
}
object Resolve(Type type, object key, string name) {
    if(type == null)
        return null;
    if(key != null)
        return Container.ResolveKeyed(key, type);
    if(name != null)
        return Container.ResolveNamed(name, type);
    return Container.Resolve(type);
}

XAML에서는 다음과 같이 간단하게 사용합니다.

DataContext="{local:DISource Type=viewModels:MainViewModel}"

이렇게 하면 DataContext를 뷰에 쉽게 할당하고 DI 컨테이너를 사용하여 필요한 모든 매개변수를 뷰 모델에 자동으로 직접 주입할 수 있습니다.이 기술을 사용하면 DI 컨테이너나 다른 매개 변수를 View 생성자에게 전달할 필요가 없습니다.

DISource는 컨테이너 유형에 의존하지 않으므로 모든 종속성 주입 프레임워크에서 사용할 수 있습니다.DIS 소스를 설정하기에 충분합니다.DI 컨테이너 사용 방법을 알고 있는 메서드에 속성을 확인합니다.

WPF MVVM 애플리케이션의 종속성 주입에서 이 기술을 더 자세히 설명했습니다.

app.xaml에서 시작 uri를 제거합니다.

App.xaml.cs

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        IoC.Configure(true);

        StartupUri = new Uri("Views/MainWindowView.xaml", UriKind.Relative);

        base.OnStartup(e);
    }
}

이제 IoC 클래스를 사용하여 인스턴스를 구성할 수 있습니다.

MainWindowView.xaml.cs

public partial class MainWindowView
{
    public MainWindowView()
    {
        var mainWindowViewModel = IoC.GetInstance<IMainWindowViewModel>();

        //Do other configuration            

        DataContext = mainWindowViewModel;

        InitializeComponent();
    }

}

언급URL : https://stackoverflow.com/questions/25366291/how-to-handle-dependency-injection-in-a-wpf-mvvm-application

반응형