Share via


Nasıl yapılır: DataGrid denetimindeki verileri gruplandırma, sıralama ve filtreleme

Verileri gruplandırarak, sıralayarak ve filtreleyerek verileri farklı şekillerde görüntülemek DataGrid genellikle yararlıdır. içindeki DataGridverileri gruplandırmak, sıralamak ve filtrelemek için, bu işlevleri destekleyen bir CollectionView veriye bağlarsınız. Ardından temel alınan kaynak verileri etkilemeden içindeki CollectionView verilerle çalışabilirsiniz. Koleksiyon görünümündeki değişiklikler kullanıcı arabirimine DataGrid (UI) yansıtılır.

sınıfı, CollectionView arabirimini uygulayan IEnumerable bir veri kaynağı için gruplandırma ve sıralama işlevi sağlar. sınıfı, CollectionViewSource XAML'den bir CollectionView öğesinin özelliklerini ayarlamanıza olanak tanır.

Bu örnekte, bir nesne koleksiyonu Task ile CollectionViewSourceilişkilidir. CollectionViewSource için olarak ItemsSourceDataGridkullanılır. gruplandırma, sıralama ve filtreleme üzerinde gerçekleştirilir CollectionViewSource ve kullanıcı arabiriminde DataGrid görüntülenir.

Grouped data in a DataGrid DataGrid'de gruplandırılmış veriler

CollectionViewSource'ı ItemsSource olarak kullanma

Bir denetimdeki DataGrid verileri gruplandırmak, sıralamak ve filtrelemek için, öğesini bu işlevleri destekleyen bir CollectionView öğesine bağlarsınızDataGrid. Bu örnekte, DataGrid bir nesne için CollectionViewSource bu işlevleri sağlayan öğesine List<T>Task bağlıdır.

DataGrid'i CollectionViewSource'a bağlamak için

  1. Arabirimini uygulayan IEnumerable bir veri koleksiyonu oluşturun.

    Koleksiyonunuzu oluşturmak için kullanırsanızList<T>, örneğini List<T>oluşturmak yerine öğesinden List<T> devralan yeni bir sınıf oluşturmanız gerekir. Bu, XAML'de koleksiyona veri bağlamanızı sağlar.

    Dekont

    Koleksiyondaki nesnelerin, özellik değişikliklerine INotifyPropertyChanged ve IEditableObject düzenlemelerine doğru yanıt verebilmesi için DataGrid değiştirilen arabirimi ve arabirimi uygulaması gerekir. Daha fazla bilgi için bkz . Özellik Değişikliği BildirimiNi Uygulama.

    // 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'de koleksiyon sınıfının bir örneğini oluşturun ve x:Key Yönergesini ayarlayın.

  3. XAML'de sınıfının bir örneğini CollectionViewSource oluşturun, x:Key Yönergesini ayarlayın ve koleksiyon sınıfınızın örneğini olarak Sourceayarlayın.

    <Window.Resources>
        <local:Tasks x:Key="tasks" />
        <CollectionViewSource x:Key="cvsTasks" Source="{StaticResource tasks}" 
                              Filter="CollectionViewSource_Filter">
        </CollectionViewSource>    
    </Window.Resources>
    
  4. sınıfının bir örneğini DataGrid oluşturun ve özelliğini olarak CollectionViewSourceayarlayınItemsSource.

    <DataGrid x:Name="dataGrid1" 
              ItemsSource="{Binding Source={StaticResource cvsTasks}}"
              CanUserAddRows="False">
    
  5. kodunuzdan öğesine erişmek CollectionViewSource için yöntemini kullanarak GetDefaultView öğesine bir başvuru CollectionViewSourcealın.

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

DataGrid'de öğeleri gruplandırma

Öğelerin içinde DataGridnasıl gruplandırılacağını belirtmek için, kaynak görünümdeki PropertyGroupDescription öğeleri gruplandırmak için türünü kullanırsınız.

