XAML or HTML

032. 데이터템플릿 & 트리거(DataTemplate & Trigger) #3

XAML 뽀개기

이전 포스트에서는 데이타템플릿, 스타일을 컨버터, 셀렉터를 활용해 하나의 컨트롤에서 다양한 형태를 보여줄 있었습니다. 이번 포스트에서는 트리거를 이용하는 방법을 살펴봅니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Style & Template Trigger -->
<DataTemplate x:Key="DataTemplate_Message" DataType="{x:Type local:ExchangeMessage}">
    <StackPanel>
        <TextBlock x:Name="PART_Name" Text="{Binding Name}"/>
        <Border x:Name="PART_Background" Background="White" 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"/>
    </StackPanel>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsMine}" Value="True">
            <Setter Property="Background" Value="#FFFFEB33" TargetName="PART_Background"/>
            <Setter Property="HorizontalAlignment" Value="Right" TargetName="PART_Time"/>
            <Setter Property="Visibility" TargetName="PART_Name">
                <Setter.Value>Collapsed</Setter.Value>
            </Setter>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>
cs

 

이번에는 데이타템플릿이 하나만 필요합니다. 다른 사람의 메시지를 기본 템플릿으로 정의하고 데이타트리거를 이용해 메시지일 때에 달라져야하는 속성들을 정의합니다. 이전 예제와 동일한 형태로 정의하므로 자세한 프로퍼티 사항들은 생략합니다. 필요한 요소들을 TargetName 지정해야하므로 x:Name 선언하는 것에 주의합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Style x:Key="Style_Message" 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.Triggers>
                    <DataTrigger Binding="{Binding IsMine}" Value="True">
                        <Setter Property="HorizontalAlignment" Value="Right" TargetName="PART_ListBoxItem"/>
                        <Setter Property="Margin" Value="20,0,5,5" TargetName="PART_ListBoxItem"/>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        <Setter.Value>
    </Setter>
</Style>
cs


스타일도 데이타템플릿과 동일한 방법으로 데이타프리거를 이용합니다. PART_ListBoxItem의 HorizontalAlignment 속성은 Left, Margin은 5,0,20,5로 선언합니다. 기본으로 선언한 스타일은 다른 사람의 메시지 스타일이 됩니다. 다음엔 데이터트리거를 이용해서 내 메시지일 떄에 달라져야하는 속성들의 값을 반대로 정의합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
<!-- Style & Template Trigger -->
<ListBox ItemTemplate="{StaticResource DataTemplate_Message}" 
         ItemContainerStyle="{StaticResource Style_Message}"
         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

 

ItemTemplate ItemContainerStyle 선언하는 것은 이전과 다를 것이 없습니다.

 

 

실행해서 태스트해봅니다

 

컨버터를 사용할지 셀렉터를 사용할지 트리거를 사용할지는 여러분이 판단하시길 바랍니다. 여러 조건과 상황에 따른 옳은 판단이 중요할 같습니다.

 

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

010. 데이타트리거(DataTrigger)

XAML 뽀개기

데이타 트리거에 대해서 다시 한번 살펴봅니다.  

  

1
2
3
4
public class SampleData
{
    public string Type { get; set; }
}
cs

 

예제를 위해 간단히 SampleData 클래스를 추가합니다. string 타입의 Type .Net property 정의합니다.

 

1
2
3
4
5
6
7
8
<!-- Type 데이타 트리거 -->
<DataTrigger Binding="{Binding Type}" Value="Good">
    <Setter Property="TextBox.Foreground" Value="LimeGreen" />
</DataTrigger>
<!--OR-->
<DataTrigger Binding="{Binding Type}" Value="Bad">
    <Setter Property="TextBox.Foreground" Value="Red" />
</DataTrigger>
cs

 

Style_DataTriggerTextBox 스타일에 데이타 트리거를 추가하고 Type 프로퍼티와 바인딩합니다. Type이 Good이거나 Bad 호출됩니다.

 

1
2
3
4
<TextBox x:Name="txbGood" Style="{StaticResource Style_DataTriggerTextBox}"
         Text="Good"/>
<TextBox x:Name="txbBad" Style="{StaticResource Style_DataTriggerTextBox}"
         Text="Bad" Grid.Column="1"/>
cs

 

