티스토리 뷰

반응형

2022.12.21 - [WPF .NET] - Trigger를 사용해서 동적 UI 구성하기 Part2

2022.12.14 - [WPF .NET] - Trigger를 사용해서 동적 UI 구성하기 Part1 - Overview

이전 회차에서는 PropertyTrigger를 이용해서 동적UI를 구성하는 방법을 확인 했습니다. 컨트롤의 프로퍼티를 이용하기 때문에, Model이나 ViewModel의 데이터를 이용한 Trigger를 할 수 없어서 약간은 답답하셨을 것 같네요.

 

이번에는 Model이나 ViewModel의 프로퍼티를 이용해서 Trigger가 가능한 DataTrigger에 대해서 알아 보도록 하겠습니다.

1. Data Trigger

  • <DataTrigger/>로 사용할 수 있습니다.
  • Binding 기능을 이용하여 데이터와 연결할 수 있으며, 그 데이터가 변경되면 동작합니다.
  • 의존속성(DependencyProperty)과 일반 속성(Public Property)를 모두 사용할 수 있습니다.
  • 접근 가능 범위내 다른 컨트롤의 속성을 사용할 수 있습니다.
  • 여러개의 조건을 사용하기 위해서는 <MultiDataTrigger/>를 이용합니다.

2. 예제

ItemTemplate 소스만 가져왔습니다.

아래 내용을 보시면 Border가 한개있고, TextBlock이 있습니다.

여기서, Sex라는 프로퍼티값이 변경이되면, 두 컨트롤을 모두 수정해야하는 경우에는 Style.Triggers를 사용해서 각각의 컨트롤에 트리거를 추가하기 보다는, DataTemplate.Triggers에서 두개를 수정하도록하면, 코드 보기가 좋고, 간결 합니다.

ItemTemplate 내부이기 때문에, Person 모델의 프로퍼티와 바인딩을 하는 것이 일반적입니다.

<DataTemplate x:Key="PersonItemTemplate">
    <Border x:Name="Bd" Background="Gray">
        <TextBlock
            x:Name="Content"
            Margin="4"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Foreground="White"
            Text="{Binding Name}"
            TextAlignment="Center" />
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Sex}" Value="True">
            <Setter TargetName="Bd" Property="Background" Value="SkyBlue" />
            <Setter TargetName="Content" Property="Foreground" Value="DarkBlue" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Sex}" Value="False">
            <Setter TargetName="Bd" Property="Background" Value="Pink" />
            <Setter TargetName="Content" Property="Foreground" Value="Blue" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

실행

아이템을 선택하면 오른쪽에 상세 내용을 표시합니다.

True 버튼을 클릭하면, Sex 프로퍼티가 변경되면서, 다음 이미지처럼 표시됩니다.

값이 변경되면, 바로 Trigger가 반응해서, 색을 변경해 줍니다.

이 프로젝트에서 사용한 Person 모델의 Sex 프로퍼티는 아래와 같습니다.
public bool? Sex { get; set; }

Sex가 null인 경우, 기본 속성으로 표시됩니다.

3. Converter를 이용한 Trigger

Person에 Childs 속성을 추가하고, Childs에 값이 있으면, True아니면 False를 반환하는 컨버터를 추가해서 작업을 진행하겠습니다.

컨버터에 대한 자세한 설명은 아래 링크를 참고하세요
MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part5 Converter (tistory.com)

Person.cs

public IList<object> Childs { get; set; }

초기값으로 아래 처럼 생성 후 Id 1번에 Childs 속성에 입력합니다.

var childs = new List<object> 
{
    new Person { Id = 10, Name = "kaki104_C1", Sex = true, Description = "Item1" , X = 0, Y = 210, Age = 1},
    new Person { Id = 11, Name = "kaki104_C2", Sex = true, Description = "Item1" , X = 0, Y = 210, Age = 1},
};

* ListToBoolConverter.cs 추가

using System;
using System.Collections;
using System.Globalization;
using System.Windows.Data;

namespace TriggerSample3
{
    /// <summary>
    /// List 데이터 타입 존재 여부와 카운트 여부를 bool로 변환, null, 0이면 false, 0 크면 true
    /// </summary>
    public class ListToBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value is IList list && list.Count > 0 ? true : (object)false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

MainWindow.xaml

Window.Resources에 있는 템플릿을 수정합니다.

<Window.Resources>
    <local:ListToBoolConverter x:Key="ListToBoolConverter" />

    <DataTemplate x:Key="PersonItemTemplate">
        <Border x:Name="Bd" Background="Gray">
            <StackPanel Orientation="Horizontal">
                <TextBlock
                    x:Name="Content"
                    Margin="4"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Foreground="White"
                    Text="{Binding Name}"
                    TextAlignment="Center" />
                <CheckBox
                    x:Name="HasChilds"
                    Margin="5,0,0,0"
                    VerticalAlignment="Center"
                    IsChecked="{Binding Childs, Converter={StaticResource ListToBoolConverter}}" 
                    IsHitTestVisible="False" />
            </StackPanel>
        </Border>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding Sex}" Value="True">
                <Setter TargetName="Bd" Property="Background" Value="SkyBlue" />
                <Setter TargetName="Content" Property="Foreground" Value="DarkBlue" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Sex}" Value="False">
                <Setter TargetName="Bd" Property="Background" Value="Pink" />
                <Setter TargetName="Content" Property="Foreground" Value="Blue" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Childs, Converter={StaticResource ListToBoolConverter}}" Value="False">
                <Setter TargetName="HasChilds" Property="Background" Value="Gray" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</Window.Resources>

실행

오른쪽 상세 정보에도 Childs를 추가하고, 목록을 볼 수 있도록 수정했습니다. 자세한 코드는 아래 소스 코드를 참고하시기 바랍니다.

4. 소스

DataTrigger를 이용하면 바인딩을 이용해서 대부분의 데이터 연동 작업이 가능합니다. 그런데, 그 중에 RelativeSource를 이용해서 TemplateParent나 Self등을 이용해서 컨트롤의 속성과 연결해서 작업을 하려고 생각했다면, 그 방법 말고 Trigger를 이용해서 직접 접근이 가능한 방법으로 구현하시는 것을 권장 드립니다. 아무래도 Binding으로 접근하는 것이 직접 접근하는 방법 보다는 느릴 것이라 생각합니다.

WpfTest/TriggerSample3 at master · kaki104/WpfTest (github.com)

 

GitHub - kaki104/WpfTest

Contribute to kaki104/WpfTest development by creating an account on GitHub.

github.com

 

반응형
댓글