DataTemplateSelector을 유동적인 변경값에 따른 Template 변경에 대해 문의드립니다.

File의 다운로드 현황에 따라 Datagrid Template을 유동적으로 보여주는 기능을 구현 중입니다.
구체적으로

  • 다운로드가 시작되지 않으면 WaitingTemplate을,
  • 시작하면 ProgressTemplate을,
  • 완료되면 ComplateTemplate을 보여주려고 합니다.

하지만 문제가 발생한 부분이 File자체가 INotifyPropertyChanged 로 변경이 되면 이를 참고하는 값들이 변경이 일어남에도(실제로 ProgressTemplate 만을 사용할 경우에는 값이 변경됨에 따라 Progressbar가 변경됨을 확인 할 수 있었습니다.) 하기 영역이 변경되는 정보에 따라 ‘유동적으로’ DataTemplateSelector이 변경 되지 않은 것을 확인할 수 있습니다. 그냥 초기에 호출되는 WaitingTemplate만 계속 보입니다.

<temp:ProgressTemplateSelector CompleteTemplate="{StaticResource CompleteTemplate}"
                                                   ProgressTemplate="{StaticResource ProgressTemplate}"
                                                   WaitingTemplate="{StaticResource WaitingTemplate}" />

DataTemplateSelector의 변경을 Dynamic하게 변경하려면 어떤 부분을 수정해야할지 의견을 부탁드립니다.

namespace WPF_ParsingXML.View.Templates
{
    public class ProgressTemplateSelector : DataTemplateSelector
    {
        public DataTemplate WaitingTemplate
        { get; set; }

        public DataTemplate ProgressTemplate
        { get; set; }

        public DataTemplate CompleteTemplate
        { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            File file= item as File;

            if (file!= null)
            {
                if (file.StatusFilesize == 0)
                {
                    return WaitingTemplate;
                }
                if (file.StatusFilesize == 100)
                {
                    return CompleteTemplate;
                }
                return ProgressTemplate;
            }
            else
                return base.SelectTemplate(item, container);
        }
    }
}

<UserControl x:Class="WPF_ParsingXML.View.FileSubPages.UserControls.SelectedFileListup"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:WPF_ParsingXML.View.FileSubPages.UserControls"
             xmlns:temp="clr-namespace:WPF_ParsingXML.View.Templates"
             mc:Ignorable="d"
             d:DesignHeight="450"
             d:DesignWidth="500">
    <UserControl.Resources>
        <DataTemplate x:Key="WaitingTemplate">
            <TextBlock Margin="2"
                       Text="Waiting"
                       Foreground="Green" />
        </DataTemplate>
        <DataTemplate x:Key="ProgressTemplate">
            <Grid Width="200"
                  Margin="3">
                <ProgressBar VerticalAlignment="Center"
                             Height="20"
                             Minimum="0"
                             Maximum="100"
                             Value="{Binding StatusFilesize}" />
                <TextBlock Text="{Binding StatusFilesize,StringFormat={}{0:0}%}"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="CompleteTemplate">
            <TextBlock Margin="2"
                       Text="Completed"
                       Foreground="Green" />
        </DataTemplate>
    </UserControl.Resources>
    <DataGrid Name="fileList1"
              ItemsSource="{Binding Selectedfiles}"
              HorizontalAlignment="Center"
              VerticalAlignment="Center"
              AutoGenerateColumns="False"
              IsReadOnly="True"
              ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <DataGrid.Columns>
            <DataGridTextColumn Header="FileName"
                                Binding="{Binding FileName}"
                                Width="1*" />
            <DataGridTextColumn Header="Modified Date"
                                Binding="{Binding Modified}"
                                Width="1*" />
            <DataGridTextColumn Header="Origin Uri"
                                Binding="{Binding OriginUri}"
                                Width="1*" />
            <DataGridTextColumn Header="Size"
                                Binding="{Binding Size, StringFormat={}{0:n1} MB}"
                                Width="1*" />
            <DataGridTemplateColumn  Header="Status"
                                     Width="2*">
                <DataGridTemplateColumn.CellTemplateSelector>
                    <temp:ProgressTemplateSelector CompleteTemplate="{StaticResource CompleteTemplate}"
                                                   ProgressTemplate="{StaticResource ProgressTemplate}"
                                                   WaitingTemplate="{StaticResource WaitingTemplate}" />
                </DataGridTemplateColumn.CellTemplateSelector>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

