본문 바로가기

XAML 뽀개기

031. 데이터템플릿 & 셀렉터(DataTemplate & Selector) #2

이전 포스트에서는 데이타템플릿(DataTemplate) 하는 기능을 알아보았고 컨버터(Converter) 이용해 내가 보낸 메시지와 다른 사람이 보낸 메시지를 다르게 보이게 하는 방법을 살펴봤습니다. 이번 포스트에서는 셀렉터(Selector) 어떤 기능을 하는지 어떻게 사용하는지 살펴봅니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Template Selector -->
<DataTemplate x:Key="DataTemplate_Message_Left" DataType="{x:Type local:ExchangeMessage}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" 
                   Visibility="{Binding IsMine, Converter={StaticResource BooleanToVisibilityConverter}, 
                                                ConverterParameter=inverted}"/>
    <Border Background="{Binding IsMine, Converter={StaticResource BooleanToBrushConverter}}" 
            CornerRadius="5" Margin="0,3">
        <TextBlock Text="{Binding Message}" TextWrapping="Wrap" Grid.Column="1" FontSize="11" Margin="5,3,5,7"/>
    </Border>
    <TextBlock x:Name="PART_Time" Text="{Binding ConfirmedTime, StringFormat=hh:mm:ss}" FontSize="8"/>
</DataTemplate>
cs

 

기존의 데이타템플릿의 키값을 DataTemplate_Message_Left로 변경하고 전체 XAML 코드를 [복사 > 붙여넣기]합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
<DataTemplate x:Key="DataTemplate_Message_Right" DataType="{x:Type local:ExchangeMessage}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" 
                   Visibility="{Binding IsMine, Converter={StaticResource BooleanToVisibilityConverter}, 
                                                ConverterParameter=inverted}"/>
        <Border Background="{Binding IsMine, Converter={StaticResource BooleanToBrushConverter}}" 
                CornerRadius="5" Margin="0,3">
            <TextBlock Text="{Binding Message}" TextWrapping="Wrap" Grid.Column="1" FontSize="11" Margin="5,3,5,7"/>
        </Border>
        <TextBlock x:Name="PART_Time" Text="{Binding ConfirmedTime, StringFormat=hh:mm:ss}" FontSize="8"
                   HorizontalAlignment="Right"/>
    </StackPanel>
</DataTemplate>
cs

 

복사+붙여넣기한 데이타템플릿의 키값은 DataTemplate_Message_Right으로 변경합니다. 데이타템플릿이 2 준비되었습니다. 확인 시간을 오른쪽으로 정렬하기 위해 PART_Time의 HorizontalAlignment 속성을 Right 선언했습니다.

 

MessageStyleTemplateSelector.cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MessageTemplateSelector : DataTemplateSelector
{
    public DataTemplate LeftTemplate { get; set; }
    public DataTemplate RightTemplate { get; set; }
    public string PropertyToCheck { get; set; }
    public string PropertyValue { get; set; }
 
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var exchangeMessage = (ExchangeMessage)item;
        var type = exchangeMessage.GetType();
        var property = type.GetProperty(PropertyToCheck);
 
        if (property.GetValue(exchangeMessage, null).ToString() == PropertyValue)
        {
            return RightTemplate;
        }
        else
        {
            return LeftTemplate;
        }
    }
}
cs

 

DataTemplateSelector를 상속 받은 MessageTemplateSelector 추가합니다. SelectTemplate 메소드를 재정의합니다. Selector 작성할 메소드를 재정의하는 일은 필수입니다. 사용자가 지정한 프로퍼티(PropertyToCheck) 지정한 (PropertyValue) 일치하면 지정한 데이터템플릿을 리턴하도록 코드를 작성했습니다.

 

1
xmlns:slt="clr-namespace:Sample.Selector" 
cs

 

셀렉터를 XAML 코드에서 사용하려면 네임스페이스를 선언해야 합니다.

 