1
2
this.txbGood.DataContext = new SampleData { Type = "Good" };
this.txbBad.DataContext = new SampleData { Type = "Bad" };
cs

 

비하인드코드에서 TextBox의 DataContext에 SampleData가 생성되도록 정의합니다.


 


런타임에서만 확인됩니다.

 

여기서 Text 속성은 의미가 없습니다. DataContext를 통해 바인딩된 Type 프로퍼티에 의해 호출됩니다.

 

멀티 데이타 트리거에 대해서도 다시 한번 살펴봅니다.  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 멀티 데이타 트리거 -->
<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Type}" Value="Good"/>
        <Condition Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="Good"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="TextBox.Foreground" Value="LimeGreen"/>
    <Setter Property="TextBox.FontWeight" Value="Bold"/>
</MultiDataTrigger>
<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Type}" Value="Bad"/>
        <Condition Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="Bad"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="TextBox.Foreground" Value="Red"/>
    <Setter Property="TextBox.FontWeight" Value="Bold"/>
</MultiDataTrigger>
cs

 

기존 스타일에 멀티 데이타 트리거를 추가합니다. TextBox Text 속성을 추가 조건으로 추가했습니다. 그리고 FontWeight 속성이 Bold 되도록 Setter 추가했습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
<TextBox x:Name="txbGood" Style="{StaticResource Style_DataTriggerTextBox}"
         Text="Good">
    <TextBox.DataContext>
        <local:SampleData Type="Good"/>
    </TextBox.DataContext>
</TextBox>
<TextBox x:Name="txbBad" Style="{StaticResource Style_DataTriggerTextBox}"
         Text="Bad" Grid.Column="1">
    <TextBox.DataContext>
        <local:SampleData Type="Bad"/>
    </TextBox.DataContext>
</TextBox>
cs

 

기존 비하인드코드에서 DataContext에 생성한 SampleData 주석처리하고 XAML 코드에서 생성하도록 변경했습니다. 멀티 데이타 트리거의 조건에 맞게 Type Text 준비되었습니다.


 


이제는 디자인 타임에서도 바로 확인 가능합니다. 빌드를 해줘야 보입니다.

 


런타임에서 확인해보면 데이타 트리거 멀티 데이타 트리거 모두 동작하는 것을 확인할 있습니다.


샘플 코드 : https://github.com/CharlesKwon/XamlSimplified

009. 4가지 트리거(Triggers)

XAML 뽀개기

Style Trigger 비슷 점이 많습니다. 다른 점이라면 스타일은 무조건 적용되는 반면 트리거는 조건을 수반한다는 점입니다.

 

트리거는 4가지 종류가 있습니다.

 

  1. Property : Dependency Property 변경될 호출됩니다.
  2. Event : 이벤트가 발생할 호출됩니다.
  3. Data : Binding 문법으로 연결된 .NET property 특정 데이타일 호출됩니다.
  4. Multi(& MultiData) : 조건을 다수 사용해 논리곱(AND) 관계를 정의합니다.

 

1
2
3
<TextBox Style="{StaticResource Style_BlueFocusTextBox}"/>
<TextBox Style="{StaticResource Style_BlueFocusTextBox}"
         Grid.Column="1"/>
cs

 

2개의 TextBox에 동일한 스타일을 적용했습니다. 2개를 예제로 이유는 포커스 상태를 쉽게 잃기 위함일뿐 다른 이유는 없습니다.

 

1. 프로퍼티 트리거


1
2
3
4
5
6
7
8
9
10
<Style x:Key="Style_BlueFocusTextBox">
    <Style.Triggers>
        <!-- IsFocused 프로퍼티 트리거 -->
        <Trigger Property="TextBox.IsFocused" Value="True">
            <Setter Property="TextBox.BorderThickness" Value="3"/>
        </Trigger>
    </Style.Triggers>
    <Setter Property="Control.Width" Value="60"/>
    <Setter Property="Control.Height" Value="30"/>
</Style>
cs

 

TextBox Focused 상태(State) 되면 IsFocused 속성이 True 변경됩니다. 조건일 BorderThickness 속성이 3으로 변경되는 트리거를 정의했습니다.

 

 

해당 조건을 벗어나면 추가 코드 없이도 트리거가 자동 취소됩니다.

 

2. 이벤트 트리거


