iconDreamine
← 목록

Dreamine.MVVM.Locators

stablev1.0.10

ServiceLocator 패턴 구현. DI 컨테이너를 통한 ViewModel 조회.

#dreamine#locator#mvvm#registration#resolution#service#viewmodel
TFM net8.0Package Dreamine.MVVM.Locators참조 Dreamine.MVVM.Interfaces

Dreamine.MVVM.Locators

Dreamine MVVM 프레임워크를 위한 View ↔ ViewModel 매핑 인프라입니다.

이 라이브러리는 규칙 기반 해석, 수동 등록, 역방향 View 해석, Dependency Injection 연동을 지원하는 경량 ViewModel Locator 를 제공합니다.

이 라이브러리는 플랫폼 비종속 매핑 엔진으로 설계되었습니다. 핵심 목적은 View 타입으로부터 적절한 ViewModel 타입을 찾고, ViewModel 타입으로부터 적절한 View 타입을 찾는 것입니다.

DataContext, FrameworkElement, Loaded 이벤트 연결 같은 WPF 전용 처리는 이 라이브러리의 책임이 아닙니다.

➡️ English README

주요 기능

  • 규칙 기반 View ↔ ViewModel 매핑
  • 수동 ViewModel 등록
  • 선택적 DI Resolver 연동
  • 어셈블리 자동 스캔
  • ViewModel 로부터 View 역방향 해석
  • 테스트 격리 및 앱 재구성을 위한 Reset/cache clear API
  • 루트 네임스페이스 탐색 지원
  • 하위 폴더 및 중첩 네임스페이스 탐색 지원
  • 다음과 같은 다양한 네임스페이스 구조 지원:
    • Views
    • View
    • Pages
    • Windows
    • Dialogs
    • GUI
    • GUIs
    • Screens
    • Controls

설계 목적

Dreamine.MVVM.Locators 는 WPF 전용 타입에 직접 종속되지 않도록 설계되었습니다.

이 라이브러리의 책임은 다음과 같습니다.

  • View 타입으로부터 ViewModel 타입 해석
  • ViewModel 타입으로부터 View 타입 해석
  • 일관된 규칙 기반 매핑 엔진 제공
  • 수동 매핑 및 DI 기반 생성 지원

이 라이브러리의 책임이 아닌 것은 다음과 같습니다.

  • DataContext 설정
  • Loaded 이벤트 구독
  • Window, Page, UserControl 상속 여부 판별

이런 플랫폼 전용 동작은 별도의 WPF 전용 계층에서 처리해야 합니다.

매핑 동작 방식

Locator 는 다음 순서로 타입을 찾습니다.

View → ViewModel

  1. 수동 등록 맵 확인
  2. 규칙 기반 네임스페이스 후보 탐색
  3. 부모 네임스페이스 확장 탐색
  4. 루트 네임스페이스 탐색
  5. 어셈블리 전체에서 타입명 fallback 검색

예를 들어 View 이름이 MainWindow 라면 다음과 같은 후보를 순서대로 찾습니다.

  • MyApp.ViewModels.MainWindowViewModel
  • MyApp.Views.Admin.MainWindowMyApp.ViewModels.Admin.MainWindowViewModel
  • MyApp.GUI.Login.MainWindowMyApp.ViewModels.Login.MainWindowViewModel
  • MyApp.MainWindowViewModel

ViewModel → View

  1. 규칙 기반 네임스페이스 후보 탐색
  2. 부모 네임스페이스 확장 탐색
  3. 루트 네임스페이스 탐색
  4. 어셈블리 전체에서 타입명 fallback 검색

지원 가능한 구조 예시

루트 네임스페이스

namespace MyApp;

public partial class MainWindow
{
}

public sealed class MainWindowViewModel
{
}

기본 Views / ViewModels 구조

namespace MyApp.Views;

public partial class MainWindow
{
}

namespace MyApp.ViewModels;

public sealed class MainWindowViewModel
{
}

하위 폴더 구조

namespace MyApp.Views.Admin;

public partial class MainWindow
{
}

namespace MyApp.ViewModels.Admin;

public sealed class MainWindowViewModel
{
}

대체 폴더명 구조

namespace MyApp.GUI.Account;

public partial class LoginDialog
{
}

namespace MyApp.ViewModels.Account;

public sealed class LoginDialogViewModel
{
}

Dependency Injection 지원