1
2
3
4
<slt:MessageTemplateSelector x:Key="MessageTemplateSelector" 
                             PropertyToCheck="IsMine" PropertyValue="True" 
                             LeftTemplate="{StaticResource DataTemplate_Message_Left}" 
                             RightTemplate="{StaticResource DataTemplate_Message_Right}"/>
cs

 

MessageTemplateSelector를 리소스에 선언합니다. 이전 포스트에서는 IsMine 프로퍼티에 True 값이 될때 컨버터가 동작하도록 했었습니다. 이번에도 마찬가지 방식으로 지정한 데이타템플릿이 리턴되도록 했습니다.

 

1
2
3
4
5
6
7
8
9
10
11
<!-- Style & Template Selector -->
<ListBox ItemTemplateSelector="{StaticResource MessageTemplateSelector}" 
         Grid.Column="1">
    <ListBox.ItemsSource>
        <x:Array Type="{x:Type local:ExchangeMessage}">
            <local:ExchangeMessage Name="X Friend" Message="Charles~ R u there?" ConfirmedTime="2018-03-01 12:24:33" IsMine="False"/>
            <local:ExchangeMessage Name="Me" Message="Yeah~ i'm here. What's up?" ConfirmedTime="2018-03-01 12:25:44" IsMine="True"/>
            <local:ExchangeMessage Name="X Friend" Message="Do u have time tonight? Let's get it!" ConfirmedTime="2018-03-01 12:26:55" IsMine="False"/>
        </x:Array>
    </ListBox.ItemsSource>
</ListBox>
cs

 

ItemTemplateSelector 속성에 셀렉터를 선언합니다.

 


확인 시간이 오른쪽으로 정렬되었습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Style Selector -->
<Style x:Key="Style_Message_Left" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <ContentPresenter x:Name="PART_ListBoxItem" 
                                  HorizontalAlignment="Left" 
                                  Margin="5,0,20,5"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="Style_Message_Right" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <ContentPresenter x:Name="PART_ListBoxItem" 
                                  HorizontalAlignment="Right" 
                                  Margin="20,0,5,5"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
cs

 

추가로 스타일셀렉터를 살펴봅니다. 아이템 전체를 오른쪽으로 정렬하려고 합니다. 데이타템플릿과 마찬가지로 Style_Message_Left, Style_Message_Right 2개의 스타일을 정의합니다. PART_ListBoxItem의 HorizontalAlignment, Margin 속성을 서로 반대로 정의합니다.

 

1
2
3
4
<slt:MessageStyleSelector x:Key="MessageStyleSelector" 
                          PropertyToCheck="IsMine" PropertyValue="True"
                          LeftStyle="{StaticResource Style_Message_Left}"
                          RightStyle="{StaticResource Style_Message_Right}"/>
cs

 

MessageTemplateSelector와 마찬가지로 MessageStyleSelector를 리소스에 선언합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Style & Template Selector -->
<ListBox ItemTemplateSelector="{StaticResource MessageTemplateSelector}" 
         ItemContainerStyleSelector="{StaticResource MessageStyleSelector}" 
         Grid.Column="1">
    <ListBox.ItemsSource>
        <x:Array Type="{x:Type local:ExchangeMessage}">
            <local:ExchangeMessage Name="X Friend" Message="Charles~ R u there?" ConfirmedTime="2018-03-01 12:24:33" IsMine="False"/>
            <local:ExchangeMessage Name="Me" Message="Yeah~ i'm here. What's up?" ConfirmedTime="2018-03-01 12:25:44" IsMine="True"/>
            <local:ExchangeMessage Name="X Friend" Message="Do u have time tonight? Let's get it!" ConfirmedTime="2018-03-01 12:26:55" IsMine="False"/>
        </x:Array>
    </ListBox.ItemsSource>
</ListBox>
cs

 

ItemContainerStyleSelector속성에 셀렉터를 선언합니다.


 

실행해서 태스트해봅니다

 

이처럼 셀렉터는 하나의 컨트롤에 여러 개의 템플릿이나 여러 개의 스타일을 함께 표현할 있게 해줍니다.

 

참고 : JERRIE PELSER > 3 Techniques you can use to make your data templates dynamic