DataGrid'deki öğeleri XAML kullanarak gruplandırmak için

  1. Gruplandırma ölçütü özelliğini belirten bir PropertyGroupDescription oluşturun. Özelliğini XAML'de veya kodda belirtebilirsiniz.

    1. XAML'de özelliğinin adına göre gruplandıracak şekilde ayarlayın PropertyName .

    2. Kodda, özelliğin adını gruplandırmak için oluşturucuya geçirin.

  2. PropertyGroupDescription öğesini koleksiyona CollectionViewSource.GroupDescriptions ekleyin.

  3. Daha fazla gruplandırma düzeyi eklemek için koleksiyonuna GroupDescriptions ek örnekleri PropertyGroupDescription ekleyin.

    <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. Bir grubu kaldırmak için öğesini koleksiyondan GroupDescriptions kaldırınPropertyGroupDescription.

  5. Tüm grupları kaldırmak için koleksiyonun Clear yöntemini çağırın 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
    

öğeleri içinde DataGridgruplandırıldığında, her grubun görünümünü belirten bir GroupStyle tanımlayabilirsiniz. Bunu DataGrid koleksiyonuna GroupStyle ekleyerek uygularsınızGroupStyle. Birden çok gruplandırma düzeyiniz varsa, her grup düzeyine farklı stiller uygulayabilirsiniz. Stiller tanımlanma sırasına göre uygulanır. Örneğin, iki stil tanımlarsanız, ilki en üst düzey satır gruplarına uygulanır. İkinci stil, ikinci düzeyde ve daha düşük tüm satır gruplarına uygulanır. DataContext öğesinin değeriGroupStyle, grubun temsil ettiği değeridirCollectionViewGroup.

Satır grubu üst bilgilerinin görünümünü değiştirmek için

  1. Satır grubunun görünümünü tanımlayan bir GroupStyle oluşturun.

  2. Etiketlerin GroupStyle<DataGrid.GroupStyle> içine yerleştirin.

    <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'de öğeleri sıralama

Öğelerin içinde DataGridnasıl sıralanacağını belirtmek için, kaynak görünümdeki SortDescription öğeleri sıralamak için türünü kullanırsınız.

DataGrid'deki öğeleri sıralamak için

  1. Sıralama ölçütü özelliğini belirten bir SortDescription oluşturun. Özelliğini XAML'de veya kodda belirtebilirsiniz.

    1. XAML'de, sıralama ölçütü olarak özelliğinin adına ayarlayın PropertyName .

    2. Kodda, ölçütü ve ListSortDirection sıralamak için özelliğinin adını oluşturucuya geçirin.

  2. SortDescription öğesini koleksiyona CollectionViewSource.SortDescriptions ekleyin.

  3. Ek özelliklere göre sıralamak için koleksiyonuna SortDescriptions ek örnekleri SortDescription ekleyin.

    <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'de öğeleri filtreleme

kullanarak içindeki DataGridCollectionViewSourceöğeleri filtrelemek için CollectionViewSource.Filter , olay işleyicisinde filtreleme mantığını sağlarsınız.

DataGrid'deki öğeleri filtrelemek için

  1. Olay için CollectionViewSource.Filter bir işleyici ekleyin.

  2. Olay işleyicisinde Filter filtreleme mantığını tanımlayın.

    Görünüm her yenilendiğinde filtre uygulanır.

    <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
    

Alternatif olarak, filtreleme mantığını sağlayan bir yöntem oluşturarak ve filtreyi uygulamak için özelliğini ayarlayarak CollectionView.Filter içindeki DataGrid öğeleri filtreleyebilirsiniz. Bu yöntemin bir örneğini görmek için bkz . Görünümdeki Verileri Filtreleme.

Örnek

Aşağıdaki örnekte, içindeki CollectionViewSource verileri gruplandırma, sıralama ve filtreleme Task ile gruplandırılmış, sıralanmış ve filtrelenmiş Task verileri bir içinde görüntüleme gösterilmektedirDataGrid. CollectionViewSource için olarak ItemsSourceDataGridkullanılır. gruplandırma, sıralama ve filtreleme üzerinde gerçekleştirilir CollectionViewSource ve kullanıcı arabiriminde DataGrid görüntülenir.

Bu örneği test etmek için DGGroupSortFilterExample adını proje adınızla eşleşecek şekilde ayarlamanız gerekir. Visual Basic kullanıyorsanız, için Window sınıf adını aşağıdaki şekilde değiştirmeniz gerekir.

<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

Ayrıca bkz.