방법: DataGrid 컨트롤에서 데이터 그룹화, 정렬 및 필터링

DataGrid데이터를 그룹화, 정렬 및 필터링 하 여 다양 한 방법으로의 데이터를 보는 것이 유용한 경우가 많습니다. 에서 데이터를 그룹화, 정렬 및 필터링 하려면 DataGrid CollectionView 이러한 함수를 지 원하는에 바인딩합니다. 그런 다음 CollectionView 기본 원본 데이터에 영향을 주지 않고의 데이터를 사용할 수 있습니다. 컬렉션 뷰의 변경 내용은 DataGrid UI (사용자 인터페이스)에 반영 됩니다.

CollectionView클래스는 인터페이스를 구현 하는 데이터 소스에 대 한 그룹화 및 정렬 기능을 제공 합니다 IEnumerable . CollectionViewSource클래스를 사용 하면 XAML에서의 속성을 설정할 수 있습니다 CollectionView .

이 예제에서는 개체의 컬렉션을에 Task 바인딩합니다 CollectionViewSource . 는의 CollectionViewSource 로 사용 됩니다 ItemsSource DataGrid . 그룹화, 정렬 및 필터링은에서 수행 되며 CollectionViewSource UI에 표시 됩니다 DataGrid .

DataGrid의 그룹화 된 데이터 DataGrid의 그룹화 된 데이터

System.windows.controls.itemscontrol.itemssource로 CollectionViewSource 사용

컨트롤에서 데이터를 그룹화, 정렬 및 필터링 하려면 DataGrid DataGrid CollectionView 이러한 함수를 지 원하는에 바인딩합니다. 이 예제에서는 DataGrid CollectionViewSource 개체의에 대해 이러한 함수를 제공 하는에 List<T> 바인딩됩니다 Task .

CollectionViewSource에 DataGrid를 바인딩하려면

  1. 인터페이스를 구현 하는 데이터 컬렉션을 만듭니다 IEnumerable .

    List<T>를 사용 하 여 컬렉션을 만드는 경우의 인스턴스를 인스턴스화하는 대신에서 상속 하는 새 클래스를 만들어야 합니다 List<T> List<T> . 이렇게 하면 XAML에서 컬렉션에 데이터 바인딩할 수 있습니다.

    참고

    컬렉션의 개체는 INotifyPropertyChanged IEditableObjectDataGrid 속성 변경 및 편집에 올바르게 응답 하기 위해 변경 된 인터페이스와 인터페이스를 구현 해야 합니다. 자세한 내용은 속성 변경 알림 구현을 참조하세요.

    // Requires using System.Collections.ObjectModel;
    public class Tasks : ObservableCollection<Task>
    {
        // Creating the Tasks collection in this way enables data binding from XAML.
    }
    
    ' Requires Imports System.Collections.ObjectModel
    Public Class Tasks
        Inherits ObservableCollection(Of Task)
        ' Creating the Tasks collection in this way enables data binding from XAML.
    End Class
    
  2. XAML에서 컬렉션 클래스의 인스턴스를 만들고 X:Key 지시어를 설정 합니다.

  3. XAML에서 클래스의 인스턴스를 만들고, CollectionViewSource x:Key 지시문을 설정 하 고, 컬렉션 클래스의 인스턴스를로 설정 Source 합니다.

    <Window.Resources>
        <local:Tasks x:Key="tasks" />
        <CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}" 
                              Filter="CollectionViewSource_Filter">
        </CollectionViewSource>    
    </Window.Resources>
    
  4. 클래스의 인스턴스를 만들고 DataGrid 속성을로 설정 합니다 ItemsSource CollectionViewSource .

    <DataGrid x:Name="dataGrid1" 
              ItemsSource="{Binding Source={StaticResource cvsTasks}}"
              CanUserAddRows="False">
    
  5. 코드에서에 액세스 하려면 메서드를 사용 하 여에 대 한 CollectionViewSource GetDefaultView 참조를 가져옵니다 CollectionViewSource .

    ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
    
    Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
    