1
2
3
4
5
6
7
8
9
10
<Storyboard x:Key="MouseEnterAnimation">
    <ColorAnimation 
        Storyboard.TargetProperty="(TextBox.Background).(SolidColorBrush.Color)" 
        To="LightGray" Duration="0:0:0.1"/>
</Storyboard>
<Storyboard x:Key="MouseLeaveAnimation">
    <ColorAnimation 
        Storyboard.TargetProperty="(TextBox.Background).(SolidColorBrush.Color)" 
        To="White" Duration="0:0:0.1"/>
</Storyboard>
cs


1
2
3
4
5
6
7
8
<!-- MouseEnter 이벤트 트리거 -->
<EventTrigger RoutedEvent="TextBox.MouseEnter">
    <BeginStoryboard Storyboard="{StaticResource MouseEnterAnimation}"/>
</EventTrigger>
<!-- MouseLeave 이벤트 트리거 -->
<EventTrigger RoutedEvent="TextBox.MouseLeave">
    <BeginStoryboard Storyboard="{StaticResource MouseLeaveAnimation}"/>
</EventTrigger>
cs

  

MouseEnter와 MouseLeave 이벤트가 발생할 MouseEnterAnimation와 MouseLeaveAnimation Storyboard 각각 실행합니다. 스토리보드는 리소스에 정의합니다. 이벤트 트리거는 프로퍼티 트리거와 약간 다릅니다. 스토리보드를 사용하기 때문에 트리거를 취소하려면 이전 트리거에 반하는 또는 그에 준하는 트리거를 추가해야 합니다.

 

 

이렇게 추가 코드가 없으면 트리거가 자동 취소되지 않습니다. 스토리보드의 동작을 이해한다면 알맞은 트리거를 추가할 있습니다.

 

3. 데이 트리거


1
2
3
4
<!-- Text 데이타 트리거 -->
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="Bad">
    <Setter Property="TextBox.Foreground" Value="Red" />
</DataTrigger>
cs

 

 

데이타 트리거는 프로퍼티 트리거와 비슷합니다. 예제는 Bad라는 문자열이 Text 속성에 들어왔을 호출됩니다.

 

4. 멀티 트리거


1
2
3
4
5
6
7
8
9
10
<!-- Text 멀티 트리거 -->
<MultiTrigger>
    <MultiTrigger.Conditions>
        <Condition Property="TextBox.Text" Value="Good"/>
        <!-- AND -->
        <Condition Property="TextBox.IsMouseOver" Value="True"/>
    </MultiTrigger.Conditions>
    <Setter Property="TextBox.Foreground" Value="LimeGreen"/>
    <Setter Property="TextBox.FontWeight" Value="Bold"/>
</MultiTrigger>
cs


1
2
3
4
<TextBox Style="{StaticResource Style_BlueFocusTextBox}" 
         Tag="Good"/>
<TextBox Style="{StaticResource Style_BlueFocusTextBox}"
         Grid.Column="1"/>
cs



이전에 정의한 각각의 트리거가 논리합(OR)이라면, 멀티트리거는 논리곱(AND)이라 있습니다. 정의된 모든 Condition이 참일 트리거가 호출됩니다. 예제에서는  Text 속성 Good으로 참이되고 IsMouseOver 속성이 True로 참이될 트리거가 호출됩니다. 

 

5. 멀티 데이 트리거


1
2
3
4
5
6
7
8
9
10
<!-- Text  & Tag 멀티 데이타 트리거 -->
<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="Bad"/>
        <!-- AND -->
        <Condition Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Bad"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="TextBox.Foreground" Value="Red"/>
    <Setter Property="TextBox.FontWeight" Value="Bold"/>
</MultiDataTrigger>
cs

  

1
2
3
4
<TextBox Style="{StaticResource Style_BlueFocusTextBox}" 
         Tag="Good"/>
<TextBox Style="{StaticResource Style_BlueFocusTextBox}"
         Grid.Column="1" Tag="Bad"/>
cs

 


이번엔 설명이 필요없을 같습니다. 기존 두번째 TextBox  Tag="Bad"를 추가하 트리거의 모든 조건이 참이 되도록 했습니다. DataTrigger 대해서는 다음 포스트에서 다시 살펴보겠습니다.


샘플 코드 : https://github.com/CharlesKwon/XamlSimplified