WPF 스터디 3회 후기 | 이재웅

안녕하세요. 이재웅입니다!
2월 25일에 있었던 WPF 스터디 3회 후기를 남겨봅니다.

어느 덧 벌써 3번의 스터디를 진행했네요? (시간 참 빠르죠)

인원

이번에도 많은 분들께서 참여해주셨습니다. 저를 포함해 22명의 인원이 함께 했습니다.
다시 한번 참여해주신 모든 분들에게 감사 인사 드립니다.

그리고 개인 사정 상 참여하지 못하신 분들도 미리 여부를 알려주셔서 참 고마웠습니다. :smile:

(모두 모인 사진을 못 찍었어요 아쉽 ㅠㅠ)

후원

그리고 이번 후원도 백승찬 대표님께서 연속으로 후원해주셨습니다.
(남은 과자는 제가 집에 가서 다 해치웠습니다.)

장소

이번에도 역시 지난번과 같은 곳에서 진행하게 되었습니다.

4회까지는 계속 쭉~ 이렇게 갑니다.

  • [온라인끝] 서울특별시 금천구 서부샛길 606 A동 16층 1608호
  • 토요일 13:00 ~ 17:00

열기

이번 모임은 열기가 지난 번 보다 뜨거웠습니다. 왜냐구요? 제가 중간 중간 어리버리 하는 바람에 많은 분들이 적극적으로 서포팅을 해주셨어요. 그래서 뜻하지 않게 역대 소통이 가장 잘된 회차가 아니었나 싶습니다. :smile: (오히려 좋아)

그리고 Prism 커밍아웃 드립도 기억납니다.

Prism 장단점?

이때 반응이 좋았죠… (저는 반응 좋았던 기억만 간직합니다…)

오프닝

멀리서 오시는 분들을 기다리는 동안 잠깐 저의 ChatGTP 활용 방법과 사례에 대해 소개를 했습니다. (제 기억으로는 빵빵 터진 것 같은데 왜곡된 기억일 수도…) 혹시 기억나는 드립 있으시면 댓글로좀 남겨주세요…

내용

2회 스터디 후기 때 예고한 대로 Prism과 CommunityToolkit.Mvvm을 진행했습니다. 이번에는 참고할 소스코드 양이 많아 Markdown 문서를 시작 전에 미리 준비했었습니다. 문서 보기

어떠셨나요? 제 개인적인 의견으로는 기존의 PPT 보다는 훨씬 도움이 될 것 같다는 생각이 들었습니다. 사실 PPT으로 준비한 것이 크게 없었죠. ㅎㅎㅎ

그럼 본격적으로 내용을 한번 복기 해볼게요.

가장 먼저 PrismApplication을 살펴봤죠.

PrismBootstrapper를 통해 Application을 셋팅하고 동작시킬 수도 있지만 PrismApplication을 직접 상속받는다면 CreateShell, RegisterTypes와 같은 abstract 추상 메서드 시점도 확보할 수 있고 더 나아가 ConfigureModuleCatalog(override)를 제어할 수 있는 시점까지도 내부적으로 가질 수 있다는 것을 알아봤습니다.

물론 순서가 막 뒤죽박죽이었던 것도 기억하시죠?

그 다음 Application 메서드 안에 internal App WireViewModel 메서드를 만들어서 ViewModelLocationProvider를 통한 View, ViewModel간 등록 시나리오를 수동으로 제어하는 방법에 대해 확인해봤습니다.

ViewModelLocationProvider.Register<MainContent, MainContentViewModel>();

또한 Chain 메서드 형식으로 동작시키기 위해 아래처럼 메서드를 구성했죠.

internal App WireViewModel()
{
    ViewModelLocationProvider.Register ...
    return this;
}

저도 후기를 작성하며 지난 내용들이 새록새록 기억이 납니다… :smile:

그리고 ViewModelLocationProvider.Register를 통해 뷰/뷰모델 시나리오를 등록했다 하여도 WireDataContextChanged 콜백 메서드 등록을 통한 수동 DataContext 연결이 없다면 아무 소용 없다는 것도 함께 확인 했어요!