ViewModel 인스턴스 생성은 등록된 Resolver 를 통해 수행합니다.

resolver.Resolve(vmType)

ViewModel 생성에는 Activator.CreateInstance fallback 이 없습니다. Resolver 가 등록되어 있지 않거나 Resolver 가 null을 반환하면, Resolve(...)는 두 상황을 구분하는 InvalidOperationException을 발생시킵니다.

사용 예시

Resolver 등록

ViewModelLocator.RegisterResolver(new MyResolver());

Locator 상태 초기화

ViewModelLocator.Clear();         // 매핑과 lookup cache 초기화, resolver 유지
ViewModelLocator.ClearResolver(); // resolver 만 제거
ViewModelLocator.Reset();         // 매핑, lookup cache, resolver 모두 초기화

Reset()은 단위테스트 격리에 유용합니다. Clear()는 DI 전략은 유지하면서 매핑만 다시 구성해야 할 때 사용합니다.

수동 매핑 등록

ViewModelLocator.Register(typeof(MainWindow), typeof(MainWindowViewModel));

ViewModel 해석

var vm = ViewModelLocator.Resolve(typeof(MainWindow));

View 해석

var view = ViewModelLocator.ResolveView(typeof(MainWindowViewModel));

자동 등록

ViewModelLocator.RegisterAll(Assembly.GetExecutingAssembly());

권장 규약

이 Locator 는 폭넓은 매칭을 지원하지만, 권장 구조는 여전히 다음과 같습니다.

  • Views
  • ViewModels

이 규약을 유지하면 프로젝트 예측 가능성이 높아지고, 동일 타입명이 여러 개 존재할 때 모호성을 줄일 수 있습니다.

참고 사항

  • 폭넓은 매칭은 유연성을 높이지만, 동일한 타입명이 여러 개 있을 경우 fallback 매칭이 모호해질 수 있습니다.
  • 후보가 여러 개일 가능성이 있으면 수동 등록을 우선하는 것이 안전합니다.
  • 이 라이브러리는 매핑과 생성까지를 담당하며, 플랫폼 이벤트 연결은 담당하지 않습니다.
  • AppDomain 타입 스캔 결과는 캐시되며, 새 Assembly 가 로드되거나 Clear() / Reset()이 호출되면 무효화됩니다.

라이선스

MIT License

구조 다이어그램

classDiagram
    class IViewModelLocator {
        <<interface>>
        +Locate~T~() T
        +Locate(Type) object
    }
    class ViewModelLocator {
        -IServiceProvider _provider
        +Locate~T~() T
        +Locate(Type) object
    }
    class ViewModelLocatorBase {
        <<abstract>>
        +Locate~T~() T
        #ResolveViewModel~T~() T
    }
    class AutoViewModelLocator {
        -Dictionary~Type,Type~ _map
        +Register~TView, TViewModel~() void
        +Locate(Type) object
    }
    IViewModelLocator <|.. ViewModelLocator
    IViewModelLocator <|.. AutoViewModelLocator
    ViewModelLocatorBase <|-- ViewModelLocator
    ViewModelLocatorBase <|-- AutoViewModelLocator

API 문서

타입

ViewModelLocator

\if KO View와 ViewModel 간의 타입 매핑 및 인스턴스 생성을 이름과 네임스페이스 규칙으로 지원합니다. \endif \if EN Supports convention-based type mapping and instance creation between Views and ViewModels. \endif

ViewNamingConvention

\if KO View 및 ViewModel 타입 이름을 확인하기 위한 공통 명명 규칙을 제공합니다. \endif \if EN Provides shared naming rules for resolving View and ViewModel type names. \endif

ViewModelLocator

#cctor Method

\if KO 어셈블리 로드 시 타입 검색 캐시가 초기화되도록 로케이터를 구성합니다. \endif \if EN Configures the locator to clear type lookup caches when an assembly is loaded. \endif

Clear Method

\if KO 구성된 확인자는 유지하고 등록된 매핑과 타입 검색 캐시를 지웁니다. \endif \if EN Clears registered mappings and type lookup caches while retaining the configured resolver. \endif

ClearResolver Method

\if KO 구성된 ViewModel 확인자를 제거합니다. \endif \if EN Removes the configured ViewModel resolver. \endif

ClearTypeCache Method

