안녕하세요.
WPF 고민 해소를 위한 SLOG는…
WPF 작업을 할 때 잘 풀리지 않거나 고민 되는 부분들을 알아보고 해결해나가기 위한 공간입니다.
그리고 함께 공유하고 싶은 내용들을 하나 씩 작성할 계획입니다.
감사합니다.
안녕하세요.
WPF 작업을 할 때 잘 풀리지 않거나 고민 되는 부분들을 알아보고 해결해나가기 위한 공간입니다.
그리고 함께 공유하고 싶은 내용들을 하나 씩 작성할 계획입니다.
감사합니다.
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를 사용해보면 어떨까? 하는 생각을 해봅니다.
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
요 정도면 되지 않을까요?
저도 @Greg.Lee 님 의견과 같은 생각이고 폐기한 사례입니다!
그리고 자세하게 좋은 설명 해주신 내용도 조만간 정리해서 글 이어나가 보도록 할게요.
그리고 앞서 언급 했던 프로젝트 속성 창도 만들어 보고 있는데, 공유 해보면 재밌을 것 같습니다!
아, 참고로 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}"/>
요정도면 되지 않을까 하네욤 ~ㅂ~
TBD…
기존 WPF 개발 방식에서 CommunityToolkit.Mvvm 대체 부분에 대한 연구 중…
기존 방식에서는 ClickCommand 속성과 초기화 선언, 메서드 까지 3개의 필수 구현 요소가 필요합니다.
public ICommand ClickCommand { get; init }
public MainViewModel()
{
ClickCommand = new RelayCommand<string>(Click);
}
private void Click(object value)
{
}
소스코드의 양은 여전히 많지만 장점이라면 선언 부분을 정확하게 확인하고 참조 부분이 모두 연결되어있다는 점입니다.
이에 반해, CommunityToolkit.Mvvm에서는 RelayCommand Attribute만으로도 나머지 필수 요소들이 또 다른 partial 클래스에 자동으로 생성되기 때문에 잔 실수도 줄어들게 됩니다.
[RelayCommand]
private void Click(object value)
{
}
물론 처음에는 기존 참조 체계의 익숙함 때문에 분석이나 디버깅 등을 할 때 어색함을 계속 느낄 수 있습니다.