DataGrid에서 항목 그룹화

에서 항목을 그룹화 하는 방법을 지정 하려면 DataGrid PropertyGroupDescription 형식을 사용 하 여 원본 뷰의 항목을 그룹화 합니다.

XAML을 사용 하 여 DataGrid에서 항목을 그룹화 하려면

  1. PropertyGroupDescription그룹화 할 속성을 지정 하는을 만듭니다. XAML 또는 코드에서 속성을 지정할 수 있습니다.

    1. XAML에서을 PropertyName 그룹화 할 속성의 이름으로 설정 합니다.

    2. 코드에서 그룹화 할 속성의 이름을 생성자에 전달 합니다.

  2. PropertyGroupDescription 컬렉션에 추가 합니다 CollectionViewSource.GroupDescriptions .

  3. 컬렉션에의 추가 인스턴스 PropertyGroupDescription GroupDescriptions 를 추가 하 여 그룹화 수준을 더 추가 합니다.

    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="ProjectName"/>
        <PropertyGroupDescription PropertyName="Complete"/>
    </CollectionViewSource.GroupDescriptions>
    
    ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
    if (cvTasks != null && cvTasks.CanGroup == true)
    {
        cvTasks.GroupDescriptions.Clear();
        cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
        cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
    }
    
    Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
    If cvTasks IsNot Nothing And cvTasks.CanGroup = True Then
        cvTasks.GroupDescriptions.Clear()
        cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
        cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
    End If
    
  4. 그룹을 제거 하려면 PropertyGroupDescription 컬렉션에서을 제거 합니다 GroupDescriptions .

  5. 모든 그룹을 제거 하려면 Clear 컬렉션의 메서드를 호출 합니다 GroupDescriptions .

    ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
    if (cvTasks != null)
    {
        cvTasks.GroupDescriptions.Clear();
    }
    
    Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
    If cvTasks IsNot Nothing Then
        cvTasks.GroupDescriptions.Clear()
    End If
    

에서 항목이 그룹화 되 면 DataGrid GroupStyle 각 그룹의 모양을 지정 하는를 정의할 수 있습니다. 를 GroupStyle DataGrid의 컬렉션에 추가 하 여 적용 합니다 GroupStyle . 그룹화 수준이 여러 개인 경우 각 그룹 수준에 다른 스타일을 적용할 수 있습니다. 스타일은 정의 된 순서 대로 적용 됩니다. 예를 들어 두 스타일을 정의 하는 경우 첫 번째이 최상위 행 그룹에 적용 됩니다. 두 번째 스타일은 두 번째 수준 이하의 모든 행 그룹에 적용 됩니다. DataContext의는 GroupStyle CollectionViewGroup 그룹이 나타내는입니다.

행 그룹 머리글의 모양을 변경 하려면

  1. GroupStyle행 그룹의 모양을 정의 하는을 만듭니다.

  2. 태그 안에를 추가 합니다 GroupStyle <DataGrid.GroupStyle> .

    <DataGrid.GroupStyle>
        <!-- Style for groups at top level. -->
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Margin" Value="0,0,0,5"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
                                    <Expander.Header>
                                        <DockPanel>
                                            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
                                            <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
                                        </DockPanel>
                                    </Expander.Header>
                                    <Expander.Content>
                                        <ItemsPresenter />
                                    </Expander.Content>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
        <!-- Style for groups under the top level. -->
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <DockPanel Background="LightBlue">
                        <TextBlock Text="{Binding Path=Name, Converter={StaticResource completeConverter}}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
                        <TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
                    </DockPanel>
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </DataGrid.GroupStyle>
    

DataGrid에서 항목 정렬

에서 항목을 정렬 하는 방법을 지정 하려면 DataGrid 형식을 사용 하 여 SortDescription 소스 뷰의 항목을 정렬 합니다.

