티스토리 뷰

반응형

2022.09.06 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내v1.0 part9-2 StyleSelector

2022.08.31 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part9-1 DataTemplateSelector

2022.08.08 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-3 Template

2022.08.02 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-2 Template

2022.07.21 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-1 Template

2022.07.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part7 Behavior

2022.07.05 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part6 Command

2022.06.27 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part5 Converter

2022.06.15 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-2 Data Binding

2022.06.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-1 Data Binding

2022.06.07 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-3

2022.06.02 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-2

2022.05.30 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-1

2022.05.11 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part2

2022.05.09 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part1

1. DataTemplateSelector Overview

데이터 객체 및 데이터 바인딩된 요소를 기반으로 DataTemplate를 선택하는 방법을 제공하는 컨트롤입니다.

더 자세한 사항은 여기를 참고합니다.

 

예제를 이용해서 빠르게 살펴보도록 하겠습니다.

MainWindow.xaml

리스트 박스 한개와 DataTemplate 한개를 추가합니다.

<DataTemplate x:Key="MyDataTemplate">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.RowSpan="2" Content="{StaticResource My}" />
        <TextBlock Grid.Column="1" Text="{Binding Message}" />
        <TextBlock
            Grid.Row="1"
            Grid.Column="1"
            Text="{Binding CreateAt, StringFormat={}{0:t}}" />
    </Grid>
</DataTemplate>

<ListBox
    x:Name="NormalMessageList"
    ItemTemplate="{StaticResource MyDataTemplate}"
    ItemsSource="{Binding Messages}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

MessageModel.cs

셈플 모델입니다.

public class MessageModel
{
    public int Id { get; set; }
    public string MessageType { get; set; }
    public string Message { get; set; }
    public DateTime CreateAt { get; set; } = DateTime.Now;
}

MainViewModel.cs

MessageModel을 이용해서 셈플 데이터를 생성합니다.

생성된 데이터를 Messages 프로퍼티에 입력합니다.

public class MainViewModel : ObservableObject
{
    private IList<MessageModel> _messages = new ObservableCollection<MessageModel>();
    /// <summary>
    /// 메시지 목록
    /// </summary>
    public IList<MessageModel> Messages
    {
        get { return _messages; }
        set { SetProperty(ref _messages, value); }
    }

    public MainViewModel()
    {
        Messages = new ObservableCollection<MessageModel>
        {
            new MessageModel{ Id = 1, MessageType = "System", Message = "Start message" },
            new MessageModel{ Id = 2, MessageType = "Me", Message = "Hello", CreateAt = DateTime.Now.AddMinutes(1) },
            new MessageModel{ Id = 3, MessageType = "Bot", Message = "How can I help you?", CreateAt = DateTime.Now.AddMinutes(1) },
            new MessageModel{ Id = 4, MessageType = "Me", Message = "Sing a song", CreateAt = DateTime.Now.AddMinutes(1) },
            new MessageModel{ Id = 5, MessageType = "System", Message = "Please wait for a moment" , CreateAt = DateTime.Now.AddMinutes(60)},
            new MessageModel{ Id = 6, MessageType = "Bot", Message = "I can't sing" , CreateAt = DateTime.Now.AddMinutes(60)},
            new MessageModel{ Id = 7, MessageType = "Me", Message = "Quit" , CreateAt = DateTime.Now.AddMinutes(1)},
            new MessageModel{ Id = 8, MessageType = "System", Message = "End message" , CreateAt = DateTime.Now.AddMinutes(1)},
        };
    }
}

기본 실행 화면

기본적으로 ListBox컨트롤은 1개의 DataTemplate만을 사용할 수 있습니다.

그런데, DataTemplateSelector은 Data 유형 혹은 Data의 내용을 이용해서 여러개의 DataTemplate을 사용할 수 있도록 지원합니다.

여기에서는 MessageType 프로퍼티의 값을 이용해서 3개의 DataTemplate를 어떻게 사용하는지 알아 보도록 하겠습니다.

2. MessageDataTemplateSelector 생성

  • DataTemplateSelector를 상속 받은 클래스를 생성합니다.
  • SelectTemplate 메소드를 override합니다.
  • 내부에 로직을 구현합니다.
  • 반환할 DataTemplate들은 미리 추가해 놓습니다.