\if KO View 및 ViewModel 타입 검색 캐시를 지웁니다. \endif \if EN Clears the View and ViewModel type lookup caches. \endif

CreateInstance Method

\if KO 구성된 확인자를 사용해 지정한 타입의 인스턴스를 생성합니다. \endif \if EN Creates an instance of the specified type using the configured resolver. \endif

type— \if KO 생성할 타입입니다. \endif \if EN The type to create. \endif

반환: \if KO 확인자가 생성한 인스턴스입니다. \endif \if EN The instance created by the resolver. \endif

FindViewModelType Method

\if KO 이름 규칙과 선택적 타입 목록을 사용해 View에 대응하는 ViewModel 타입을 찾습니다. \endif \if EN Finds the ViewModel type corresponding to a View using naming conventions and an optional type set. \endif

viewType— \if KO View 타입입니다. \endif \if EN The View type. \endif
cachedTypes— \if KO 선택적으로 미리 확보한 검색 타입 목록입니다. \endif \if EN An optional preloaded set of types to search. \endif

반환: \if KO 대응하는 ViewModel 타입이며, 찾지 못하면 입니다. \endif \if EN The matching ViewModel type, or when none is found. \endif

FindViewType Method

\if KO 이름 규칙에 따라 ViewModel 타입에 대응하는 View 타입을 찾습니다. \endif \if EN Finds the View type corresponding to a ViewModel type by naming convention. \endif

viewModelType— \if KO ViewModel 타입입니다. \endif \if EN The ViewModel type. \endif

반환: \if KO 대응하는 View 타입이며, 찾지 못하면 입니다. \endif \if EN The matching View type, or when none is found. \endif

GetAllLoadableTypes Method

\if KO 현재 AppDomain의 모든 비동적 어셈블리에서 로드 가능한 구체 클래스 타입을 가져옵니다. \endif \if EN Gets loadable concrete class types from all non-dynamic assemblies in the current AppDomain. \endif

반환: \if KO 캐시된 로드 가능 타입 배열입니다. \endif \if EN The cached array of loadable types. \endif

GetLoadableTypes Method

\if KO 지정한 어셈블리에서 로드 가능한 타입만 가져옵니다. \endif \if EN Gets only the types that can be loaded from the specified assembly. \endif

assembly— \if KO 검색할 어셈블리입니다. \endif \if EN The assembly to inspect. \endif

반환: \if KO 로드 가능한 타입 시퀀스이며, 조회 실패 시 빈 시퀀스입니다. \endif \if EN The loadable type sequence, or an empty sequence when inspection fails. \endif

Register Method

\if KO View와 ViewModel 타입 매핑을 수동으로 등록합니다. \endif \if EN Manually registers a View-to-ViewModel type mapping. \endif

viewType— \if KO View 타입입니다. \endif \if EN The View type. \endif
viewModelType— \if KO ViewModel 타입입니다. \endif \if EN The ViewModel type. \endif
RegisterAll Method

\if KO 지정된 어셈블리에서 View와 ViewModel 매핑을 자동으로 등록합니다. \endif \if EN Automatically registers View-to-ViewModel mappings from the specified assembly. \endif

assembly— \if KO 검색할 어셈블리입니다. \endif \if EN The assembly to search. \endif
RegisterResolver Method

\if KO 외부 ViewModel 확인자를 등록합니다. \endif \if EN Registers an external ViewModel resolver. \endif

resolver— \if KO 등록할 확인자 구현입니다. \endif \if EN The resolver implementation to register. \endif
Reset Method

\if KO 등록된 매핑, 타입 검색 캐시 및 확인자를 포함한 모든 로케이터 상태를 초기화합니다. \endif \if EN Clears all locator state, including mappings, type lookup caches, and the configured resolver. \endif

Resolve Method

\if KO View 타입에 대응하는 ViewModel 인스턴스를 확인합니다. \endif \if EN Resolves the ViewModel instance corresponding to a View type. \endif

viewType— \if KO View 타입입니다. \endif \if EN The View type. \endif

반환: \if KO 확인된 ViewModel 인스턴스이며, 타입을 찾지 못하면 입니다. \endif \if EN The resolved ViewModel instance, or when no matching type is found. \endif

ResolveView Method

\if KO ViewModel 타입에 대응하는 View 인스턴스를 생성합니다. \endif \if EN Creates the View instance corresponding to a ViewModel type. \endif