DataGrid에서 항목을 정렬 하려면

  1. SortDescription정렬할 속성을 지정 하는을 만듭니다. XAML 또는 코드에서 속성을 지정할 수 있습니다.

    1. XAML에서을 PropertyName 정렬할 속성의 이름으로 설정 합니다.

    2. 코드에서 정렬할 속성의 이름을 및 ListSortDirection 생성자에 전달 합니다.

  2. SortDescription 컬렉션에 추가 합니다 CollectionViewSource.SortDescriptions .

  3. 추가 속성을 기준으로 정렬 하려면의 인스턴스를 SortDescription 컬렉션에 추가 SortDescriptions 합니다.

    <CollectionViewSource.SortDescriptions>
        <!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
        <scm:SortDescription PropertyName="ProjectName"/>
        <scm:SortDescription PropertyName="Complete" />
        <scm:SortDescription PropertyName="DueDate" />
    </CollectionViewSource.SortDescriptions>
    
    // Requires using System.ComponentModel;
    ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
    if (cvTasks != null && cvTasks.CanSort == true)
    {
        cvTasks.SortDescriptions.Clear();
        cvTasks.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
        cvTasks.SortDescriptions.Add(new SortDescription("Complete", ListSortDirection.Ascending));
        cvTasks.SortDescriptions.Add(new SortDescription("DueDate", ListSortDirection.Ascending));
    }
    
    Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
    If cvTasks IsNot Nothing And cvTasks.CanSort = True Then
        cvTasks.SortDescriptions.Clear()
        cvTasks.SortDescriptions.Add(New SortDescription("ProjectName", ListSortDirection.Ascending))
        cvTasks.SortDescriptions.Add(New SortDescription("Complete", ListSortDirection.Ascending))
        cvTasks.SortDescriptions.Add(New SortDescription("DueDate", ListSortDirection.Ascending))
    End If
    

DataGrid에서 항목 필터링

을 사용 하 여에서 항목을 필터링 하려면 DataGrid CollectionViewSource 이벤트에 대 한 처리기에서 필터링 논리를 제공 합니다 CollectionViewSource.Filter .

DataGrid에서 항목을 필터링 하려면

  1. 이벤트에 대 한 처리기를 추가 CollectionViewSource.Filter 합니다.

  2. Filter이벤트 처리기에서 필터링 논리를 정의 합니다.

    뷰를 새로 고칠 때마다 필터가 적용 됩니다.

    <CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}" 
                          Filter="CollectionViewSource_Filter">
    
    private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
    {
        Task t = e.Item as Task;
        if (t != null)
        // If filter is turned on, filter completed items.
        {
            if (this.cbCompleteFilter.IsChecked == true && t.Complete == true)
                e.Accepted = false;
            else
                e.Accepted = true;
        }
    }
    
    Private Sub CollectionViewSource_Filter(ByVal sender As System.Object, ByVal e As System.Windows.Data.FilterEventArgs)
        Dim t As Task = e.Item
        If t IsNot Nothing Then
            ' If filter is turned on, filter completed items.
            If Me.cbCompleteFilter.IsChecked = True And t.Complete = True Then
                e.Accepted = False
            Else
                e.Accepted = True
            End If
        End If
    End Sub
    

또는 DataGrid 필터링 논리를 제공 하는 메서드를 만들고 CollectionView.Filter 필터를 적용 하도록 속성을 설정 하 여에서 항목을 필터링 할 수 있습니다. 이 메서드의 예를 보려면 뷰에서 데이터 필터링을 참조 하세요.

예제

다음 예에서는에서 데이터를 그룹화, 정렬 및 필터링 Task CollectionViewSource 하 고에서 그룹화, 정렬 및 필터링 된 데이터를 표시 하는 방법을 보여 줍니다 Task DataGrid . 는의 CollectionViewSource 로 사용 됩니다 ItemsSource DataGrid . 그룹화, 정렬 및 필터링은에서 수행 되며 CollectionViewSource UI에 표시 됩니다 DataGrid .

