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

025. 커스텀 트리거 & 액션(Custom Trigger & Action) #2

XAML 뽀개기

이전 포스트에서 기본 XAML 코드와 기본 CS 코드를 준비했습니다.



간단한 시계가 완성되었습니다. 이제부터 커스텀 트리거와 커스텀 액션을 정의해봅시다.


TimeChangedTrigger < TimeToAlarmBehavior.cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class TimeChangedTrigger : TriggerBase<TextBlock>
{
    public string SpecialTime
    {
        get { return (string)GetValue(SpecialTimeProperty); }
        set { SetValue(SpecialTimeProperty, value); }
    }
 
    public static readonly DependencyProperty SpecialTimeProperty =
        DependencyProperty.Register(
            "SpecialTime"
            typeof(string), 
            typeof(TimeChangedTrigger), 
            new PropertyMetadata(string.Empty));
 
    protected override void OnAttached()
    {
        base.OnAttached();
 
        AssociatedObject.Loaded += AssociatedObject_Loaded;
    }
 
    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= AssociatedObject_Loaded;
 
        base.OnDetaching();
    }
 
    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        var textBlock = AssociatedObject as TextBlock;
        if (textBlock == nullreturn;
 
        var dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
        if (dpd == nullreturn;
        dpd.AddValueChanged(textBlock, OnTextChanged);
    }
 
    private void OnTextChanged(object sender, EventArgs e)
    {
        var textBlock = sender as TextBlock;
        if (textBlock == nullreturn;
 
        if (textBlock.Text == SpecialTime)
        {
            InvokeActions("ALARM");
        }
        else
        {
            InvokeActions("");
        }
    }
}
 
cs


위 코드는 TimeToAlarmBehavior.cs 비헤이비어 전체 코드입니다. 부분으로 나누어 살펴보겠습니다.


1
2
3
4
5
6
public class TimeChangedTrigger : TriggerBase<TextBlock>
{
 
// 생략
 
}
cs


TimeChangedTrigger 커스텀 트리거는 TriggerBase를 상속 받아서 정의합니다. 그리고 TextBlock 요소에만 적용하도록 TextBlock 사용할 요소로 지정했습니다


1
2
3
4
5
6
7
8
9
10
11
12
public string SpecialTime
{
    get { return (string)GetValue(SpecialTimeProperty); }
    set { SetValue(SpecialTimeProperty, value); }
}
 
public static readonly DependencyProperty SpecialTimeProperty =
    DependencyProperty.Register(
        "SpecialTime"
        typeof(string), 
        typeof(TimeChangedTrigger), 
        new PropertyMetadata(string.Empty));
cs


지정 시간을 받기 위해 SpecialTime 의존 속성을 추가합니다. 의존 속성에 대해서는 이전 포스트 참고합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
protected override void OnAttached()
{
    base.OnAttached();
    
    AssociatedObject.Loaded += AssociatedObject_Loaded;
}
 
protected override void OnDetaching()
{
    AssociatedObject.Loaded -= AssociatedObject_Loaded;
 
    base.OnDetaching();
}
cs

 

OnAttached와 OnDetaching 메소드를 재정의합니다. Behavior에서 메소드를 재정의하는 일은 필수라 있습니다. AssociatedObject를 통해 XAML 코드의 UI 요소를 가져옵니다. OnAttached 메소드에서는 가져온 요소에 Loaded 이벤트를 연결하고 OnDetaching 메소드에서는 Loaded 이벤트를 연결 해제합니다.

 

1
2
3
4
5
6
7
8
9
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    var textBlock = AssociatedObject as TextBlock;
    if (textBlock == nullreturn;
 
    var dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
    if (dpd == nullreturn;
    dpd.AddValueChanged(textBlock, OnTextChanged);
}
cs

 

AssociatedObject_Loaded 이벤트 핸들러에서는 TextBlock OnTextChanged 이벤트를 추가합니다. 기본 TextBlock TextChanged 이벤트가 없습니다. 하지만 간단한 방법을 찾아 적용했습니다. 참고한 링크는 아래입니다.

 

참고 : stackoverflow > How to detect a change in the Text property of a TextBlock?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void OnTextChanged(object sender, EventArgs e)
{
    var textBlock = sender as TextBlock;
    if (textBlock == nullreturn;
 
    if (textBlock.Text == SpecialTime)
    {    
        InvokeActions("ALARM");            
    }
    else
    {
        InvokeActions("");
    }
}
cs

 

OnTextChanged 이벤트 핸들러에서는 InvokeActions을 호출할 조건을 정의해 주었습니다. 사용자가 지정한 시간과 동일한 시간이 되었을 커스텀 액션을 호출하도록 하는 간단한 조건문입니다. InvokeActions 메소드는 커스텀 액션을 호출하기 위해 반드시 필요한 주요 메소드입니다. 파라미터를 이용해 "ALARM"이라는 문자열을 전달하도록 했습니다.

 

ChangeForegroundAction < TimeToAlarmBehavior.cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ChangeForegroundAction : TriggerAction<TextBlock>
{
    public Brush Foreground
    {
        get { return (Brush)GetValue(ForegroundProperty); }
        set { SetValue(ForegroundProperty, value); }
    }
 
    public static readonly DependencyProperty ForegroundProperty =
        DependencyProperty.Register(
            "Foreground"
            typeof(Brush), 
            typeof(ChangeForegroundAction), 
            new PropertyMetadata(Brushes.Black));
 
    protected override void Invoke(object parameter)
    {
        var textBlock = AssociatedObject as TextBlock;
        if (textBlock == null || parameter == nullreturn;
 
        var param = parameter.ToString().ToUpper();
        if (param == "ALARM")
        {
            textBlock.Foreground = Foreground;
        }
        else 
        {
            textBlock.Foreground = Brushes.Black;
        }
    }
}
cs

 

ChangeForegroundAction 커스텀 액션은 TriggerAction을 상속 받습니다. 그리고 TextBlock 요소에 적용 가능하게 하기 위해 TextBlock 사용할 요소로 지정했습니다. 부분으로 나누어 살펴보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
public Brush Foreground
{
    get { return (Brush)GetValue(ForegroundProperty); }
    set { SetValue(ForegroundProperty, value); }
}
 
public static readonly DependencyProperty ForegroundProperty =
    DependencyProperty.Register(
        "Foreground"
        typeof(Brush), 
        typeof(ChangeForegroundAction), 
        new PropertyMetadata(Brushes.Black));
cs

 

알람에 사용될 색상을 받기 위해 Foreground 의존 속성을 추가합니다. 의존 속성에 대해서는 이전 포스트 참고합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected override void Invoke(object parameter)
{
    var textBlock = AssociatedObject as TextBlock;
    if (textBlock == null || parameter == nullreturn;
 
    var param = parameter.ToString().ToUpper();
    if (param == "ALARM")
    {
        textBlock.Foreground = Foreground;
    }
    else 
    {
        textBlock.Foreground = Brushes.Black;
    }
}
cs

 

Invoke 메소드 재정의에서는 파라미터 값을 확인해 AssociatedObject를 통해 들어온 TextBlock Foreground 속성을 사용자가 지정한 색상으로 변경합니다.

 

 

적용하기 이전 TimeToAlarmBehavior 비헤이비어 코드가 막 준비된 빌드를 했다면 Blend for VS Assets(자산) 윈도우에서 보이게 됩니다. 이전 비헤이비어 포스트에서 설명했습니다만 드래그  드롭 동작을 이용해 디자인 뷰에서 패널에 적용하는 것이 훨씬 간편합니다. 네임스페이스 정의도 함께 이루어지므로 매우 편리합니다.


SDK에서 제공하는 ControlStoryboardAction 기본 비헤이비어도 함께 적용합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 초 -->
<TextBlock x:Name="txtSecond" Text="0" Grid.Column="2" RenderTransformOrigin="0.5,1">
    <TextBlock.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </TextBlock.RenderTransform>
    <i:Interaction.Triggers>
        <bhv:TimeChangedTrigger SpecialTime="7">
            <ei:ControlStoryboardAction Storyboard="{StaticResource StoryboardSecond}"/>
            <bhv:ChangeForegroundAction Foreground="Blue"/>
        </bhv:TimeChangedTrigger>
    </i:Interaction.Triggers>
</TextBlock>
cs

 

TimeChangedTrigger 커스텀 트리거에는 SpecialTime 속성을 원하는 시간대로 지정합니다.

 

 

ControlStoryboardAction에서는 시간이 변경될 때 실행할 StoryboardSecond 스토리보드를 지정합니다.

 

 

추가한 ChangeForegroundAction 커스텀 액션에서는 SpecialTime과 일치했을 때 변경될 Foreground 색상을 지정합니다.

 

 

실행해서 올바르게 동작하는지 확인해 봅니다.

 

몇가지 기능에 대해 추가로 설명합니다. 액션은 하나의 트리거에 여러 개를 동시에 사용할 있습니다. 여기서는 ControlStoryboardAction과 ChangeForegroundAction 동시에 사용했습니다. 그리고 ChangeForegroundAction 기본으로 제공되는 ChangePropertyAction 이용해서도 동일한 효과를 있습니다.

 

참고 : MSDN > Creating custom triggers and actions

 

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

 

관련 목차

 

023. 사용자 추가 비헤이비어(Custom Behavior)

024. 사용자 추가 비헤이비어(Custom Trigger & Action) #1

025사용자 추가 비헤이비어(Custom Trigger & Action) #2 

024. 커스텀 트리거 & 액션(Custom Trigger & Action) #1

XAML 뽀개기

이전 포스트에서는 OnlyOneExpanderBehavior 비헤이비어 단독으로 사용되었지만 이전 포스트에서 살펴본 CallMethodAction 비헤이비어인 경우 Trigger(트리거) Action(액션)으로 구성되어 있던 것을 있었습니다. 이번 포스트에서는 Custom Trigger Custom Action 대해 알아 봅시다.

 

 

이번에 사용될 예제는 간단한 알람 시계입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<Grid>
    <Grid.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="FontSize" Value="100"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>
    </Grid.Resources>
 
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
 
    <!-- 시 -->
    <TextBlock x:Name="txtHour" Text="0"/>
 
    <!-- 분 -->
    <TextBlock x:Name="txtMinute" Text="0" Grid.Column="1"/>
 
    <!-- 초 -->
    <TextBlock x:Name="txtSecond" Text="0" Grid.Column="2"/>
 
    <!-- Separation points -->
    <TextBlock Text=":" HorizontalAlignment="Right" Margin="0,0,-10,0"/>
    <TextBlock Text=":" HorizontalAlignment="Right" Margin="0,0,-10,0" Grid.Column="1"/>
</Grid>
cs

 

기본 XAML 코드 : MainWindow.xaml

 

예제를 위한 XAML 코드입니다. , , 초를 TextBlock으로 나누어 표현합니다. CS 코드에서 접근하기 윈해 x:Name 각각 선언했습니다. 다른 특이점은 없습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
        timer.Tick += Timer_Tick;
        timer.Start();
    }
 
    private void Timer_Tick(object sender, EventArgs e)
    {
        if (DateTime.Now.Hour > 12)
        {
         txtHour.Text = (DateTime.Now.Hour - 12).ToString();
        }
        else
        {
            txtHour.Text = DateTime.Now.Hour.ToString();
        }
 
        txtMinute.Text = DateTime.Now.Minute.ToString();            
        txtSecond.Text = DateTime.Now.Second.ToString();
    }
}
cs

 

기본 CS 코드 : MainWindow.xaml.cs

 

예제를 위한 CS 코드입니다. XAML 코드에 선언된 각 TextBlock 현재 시간을 1 단위로 업데이트합니다. 12단위 시간을 표현하기 위한 코드를 제외하고 다른 특이점은 없습니다.

 

, , 모두 동일한 방법이 사용될 것이므로 이제부터는 단위만 분리해서 살펴보겠습니다. 예제 시나리오는 시간이 변경될 지정한 Storyboard 실행하고, 알람이 울릴 시간(트리거) 설정할 있으며 해당 시간이 되었을 색상을 변경(액션)하는 시나리오입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Storyboard x:Key="StoryboardSecond">
    <DoubleAnimationUsingKeyFrames 
        Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" 
        Storyboard.TargetName="txtSecond">
        <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1.1">
            <EasingDoubleKeyFrame.EasingFunction>
                <SineEase EasingMode="EaseIn"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="1">
            <EasingDoubleKeyFrame.EasingFunction>
                <SineEase EasingMode="EaseOut"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>
cs

 

1
2
3
4
5
6
7
8
9
10
11
<!-- 초 -->
<TextBlock x:Name="txtSecond" Text="0" Grid.Column="2" RenderTransformOrigin="0.5,1">
    <TextBlock.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </TextBlock.RenderTransform>
</TextBlock>
cs

 

StoryboardHour, StoryboardMinute, StoryboardSecond 스토리보드 추가

 

스토리보드는 리소스에 정의해야 합니다시간이 가고 있다는 느낌을 주기 위해 ScaleY축에 약간의 움직임을 반복하도록 했습니다 모두 각각 분리해서 작성합니다.


Blend for VS 이용하면 쉽게 작성할 있습니다. RenderTransform 속성을 이용한 스토리보드를 작성하기 위해서는 각 TextBlock에 RenderTransformOrigin과 RenderTransform 그룹이 미리 정의되어 있어야 하는 것 주의가 필요 합니다. Blend for VS 이용해 스토리보드를 작성한다면 자동으로 추가되므로 걱정할 필요가 없습니다

 

다음 포스트에서는 Custom Trigger Custom Action 추가해봅시다.

 

참고 : MSDN > Creating custom triggers and actions

 

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

 

관련 목차

 

023. 사용자 추가 비헤이비어(Custom Behavior)

024. 사용자 추가 비헤이비어(Custom Trigger & Action) #1

025사용자 추가 비헤이비어(Custom Trigger & Action) #2 


020. 비헤이비어(Behavior) #1

XAML 뽀개기

Behavior 행동, 반응이란 뜻을 가지고 있습니다. 특정 상황, 조건(Trigger) 등에 따라 UI 요소에 어떤 영향을 줄  주로 사용됩니다. 사용자가 커스텀 비헤이비어를 만들어 기능을 확장할 수도 있습니다.


일단 비헤이비어를 사용하려면 System.Windows.Interactivity 어셈블리(dll) 참조가 필요합니다. 기본으로 제공되는 비헤이비어는 자주 사용되므로 Microsoft.Expression.Interactions 어셈블리도 함께 참조합니다.

 

 

Visual Studio 설치시 설정할 있는 옵션에서 SDK 제외되었다면 Blend for VS Assets(자산) 윈도우에서 기본으로 제공되는 비헤이비어가 보이지 않을 있습니다.

 

 

비헤이비어가 보이지 않는다면 Visual Studio Installer 이용해 SDK 설치합니다.

 

 

C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.5\Libraries\System.Windows.Interactivity.dll

 

이미 설치되어 있으면 경로를 참고해 수동으로 참조시켜도 됩니다.

 

 

참조 및 빌드를 후에는 기본으로 제공되는 비헤이비어를 Blend for VS에서 손쉽게 사용할 있습니다. 일반적인 사용 방법은 디자인뷰에 사용하길 원하는 UI 컨트롤 요소에 마우스로 drag & drop(드래그 드롭)하는 것입니다. 관련 어셈블리도 자동으로 참조됩니다.

 

1
2
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
cs

       

 방법을 이용하면 XAML 코드에 네임스페이스 등록도 자동으로 이루어 집니다. 여기서 접두사는 본인 취향에 맞게 수정해도 됩니다.

 

 

1
2
3
4
5
6
7
8
9
<Button Content="↑" FontWeight="Bold" FontSize="18"
        HorizontalAlignment="Right" VerticalAlignment="Bottom"
        Margin="5,5,22,5">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <ei:CallMethodAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
cs

 

비헤이비어는 드래그 앤 드롭하는 순간 UI 요소 하위에 자동으로 생성됩니다. 자동으로 생성된 XAML 코드를 살펴보면 Trigger(트리거) Action(액션)으로 구성되어 있습니다.

 

 

비헤이비어를 선택한 상태에서는 속성 윈도우에서 비헤이비어의 다양한 속성을 정의할 있습니다. 정의하지 않은 트리거의 Source 속성들은 트리 구조내 부모 요소 암시적으로 따릅니다.

 

 

트리거 타입은 새로 만들기 버튼을 눌러 변경할 있습니다.


다음 포스트에서는 나름 의미있는 예제를 들어 살펴보겠습니다.


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


참고 : MSDN > Working with built-in behaviors


관련 목차

 

020. 비헤이비어(Behavior) #1

021. 비헤이비어(CallMethodAction) #2

022. 비헤이비어(CallMethodAction) #3


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