viewModelType— \if KO ViewModel 타입입니다. \endif \if EN The ViewModel type. \endif

반환: \if KO 생성된 View 인스턴스이며, 타입을 찾지 못하면 입니다. \endif \if EN The created View instance, or when no matching type is found. \endif

_allLoadableTypesCache Field

\if KO all Loadable Types Cache 값을 보관합니다. \endif \if EN Stores the all loadable types cache value. \endif

_map Field

\if KO map 값을 보관합니다. \endif \if EN Stores the map value. \endif

_resolver Field

\if KO resolver 값을 보관합니다. \endif \if EN Stores the resolver value. \endif

_syncRoot Field

\if KO sync Root 값을 보관합니다. \endif \if EN Stores the sync root value. \endif

_viewModelTypeCache Field

\if KO view Model Type Cache 값을 보관합니다. \endif \if EN Stores the view model type cache value. \endif

_viewTypeCache Field

\if KO view Type Cache 값을 보관합니다. \endif \if EN Stores the view type cache value. \endif

ViewNamingConvention

AddIfNotEmpty Method

\if KO 값이 비어 있지 않을 때만 목록에 추가합니다. \endif \if EN Adds a value to the list only when it is not empty or whitespace. \endif

list— \if KO 값을 추가할 목록입니다. \endif \if EN The destination list. \endif
value— \if KO 조건부로 추가할 값입니다. \endif \if EN The value to add conditionally. \endif
BuildNamespaceReplacementCandidates Method

\if KO 알려진 View 관련 네임스페이스 토큰을 지정한 토큰으로 바꾼 후보를 열거합니다. \endif \if EN Enumerates candidates formed by replacing known View-related namespace tokens. \endif

sourceNamespace— \if KO 원본 네임스페이스입니다. \endif \if EN The source namespace. \endif
replacement— \if KO 적용할 대체 토큰입니다. \endif \if EN The replacement token. \endif

반환: \if KO 바뀐 네임스페이스 후보 시퀀스입니다. \endif \if EN A sequence of replaced namespace candidates. \endif

BuildViewCandidateNamespaces Method

\if KO ViewModel 네임스페이스에서 View 검색용 네임스페이스 후보를 만듭니다. \endif \if EN Builds namespace candidates for locating a View from a ViewModel namespace. \endif

viewModelNamespace— \if KO 원본 ViewModel 네임스페이스입니다. \endif \if EN The source ViewModel namespace. \endif
rootNamespace— \if KO 루트 네임스페이스입니다. \endif \if EN The root namespace. \endif

반환: \if KO 중복이 제거된 네임스페이스 후보 배열입니다. \endif \if EN A distinct array of namespace candidates. \endif

BuildViewModelCandidateNamespaces Method

\if KO View 네임스페이스에서 ViewModel 검색용 네임스페이스 후보를 만듭니다. \endif \if EN Builds namespace candidates for locating a ViewModel from a View namespace. \endif

viewNamespace— \if KO 원본 View 네임스페이스입니다. \endif \if EN The source View namespace. \endif
rootNamespace— \if KO 루트 네임스페이스입니다. \endif \if EN The root namespace. \endif

반환: \if KO 중복이 제거된 네임스페이스 후보 배열입니다. \endif \if EN A distinct array of namespace candidates. \endif

BuildViewNamespaceCandidatesFromViewModel Method

\if KO ViewModel 네임스페이스에서 여러 View 네임스페이스 후보를 열거합니다. \endif \if EN Enumerates multiple View namespace candidates from a ViewModel namespace. \endif

sourceNamespace— \if KO 원본 ViewModel 네임스페이스입니다. \endif \if EN The source ViewModel namespace. \endif

반환: \if KO View 네임스페이스 후보 시퀀스입니다. \endif \if EN A sequence of View namespace candidates. \endif

ExpandParentNamespaces Method

\if KO 지정한 네임스페이스의 부모 네임스페이스를 가까운 순서대로 반환합니다. \endif \if EN Returns parent namespaces of the specified namespace from nearest to farthest. \endif

ns— \if KO 확장할 네임스페이스입니다. \endif \if EN The namespace to expand. \endif

반환: \if KO 부모 네임스페이스 시퀀스입니다. \endif \if EN The parent namespace sequence. \endif

GetRootNamespace Method

\if KO 전체 네임스페이스에서 첫 번째 루트 구간을 가져옵니다. \endif \if EN Gets the first root segment from a full namespace. \endif