이 예를 테스트 하려면 프로젝트 이름과 일치 하도록 DGGroupSortFilterExample 이름을 조정 해야 합니다. Visual Basic를 사용 하는 경우 클래스 이름을 다음과 같이 변경 해야 Window 합니다.

<Window x:Class="MainWindow"

<Window x:Class="DGGroupSortFilterExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DGGroupSortFilterExample"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="Group, Sort, and Filter Example" Height="575" Width="525">
    <Window.Resources>
        <local:CompleteConverter x:Key="completeConverter" />
        <local:Tasks x:Key="tasks" />
        <CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}" 
                              Filter="CollectionViewSource_Filter">
            <CollectionViewSource.SortDescriptions>
                <!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
                <scm:SortDescription PropertyName="ProjectName"/>
                <scm:SortDescription PropertyName="Complete" />
                <scm:SortDescription PropertyName="DueDate" />
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="ProjectName"/>
                <PropertyGroupDescription PropertyName="Complete"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <DataGrid x:Name="dataGrid1" 
                  ItemsSource="{Binding Source={StaticResource cvsTasks}}"
                  CanUserAddRows="False">
            <DataGrid.GroupStyle>
                <!-- Style for groups at top level. -->
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Margin" Value="0,0,0,5"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
                                            <Expander.Header>
                                                <DockPanel>
                                                    <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
                                                    <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
                                                </DockPanel>
                                            </Expander.Header>
                                            <Expander.Content>
                                                <ItemsPresenter />
                                            </Expander.Content>
                                        </Expander>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
                <!-- Style for groups under the top level. -->
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <DockPanel Background="LightBlue">
                                <TextBlock Text="{Binding Path=Name, Converter={StaticResource completeConverter}}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
                                <TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
                            </DockPanel>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </DataGrid.GroupStyle>
            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Setter Property="Foreground" Value="Black" />
                    <Setter Property="Background" Value="White" />
                </Style>
            </DataGrid.RowStyle>
        </DataGrid>
        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <TextBlock Text=" Filter completed items " VerticalAlignment="Center" />
            <CheckBox x:Name="cbCompleteFilter" VerticalAlignment="Center"
                      Checked="CompleteFilter_Changed" Unchecked="CompleteFilter_Changed" />
            <Button Content="Remove Groups" Margin="10,2,2,2" Click="UngroupButton_Click" />
            <Button Content="Group by Project/Status" Margin="2" Click="GroupButton_Click" />
        </StackPanel>
    </Grid>
</Window>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;