그리고 이 것을 수행하기 위해 Core 클래스 라이브러리 프로젝트를 만들고 PrismContent라는 ContentControl을 상속받는 View 객체를 준비했습니다.

public class PrismContent : ContentControl
{
    public PrismContent()
    {
        ViewModelLocationProvider
            .AutoWireViewModelChanged(this, WireViewModelChanged);
    }
}

콜백 메서드도 이렇게 만들었죠

private void WireViewModelChanged(object arg1, object arg2)
{
    if (arg1 is FrameworkElement fe && arg2 is INotifyPropertyChanged vm)
    {
        fe.DataContext = vm;
    }
}

또 틈새를 이용한 FrameworkElement 드립도 있었습니다. :smile:
오 이제 이렇게 하면 View와 DataContext의 시점까지도 명확하게 제어할 수 있게 되었습니다.

그리고 IoC Container를 담당하는 처리가 바로 이 부분이었다는 것도 한번 언급했었죠.

ViewModelLocationProvider
            .AutoWireViewModelChanged(this, WireViewModelChanged);

바로 여기서 this를 넘겨주며 앞서 ViewModelLocationProvider.Register를 통해 등록한 View와 ViewModel을 찾아 리플렉션을 통해 생성자의 parameter를 찾아 주입시켜 줄 것입니다!

그리고 Region을 활용하는 방법에 대해서도 살펴봤습니다.

우선 대표적인 방법으로는 Attached DependencyProperty을 활용하는 것이 기본적입니다.

prism:RegionManager.RegionName="RegionName"

그리고 여기에 더해 우리는 Region을 직접 구현하고 수동으로 등록하는 방법에 대해 시도해봤습니다.

그러기 위해서 이번에도 Core 안에 ContentControl을 상속받는 PrismRegion 클래스를 구현했어요.

public class PrismRegion : ContentControl
{
    public static readonly DependencyProperty ContentNameProperty = 
        DependencyProperty.Register(
            "RegionName", typeof(string), typeof(PrismRegion), 
            new PropertyMetadata(ContentNamePropertyChanged));

    public string RegionName
    {
        get => (string)GetValue(ContentNameProperty);
        set => SetValue(ContentNameProperty, value);
    }
}

그리고 아래는 콜백 함수였죠.
참고로 DP 형식만 다를 뿐 내부적으로 등록되는 Region 동작은 동일합니다.

private static void ContentNamePropertyChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue is string str
        && string.IsNullOrEmpty(str) == false
        && Application.Current?.CheckAccess() == true)
    {
        IRegionManager rm = RegionManager
            .GetRegionManager(Application.Current.MainWindow);

        RegionManager.SetRegionName((PrismRegion)d, str);
        RegionManager.SetRegionManager(d, rm);
    }
}

이렇게 함으로써 Region을 좀 더 자유롭게 제어할 수 있게 됩니다. 그리고 Region이 제대로 등록되었는지도 명확하게 확인할 수 있겠죠?

그리고 RegisterInstance/RegisterSingleton 을 통한 객체 등록 방법에 대해서도 자세하게 알아봤습니다.

이 부분은 제가 일전에 작성해둔 내용을 참고해주시면 좋습니다!! 여기 꼭!!

그리고 EventAggregator에 대해서도 자세하게 살펴봤어요.

TBD… (추후에 작성!! 한번에 다 쓸려니 손가락 아프네요. :joy:)

그리고 CommunityToolkit.Mvvm을 통해 ObservableObject을 상속바다 ViewModel 구현을 해보았고 partial 키워드가 왜 필요한지에 대해서도 제가 계속 실수하는 바람에 몇번을 반복해서 강조했습니다. (오히려 좋았어요. :smile: 긍정)

[ObservableProperty]를 통해 private field를 생성하고 코드 생성기를 통한 Property 자동 생성도 확인했습니다.

Field를 만드는 순간 Property가 바로 튀어나왔었죠!

[ObservableProperty]
private string _name;

public MainViewModel()
{
    Name << // 프라퍼티 자동 생성~
}

그밖에도 자동으로 생성되는 On<필드명>Changed를 통해 생략된 Property Set 처리를 이어나갈 수 있는 방법에 대해 살펴보았습니다. 또한 이 자동 생성 메서드는 partial으로 만들어져 있었죠. 그래서 내부적으로는 우리가 OnChanged 또는 OnChanging 메서드를 사용하든 안하든 무조건 호출되고 있다는 것도 한번 언급했어요.