ns— \if KO 전체 네임스페이스입니다. \endif \if EN The full namespace. \endif

반환: \if KO 루트 구간이며 입력이 비어 있으면 빈 문자열입니다. \endif \if EN The root segment, or an empty string when the input is empty. \endif

GetViewModelTypeNameCandidates Method

\if KO 지정한 View 타입에 대응하는 ViewModel 전체 타입 이름 후보를 만듭니다. \endif \if EN Creates full type-name candidates for a ViewModel corresponding to the specified View type. \endif

viewType— \if KO View 타입입니다. \endif \if EN The View type. \endif

반환: \if KO 구체적인 순서대로 정렬되고 중복이 제거된 ViewModel 전체 이름 후보입니다. \endif \if EN Distinct ViewModel full-name candidates ordered from most to least specific. \endif

GetViewTypeNameCandidates Method

\if KO 지정한 ViewModel 타입에 대응하는 View 전체 타입 이름 후보를 만듭니다. \endif \if EN Creates full type-name candidates for a View corresponding to the specified ViewModel type. \endif

viewModelType— \if KO ViewModel 타입입니다. \endif \if EN The ViewModel type. \endif

반환: \if KO 구체적인 순서대로 정렬된 View 전체 이름 후보이며 명명 규칙이 맞지 않으면 빈 배열입니다. \endif \if EN View full-name candidates ordered from most to least specific, or an empty array when the naming rule does not match. \endif

GetViewTypeNameCandidates Method

\if KO ViewModel 전체 타입 이름에서 View 전체 타입 이름 후보를 만듭니다. \endif \if EN Creates full type-name candidates for a View from a ViewModel full type name. \endif

viewModelFullName— \if KO ViewModel의 전체 타입 이름입니다. \endif \if EN The full type name of the ViewModel. \endif

반환: \if KO 중복과 원래 이름이 제거된 View 전체 이름 후보입니다. \endif \if EN View full-name candidates with duplicates and the original name removed. \endif

IsLikelyViewType Method

\if KO 지정한 타입이 알려진 View 명명 패턴에 해당하는지 판단합니다. \endif \if EN Determines whether the specified type is likely to be a View type. \endif

type— \if KO 검사할 타입입니다. \endif \if EN The type to inspect. \endif

반환: \if KO 알려진 View 명명 패턴과 일치하면 입니다. \endif \if EN when the type matches a known View naming pattern. \endif

ReplaceNamespaceToken Method

\if KO 네임스페이스의 중간 또는 마지막 토큰이 일치하면 이를 바꾼 값을 열거합니다. \endif \if EN Enumerates namespace values whose matching middle or final token has been replaced. \endif

sourceNamespace— \if KO 원본 네임스페이스입니다. \endif \if EN The source namespace. \endif
from— \if KO 찾을 토큰입니다. \endif \if EN The token to find. \endif
to— \if KO 대체 토큰입니다. \endif \if EN The replacement token. \endif

반환: \if KO 토큰이 바뀐 네임스페이스 시퀀스입니다. \endif \if EN A sequence of namespaces with the token replaced. \endif

DotViewModelToken Field

\if KO Dot View Model Token 값을 보관합니다. \endif \if EN Stores the dot view model token value. \endif

EmptyToken Field

\if KO Empty Token 값을 보관합니다. \endif \if EN Stores the empty token value. \endif

PageModelsToken Field

\if KO Page Models Token 값을 보관합니다. \endif \if EN Stores the page models token value. \endif

PageModelToken Field

\if KO Page Model Token 값을 보관합니다. \endif \if EN Stores the page model token value. \endif

PagesToken Field

\if KO Pages Token 값을 보관합니다. \endif \if EN Stores the pages token value. \endif

PageToken Field

\if KO Page Token 값을 보관합니다. \endif \if EN Stores the page token value. \endif

ViewModelsToken Field

\if KO View Models Token 값을 보관합니다. \endif \if EN Stores the view models token value. \endif

ViewModelToken Field

\if KO View Model Token 값을 보관합니다. \endif \if EN Stores the view model token value. \endif

ViewsToken Field

\if KO Views Token 값을 보관합니다. \endif \if EN Stores the views token value. \endif

ViewToken Field

\if KO View Token 값을 보관합니다. \endif \if EN Stores the view token value. \endif