namespace DGGroupSortFilterExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Get a reference to the tasks collection.
            Tasks _tasks = (Tasks)this.Resources["tasks"];

            // Generate some task data and add it to the task list.
            for (int i = 1; i <= 14; i++)
            {
                _tasks.Add(new Task()
                {
                    ProjectName = "Project " + ((i % 3) + 1).ToString(),
                    TaskName = "Task " + i.ToString(),
                    DueDate = DateTime.Now.AddDays(i),
                    Complete = (i % 2 == 0)
                });
            }
        }

        private void UngroupButton_Click(object sender, RoutedEventArgs e)
        {
            ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
            if (cvTasks != null)
            {
                cvTasks.GroupDescriptions.Clear();
            }
        }

        private void GroupButton_Click(object sender, RoutedEventArgs e)
        {
            ICollectionView cvTasks = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
            if (cvTasks != null && cvTasks.CanGroup == true)
            {
                cvTasks.GroupDescriptions.Clear();
                cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
                cvTasks.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
            }
        }

        private void CompleteFilter_Changed(object sender, RoutedEventArgs e)
        {
            // Refresh the view to apply filters.
            CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource).Refresh();
        }

        private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
        {
            Task t = e.Item as Task;
            if (t != null)
            // If filter is turned on, filter completed items.
            {
                if (this.cbCompleteFilter.IsChecked == true && t.Complete == true)
                    e.Accepted = false;
                else
                    e.Accepted = true;
            }
        }
    }

    [ValueConversion(typeof(Boolean), typeof(String))]
    public class CompleteConverter : IValueConverter
    {
        // This converter changes the value of a Tasks Complete status from true/false to a string value of
        // "Complete"/"Active" for use in the row group header.
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool complete = (bool)value;
            if (complete)
                return "Complete";
            else
                return "Active";
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string strComplete = (string)value;
            if (strComplete == "Complete")
                return true;
            else
                return false;
        }
    }

    // Task Class
    // Requires using System.ComponentModel;
    public class Task : INotifyPropertyChanged, IEditableObject
    {
        // The Task class implements INotifyPropertyChanged and IEditableObject
        // so that the datagrid can properly respond to changes to the
        // data collection and edits made in the DataGrid.

        // Private task data.
        private string m_ProjectName = string.Empty;
        private string m_TaskName = string.Empty;
        private DateTime m_DueDate = DateTime.Now;
        private bool m_Complete = false;

        // Data for undoing canceled edits.
        private Task temp_Task = null;
        private bool m_Editing = false;

        // Public properties.
        public string ProjectName
        {
            get { return this.m_ProjectName; }
            set
            {
                if (value != this.m_ProjectName)
                {
                    this.m_ProjectName = value;
                    NotifyPropertyChanged("ProjectName");
                }
            }
        }

        public string TaskName
        {
            get { return this.m_TaskName; }
            set
            {
                if (value != this.m_TaskName)
                {
                    this.m_TaskName = value;
                    NotifyPropertyChanged("TaskName");
                }
            }
        }

        public DateTime DueDate
        {
            get { return this.m_DueDate; }
            set
            {
                if (value != this.m_DueDate)
                {
                    this.m_DueDate = value;
                    NotifyPropertyChanged("DueDate");
                }
            }
        }

        public bool Complete
        {
            get { return this.m_Complete; }
            set
            {
                if (value != this.m_Complete)
                {
                    this.m_Complete = value;
                    NotifyPropertyChanged("Complete");
                }
            }
        }

        // Implement INotifyPropertyChanged interface.
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        // Implement IEditableObject interface.
        public void BeginEdit()
        {
            if (m_Editing == false)
            {
                temp_Task = this.MemberwiseClone() as Task;
                m_Editing = true;
            }
        }

        public void CancelEdit()
        {
            if (m_Editing == true)
            {
                this.ProjectName = temp_Task.ProjectName;
                this.TaskName = temp_Task.TaskName;
                this.DueDate = temp_Task.DueDate;
                this.Complete = temp_Task.Complete;
                m_Editing = false;
            }
        }

        public void EndEdit()
        {
            if (m_Editing == true)
            {
                temp_Task = null;
                m_Editing = false;
            }
        }
    }
    // Requires using System.Collections.ObjectModel;
    public class Tasks : ObservableCollection<Task>
    {
        // Creating the Tasks collection in this way enables data binding from XAML.
    }
}
Imports System.ComponentModel
Imports System.Collections.ObjectModel

Class MainWindow
    Public Sub New()
        InitializeComponent()

        ' Get a reference to the tasks collection.
        Dim _tasks As Tasks = Me.Resources("tasks")

        ' Generate some task data and add it to the task list.
        For index = 1 To 14
            _tasks.Add(New Task() With _
                         {.ProjectName = "Project " & ((index Mod 3) + 1).ToString(), _
                           .TaskName = "Task " & index.ToString(), _
                           .DueDate = Date.Now.AddDays(index), _
                           .Complete = (index Mod 2 = 0) _
                         })
        Next
    End Sub

    Private Sub UngroupButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
        If cvTasks IsNot Nothing Then
            cvTasks.GroupDescriptions.Clear()
        End If
    End Sub

    Private Sub GroupButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim cvTasks As ICollectionView = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource)
        If cvTasks IsNot Nothing And cvTasks.CanGroup = True Then
            cvTasks.GroupDescriptions.Clear()
            cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
            cvTasks.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
        End If
    End Sub

    Private Sub CompleteFilter_Changed(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Refresh the view to apply filters.
        CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource).Refresh()
    End Sub

    Private Sub CollectionViewSource_Filter(ByVal sender As System.Object, ByVal e As System.Windows.Data.FilterEventArgs)
        Dim t As Task = e.Item
        If t IsNot Nothing Then
            ' If filter is turned on, filter completed items.
            If Me.cbCompleteFilter.IsChecked = True And t.Complete = True Then
                e.Accepted = False
            Else
                e.Accepted = True
            End If
        End If
    End Sub