namespace WPF_ParsingXML.Models
{
    public class File: INotifyPropertyChanged, ISelectable
    {      
        public string FileName { get; set; }
        public string Modified { get; set; } 
        public string OriginUri { get; set; }
        public string Size { get; set; } //Double
        private int statusFilesize;
        public int StatusFilesize
        {
            get { return statusFilesize; }
            set
            {
                statusFilesize = value;
                this.OnPropertyChanged(nameof(StatusFilesize));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

읽어주셔서 감사합니다.

좋아요 3

아직 주니어 개발자라 코드를 보다가 궁금한 점이 생겨 여쭤봅니다.

SelectTemplate 라는 메서드는 어디서 사용하나요??

그리고 Template를 변경할 때에는 원래 Propertychanged로 notify를 안 해줘도 괜찮은 건가요?
만약 지금도 해주고 있다면 어디 부분에서 하고 있나요??

질문 글에 질문 드리는 점 죄송합니다…

좋아요 4

제 경험에 의해서는 DataTemplateSelector는 생성될 때 조건에 따라 결정될건대 Dynamic하게 하실려면 스타일로 해보시는건 어떤지요.
뭐 예를들어 아래와같이

<DataGridTemplateColumn  Header="Status"
                                     Width="2*">
                <!--<DataGridTemplateColumn.CellTemplateSelector>
                    <temp:ProgressTemplateSelector CompleteTemplate="{StaticResource CompleteTemplate}"
                                                   ProgressTemplate="{StaticResource ProgressTemplate}"
                                                   WaitingTemplate="{StaticResource WaitingTemplate}" />
                </DataGridTemplateColumn.CellTemplateSelector>-->
                <DataGridTemplateColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <TextBlock Margin="2"
                                               Text="Waiting"
                                               Foreground="Green" />
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>

                        <Style.Triggers>
                            <DataTrigger Binding="{Binding File.Progress}" Value="True">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <Grid Width="200" Margin="3">
                                                <ProgressBar VerticalAlignment="Center"
                                                             Height="20"
                                                             Minimum="0"
                                                             Maximum="100"
                                                             Value="{Binding StatusFilesize}" />
                                                <TextBlock Text="{Binding StatusFilesize,StringFormat={}{0:0}%}"
                                                           VerticalAlignment="Center"
                                                           HorizontalAlignment="Center" />
                                            </Grid>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding File.Complete}" Value="True">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <TextBlock Margin="2"
                                                       Text="Completed"
                                                       Foreground="Green" />
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </DataGridTemplateColumn.CellStyle>
            </DataGridTemplateColumn>

실제 돌려보진 않아서 샘플로만 봐주세요.

좋아요 5

DataTemplateSelector 은 처음 바인딩 시점에 결정되고 그 이후엔 더 이상 호출 하지 않습니다.

해결 하시려면 2가지 방법이 있습니다.

방법 1.
그리드에 바인딩 된 Selectedfiles 데이터를 다시 설정해서 다시 랜더링 시켜주는 방법
이 방법은 데이터 양이 많을 경우 다소 비효율 적일 수 있습니다.

방법 2.
@Nobody 님이 제안 하신 트리거를 이용하는 방법 입니다.

<DataGridTemplateColumn  Header="Status"
                                     Width="2*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding }">
                                <ContentControl.Style>
                                    <Style TargetType="{x:Type ContentControl}">

                                        <!-- 기본 템플릿 -->
                                        <Setter Property="ContentTemplate" Value="{StaticResource WaitingTemplate}" />

                                        <!-- StatusFilesize속성에 따라 템플릿 변경 -->
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding StatusFilesize}" Value="1">
                                                <Setter Property="ContentTemplate" Value="{StaticResource ProgressTemplate}" />
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding StatusFilesize}" Value="100">
                                                <Setter Property="ContentTemplate" Value="{StaticResource CompleteTemplate}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </ContentControl.Style>
                            </ContentControl>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>

위 예시에서는 StatusFilesize값이 1 인 경우 ProgressTemplate 이 적용 되도록 예시를 해놨지만
컨버터를 이용해서 1이상 100 미만 인 조건으로 처리 하시면 될 것 같습니다.

좋아요 7

SelectTemplate 메서드는

System.Windows.Controls.DataTemplateSelector 에 포함된 메서드 입니다.

2개 이상의 템플릿을 미리 정의 해 놓고
SelectTemplate 메서드에서 조건에 따라 어떤 템플릿을 적용할지 오버라이드 해서 구현하여 사용할 수 있습니다.

그리고 Template를 변경할 때에는 원래 Propertychanged로 notify를 안 해줘도 괜찮은 건가요?

└ 데이터 바인딩 되어 있는 컨트롤이 화면에 노출 될때 내부적으로 SelectTemplate 메서드가 호출되어 템플릿이 적용 되어 집니다.

좋아요 8

말씀해주신대로 수정하니, 정상 동작 확인하였습니다. 감사합니다. @aroooong @Nobody

좋아요 5

감사합니다!

좋아요 3