그리고 툴킷은 RelayCommand도 아주 편리하게 제공하고 있었죠.

[RelayCommand]
private void Save()
{

}

Parameter가 있다면 입력하면 되고 없다면 위 샘플처럼 없어도 상관 없었어요. 눈치 채셨나요? 이 것은 ICommand 프로퍼티도 아니고 그냥 단순한 private 메서드일 뿐이었죠. 이번에도 [RelayCommand] 어트리뷰트를 통해 내부에서 자동으로 SaveCommand가 생성된 것이었었죠. 그래서 내부적으로 SaveCommand가 생성되면서 Save() 메서드를 콜백하는 코드까지도 자동 생성되었어요.

그리고 RelayCommand 어트리뷰트를 더 확장해 CanExecute 처리까지 하는 방법에 대해서도 살펴봤어요.

RelayCommand + CanExecute 제가 예전에 작성했던 내용도 잠시 소개들였었죠.

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string _keywords;

private bool CanSave(object o)
{
    return !string.IsNullOrWhiteSpace(Keywords) && Keywords.Length > 0;
}

[RelayCommand(CanExecute = nameof(CanSave))]
private void Save(object o)
{
    // Save.
}

그래서 종합적으로 보면 CommunityToolkit.Mvvm의 Attribute를 적극 사용함으로 인해 매우 많은 부분의 코드 자동화를 이용할 수 있었습니다.

참고로 닷넷 프레임워크 버전에서는 아쉽게도 코드 자동 생성 기능을 사용할 수 가 없습니다. 고로 제공되는 Attribute들을 제대로 활용할 수가 없다는 말이죠.

그리고 제가 사전에 준비한 TeamViewer2 프로젝트에 대한 이야기도 잠시 나누었죠… 개인적으로도 이것에 대한 좋은 의견도 많이 남겨주셨어요. 이번 주에는 꼭 써먹을 수 있길 기대하며!!

끝으로

사실 후기를 토요일날 썼어야 했나봅니다… 몇일 지나서 작성하려니 기억이 잘 안나요. :rofl:
다음 4회 후기는 당일에 작성해봐야 겠습니다.

그리고 제가 준비한 WPF 스터디 3회 후기 내용은 여기까지 입니다.
매주 이렇게 모여주신 열정과 응원에 다시 한번 모든 분들께 감사드리며 다들 정말 고생 많으셨습니다. :smile:

감사합니다. 이재웅 드림

ps. 이번에도 (주)커넥트시스템 백승찬대표님 간식 후원 정말 고맙습니다. 그리고 4회에도 노트북 들고 스터디에도 참여하실 예정입니다. 정말 멋지십니다.

그리고 4회에는 1/2/3회에 진행했던 모든 내용이 들어있는 TeamViewer2 프로젝트를 리뷰하는 시간을 가져볼 계획입니다. 또 감사하게도 4회에는 Infragistics(인프라지스틱스) 에서 간식을 지원해주시기로 하셨습니다.

역시나 홍보, 광고 등의 이점이 없음에도 불구하고 닷넷과 WPF 발전을 위해 도움을 주시기로 하셨습니다.

또한 인프라지스틱스는 닷넷데브 행사 등 국내외 개발자들을 위한 다양한 행사에 아낌없이 지원해주시는 것으로 알고 있기 때문에 저 역시 한국의 한 개발자로써 감사드립니다. (리스펙)

9개의 좋아요

역시나 홍보, 광고 등의 이점이 없음에도 불구하고 닷넷과 WPF 발전을 위해 도움을 주시기로 하셨습니다.

이 부분은 정말 리스펙…(하지만 어차피 영업의 기본이 인식이기 때문에 후원해주신 분들에게 좋은 인식이 생겨서 순리대로 자연스럽게 홍보가 되지 않을지…)

3개의 좋아요

후기라 쓰고 강의 노트를 써버리셨군요…
마침 너무 필요한 내용이라 도움이 됩니다 ㅎㅎ 감사합니다

4개의 좋아요