WPF 고민 해소를 위한 SLOG

안녕하세요. :slight_smile:

WPF 고민 해소를 위한 SLOG는…

WPF 작업을 할 때 잘 풀리지 않거나 고민 되는 부분들을 알아보고 해결해나가기 위한 공간입니다.

그리고 함께 공유하고 싶은 내용들을 하나 씩 작성할 계획입니다.

감사합니다.

좋아요 7

DataTemplateSelector에 대한 고민입니다.

TabControl 또는 TabItem에서 ContentDataTemplateSelector를 사용했는데,
매번 선택할 때 마다 SelectTemplate 메서드가 호출되는데요.

제가 DataTemplate에 각각 UserControl을 넣어버렸습니다.
(무겁게도)

<DataTemplate x:Key="GettingStarted">
    <UserControl1/>
</DataTemplate>
<DataTemplate x:Key="CommitHistory">
    <UserControl2/>
</DataTemplate>

이렇다 보니 탭 변경이 발생할 때 마다 SelectTemplate이 호출되면서 UserControl 무게에 따라 전환되는 움직임이 느려졌습니다.

그래서 무거운 화면을 DataTemplate에 넣는 것이 잘못 되었다고 생각했는데요.

애초에 TabItem의 Content에는 모델을 넣는 것이 아닌 UI 객체를 넣는 것이 옳은 방법일까요?


제 생각을 정리하면,

이렇습니다.

<!-- 이건 기존 보통의 방식이고 -->
<TabItem Header="Getting Started.">
    <TabItem.Content>
        <UserControl1>
            <UserControl1.DataContext>
                 <data:UserControl1ViewModel/>
            </UserControl1.DataContext>
        </UserControl1>
    </TabItem.Content>
</TabItem>
<!-- 이건 제가 구현했다 폐기한 방식입니다. -->
<DataTemplate x:Key="GettingStarted">
    <UserControl1/>
</DataTemplate>
<DataTemplate x:Key="CommitHistory">
    <UserControl2/>
</DataTemplate>

<Style TargetType="TabItem">
    <Setter Property="ContentDataTemplateSelector" Value="ContentTempalteRoute"/>
</Style>

그래서 DataTemplate은 Content를 아예 바꾸기 위한 처리가 아니구나? 라고 이해했어요.

TabControl을 ListBox처럼 가볍게 생각하고 만들었다면 어땠을까 생각도 해봤는데요.

그래서 Visual Studio 프로젝트 속성 화면에 있는 텝과 같은 가벼운 TabControl을 만들 때 DataTemplateSelector를 사용해보면 어떨까? 하는 생각을 해봅니다.

좋아요 7

TemplateSelector 호출이 무겁다는 게 잘 이해가 안 가는데요.
TemplateSelector 호출 자체가 무겁다면 ItemsSource 를 이용해 호출하면 됩니다만…

제가 보기엔 View 로딩 자체가 무겁다는 것으로 인지가 됩니다.
결국 매 탭 스위칭 마다 view 가 새로 생성되어 로딩되는 게 문제인 거 같습니다.
이건 TemplateSelector를 이용한 해결보다는 View cache 를 이용한 해결이 더 적절한 방향이 아닌가 싶어요.

물론 TabControl 역시 ItemsSource에서 파생되었고
기본적으로 ItemsSource 는 ItemsSource 로 로딩되는 View 들을 cache 하지만

할당되는 대상 ItemsSource 들의 타입이 같을 때만 cache가 됩니다.

만약 각 ViewModel 마다 다른 DataTemplate을 렌더링할 용도로
ItemsSource 에 각기 다른 타입의 객체를 할당한 후 DataTemplate으로 연결하면
해당 view 들은 cache되지 않고매 view 가 표시될 때마다 새로 생성이 되어 로딩됩니다.
(아마도 그래서 무겁다고 느껴지는 거라고 예상이 됩니다…-ㅂ-)

따라서 제가 생각하기에는 View cache 쪽을 좀 더 손보는 게 어떨까하네요.

그럼 View cache는 어떻게 하냐… 하고 물으신다면…

https://blog.naver.com/vactorman/222449542805

요 정도면 되지 않을까요?

좋아요 2

저도 @Greg.Lee 님 의견과 같은 생각이고 폐기한 사례입니다!

그리고 자세하게 좋은 설명 해주신 내용도 조만간 정리해서 글 이어나가 보도록 할게요. :slight_smile:

그리고 앞서 언급 했던 프로젝트 속성 창도 만들어 보고 있는데, 공유 해보면 재밌을 것 같습니다!

좋아요 2

아, 참고로 ItemsSource 를 이용하는 건 이런 식으로 할 수 있을 거 같습니다.

public abstract class State {}

public class Started : State {}

public class Stopped : State {}

public class ViewModel
{
   public ObservableCollection<State> Source {get;} = new ObservableCollection<State>();
}

<DataTemplate DataType="{x:Type Started}">
   <utils:ViewCache ContentType="{x:Type views:ItemView}" />
</DataTemplate>
<DataTemplate DataType="{x:Type Stopped}">
   <utils:ViewCache ContentType="{x:Type views:OtherView}" />
</DataTemplate>
<TabControl ItemsSource="{Binding Source}"/>

요정도면 되지 않을까 하네욤 ~ㅂ~

좋아요 1