/// <summary>
/// Message DataTemplateSelector - 반드시 DataTemplateSelector를 상속 받습니다.
/// </summary>
public class MessageDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate MyDataTemaplte { get; set; }
    public DataTemplate BotDataTemplate { get; set; }
    public DataTemplate SystemDataTemplate { get; set; }
    /// <summary>
    /// 템플릿 선택용 로직 실행 메소드 - 반드시 override합니다.
    /// </summary>
    /// <param name="item"></param>
    /// <param name="container"></param>
    /// <returns></returns>
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //item을 MessageModel인지 확인합니다.
        if (item is MessageModel message)
        {
            //MessageType에 따라서 DataTemplate를 각각 반환합니다.
            switch(message.MessageType)
            {
                case "Me":
                    return MyDataTemaplte;
                case "Bot":
                    return BotDataTemplate;
                case "System":
                    return SystemDataTemplate;
            }
        }
        return base.SelectTemplate(item, container);
    }
}

3. MessageDataTemplateSelector 인스턴스 생성

  • MyDataTemplate은 이미 만들어져있는 템플릿을 사용했습니다.
  • BotDataTemplate와 SystemDataTemplate는 아래와 같이 구현했습니다.
<local:MessageDataTemplateSelector x:Key="MessageDataTemplateSelector" MyDataTemaplte="{StaticResource MyDataTemplate}">
    <local:MessageDataTemplateSelector.BotDataTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="30" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <ContentControl Grid.RowSpan="2" Content="{StaticResource Bot}" />
                <Border
                    Grid.Column="1"
                    BorderBrush="Gray"
                    BorderThickness="1"
                    CornerRadius="5">
                    <TextBlock Margin="5" Text="{Binding Message}" />
                </Border>
                <TextBlock
                    Grid.Row="1"
                    Grid.Column="1"
                    HorizontalAlignment="Right"
                    Text="{Binding CreateAt, StringFormat={}{0:t}}" />
            </Grid>
        </DataTemplate>
    </local:MessageDataTemplateSelector.BotDataTemplate>
    <local:MessageDataTemplateSelector.SystemDataTemplate>
        <DataTemplate>
            <Border
                Background="LightGray"
                BorderBrush="LightGray"
                BorderThickness="1"
                CornerRadius="5">
                <Grid HorizontalAlignment="Center">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="20" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <ContentControl
                        Grid.RowSpan="2"
                        VerticalAlignment="Center"
                        Content="{StaticResource System}" />
                    <StackPanel
                        Grid.Column="1"
                        VerticalAlignment="Center"
                        Orientation="Horizontal">
                        <TextBlock Text="{Binding CreateAt, StringFormat={}{0:G}}" />
                        <TextBlock Margin="10,0,0,0" Text="{Binding Message}" />
                    </StackPanel>
                </Grid>
            </Border>
        </DataTemplate>
    </local:MessageDataTemplateSelector.SystemDataTemplate>
</local:MessageDataTemplateSelector>

4. MessageDataTemplateSelector  사용

  • ItemTemplateSelector를 사용할때는 ItemTemplate를 사용할 수 없습니다.
<ListBox
    x:Name="SelectorMessageList"
    Grid.Column="1"
    ItemTemplateSelector="{StaticResource MessageDataTemplateSelector}"
    ItemsSource="{Binding Messages}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

MessageDataTemplateSelector 사용 결과

5. 소스

항상 질문은 환영 입니다~

6. 추가 사항

DataTemplate 내부에서 이벤트가 발생했을 때 Command를 실행하는 부분을 소스에 추가했습니다.

우선 Behavior가 필요하기 때문에 Microsoft.Xaml.Behaviors.Wpf nuget package를 추가했고, 아래와 같이 수정했습니다.

xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
이렇게 사용하니 디자인 타임에  아래 오류가 발생하네요;;

System.Windows.Markup.XamlParseException: ''Cannot set unknown member '{http://schemas.microsoft.com/xaml/behaviors}Interaction.Triggers'.' Line number 'xx' and line position 'xx'.'

xmlns:b="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
이렇게 바꾸워서 사용하면 디자인 타임에 잘 보입니다.

TextBlock에 MouseLeftButtonDown 이벤트를 뷰모델의 ClickCommand와 연결 시켰습니다.

MainWindow.xaml

<TextBlock Grid.Column="1" Text="{Binding Message}">
    <b:Interaction.Triggers>
        <b:EventTrigger EventName="MouseLeftButtonDown">
            <b:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext.ClickCommand}" CommandParameter="My" />
        </b:EventTrigger>
    </b:Interaction.Triggers>
</TextBlock>

MainViewModel.cs

/// <summary>
/// 클릭 커맨드
/// </summary>
public ICommand ClickCommand { get; set; }

public MainViewModel()
{
    //커맨드 생성
    ClickCommand = new RelayCommand<string>(OnClick);
}

private void OnClick(string para)
{
    _ = MessageBox.Show($"Clicked message on {para}");
}

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

 

GitHub - kaki104/WpfTest

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

github.com

 

반응형
댓글