End Class

Public Class CompleteConverter
    Implements IValueConverter
    ' This converter changes the value of a Tasks Complete status from true/false to a string value of
    ' "Complete"/"Active" for use in the row group header.
    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        Dim complete As Boolean = value
        If complete = True Then
            Return "Complete"
        Else
            Return "Active"
        End If
    End Function

    Public Function ConvertBack1(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Dim strComplete As String = value
        If strComplete = "Complete" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

' Task class
' Requires Imports System.ComponentModel
Public Class Task
    Implements INotifyPropertyChanged, IEditableObject
    ' The Task class implements INotifyPropertyChanged and IEditableObject 
    ' so that the datagrid can properly respond to changes to the 
    ' data collection and edits made in the DataGrid.

    ' Private task data.
    Private m_ProjectName As String = String.Empty
    Private m_TaskName As String = String.Empty
    Private m_DueDate As DateTime = Date.Now
    Private m_Complete As Boolean = False

    ' Data for undoing canceled edits.
    Private temp_Task As Task = Nothing
    Private m_Editing As Boolean = False

    ' Public properties.
    Public Property ProjectName() As String
        Get
            Return Me.m_ProjectName
        End Get
        Set(ByVal value As String)
            If Not value = Me.m_ProjectName Then
                Me.m_ProjectName = value
                NotifyPropertyChanged("ProjectName")
            End If
        End Set
    End Property

    Public Property TaskName() As String
        Get
            Return Me.m_TaskName
        End Get
        Set(ByVal value As String)
            If Not value = Me.m_TaskName Then
                Me.m_TaskName = value
                NotifyPropertyChanged("TaskName")
            End If
        End Set
    End Property

    Public Property DueDate() As Date
        Get
            Return Me.m_DueDate
        End Get
        Set(ByVal value As Date)
            If Not value = Me.m_DueDate Then
                Me.m_DueDate = value
                NotifyPropertyChanged("DueDate")
            End If
        End Set
    End Property

    Public Property Complete() As Boolean
        Get
            Return Me.m_Complete
        End Get
        Set(ByVal value As Boolean)
            If Not value = Me.m_Complete Then
                Me.m_Complete = value
                NotifyPropertyChanged("Complete")
            End If
        End Set
    End Property

    ' Implement INotifyPropertyChanged interface. 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Sub NotifyPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    ' Implement IEditableObject interface.
    Public Sub BeginEdit() Implements IEditableObject.BeginEdit
        If Not Me.m_Editing Then
            Me.temp_Task = Me.MemberwiseClone()
            Me.m_Editing = True
        End If
    End Sub

    Public Sub CancelEdit() Implements IEditableObject.CancelEdit
        If m_Editing = True Then
            Me.ProjectName = Me.temp_Task.ProjectName
            Me.TaskName = Me.temp_Task.TaskName
            Me.DueDate = Me.temp_Task.DueDate
            Me.Complete = Me.temp_Task.Complete
            Me.m_Editing = False
        End If
    End Sub

    Public Sub EndEdit() Implements IEditableObject.EndEdit
        If m_Editing = True Then
            Me.temp_Task = Nothing
            Me.m_Editing = False
        End If
    End Sub
End Class

' Requires Imports System.Collections.ObjectModel
Public Class Tasks
    Inherits ObservableCollection(Of Task)
    ' Creating the Tasks collection in this way enables data binding from XAML.
End Class

참고 항목