How to: Group, Sort, and Filter Data in the DataGrid Control

Microsoft Silverlight will reach end of support after October 2021. Learn more.

It is often useful to view data in a DataGrid in different ways by grouping, sorting, and filtering the data. To group, sort, and filter the data in a DataGrid, you bind it to a collection view that supports these functions. You can then manipulate the data in the collection view without affecting the underlying data. The changes in the collection view are reflected in the DataGrid user interface (UI).

The PagedCollectionView class provides grouping, sorting, and paging functionality for a data source that implements the IEnumerable interface. In this example, a collection of Task objects is wrapped in a PagedCollectionView. The PagedCollectionView is used as the ItemsSource for the DataGrid. Grouping, sorting, and filtering are performed on the PagedCollectionView and are displayed in the DataGrid UI.

The PagedCollectionView also provides paging functionality that can be used with a DataGrid. To see an example of paging, see the DataPager control.

Run this sample

Using a PagedCollectionView as an ItemsSource

To group, sort, and filter data in a DataGrid control, you bind the DataGrid to a collection view that supports these functions. In this example, the DataGrid is bound to a PagedCollectionView that provides these functions for a List<T> of Task objects.

To bind a DataGrid to a PagedCollectionView

  1. Create a data collection that implements the IEnumerable interface.

    NoteNote:

    The objects in the collection must implement the INotifyPropertyChanged changed interface and the IEditableObject interface in order for the DataGrid to respond correctly to property changes and edits.

  2. Create a PagedCollectionView and pass the data collection to the constructor.

  3. Set the DataGrid.ItemsSource property to the PagedCollectionView.

    ' Create a collection to store task data.
    Dim taskList As ObservableCollection(Of Task) = New ObservableCollection(Of Task)
    
    ' Generate some task data and add it to the task list.
    For index = 1 To 14
        taskList.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), _
                       .Notes = "Task " & index.ToString() & " is due on " & Date.Now.AddDays(index) & ". Lorum ipsum..." _
                     })
    Next
    
    Dim taskListView As New PagedCollectionView(taskList)
    Me.dataGrid1.ItemsSource = taskListView
    
    // Create a collection to store task data.
    ObservableCollection<Task> taskList = new ObservableCollection<Task>();
    // Generate some task data and add it to the task list.
    for (int i = 1; i <= 14; i++)
    {
        taskList.Add(new Task()
        {
            ProjectName = "Project " + ((i % 3) + 1).ToString(),
            TaskName = "Task " + i.ToString(),
            DueDate = DateTime.Now.AddDays(i),
            Complete = (i % 2 == 0),
            Notes = "Task " + i.ToString() + " is due on "
                  + DateTime.Now.AddDays(i) + ". Lorum ipsum..."
        });
    }
    
    PagedCollectionView taskListView = new PagedCollectionView(taskList);
    this.dataGrid1.ItemsSource = taskListView;
    

Grouping items in a DataGrid

To specify how items are grouped in a DataGrid, you use the PropertyGroupDescription type to group the items in the source view.

To group items in a DataGrid

  1. Create a PropertyGroupDescription and pass the name of the property to group by to the constructor.

  2. Add the PropertyGroupDescription to the PagedCollectionView.GroupDescriptions collection.

  3. Add additional instances of PropertyGroupDescription to the PagedCollectionView.GroupDescriptions collection to add more levels of grouping.

    If taskListView.CanGroup = True Then
        ' Group tasks by ProjectName...
        taskListView.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
        ' Then group by Complete status.
        taskListView.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
    End If
    
    if (taskListView.CanGroup == true)
    {
        // Group tasks by ProjectName...
        taskListView.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
        // Then group by Complete status.
        taskListView.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
    }
    

When items are grouped in the DataGrid, each group has a header. You can change the appearance of the DataGridRowGroupHeader by defining a custom Style and adding it to the RowGroupHeaderStyles collection. If you have multiple levels of grouping, you can apply different styles to each group level. Styles are applied in the order in which they are defined. For example, if you define two styles, the first will be applied to top level row groups. The second style will be applied to all row groups at the second level and lower. The DataContext of the DataGridRowGroupHeader is the CollectionViewGroup that the header represents.

To change the appearance of row group headers

  1. Create a Style with the TargetType of DataGridRowGroupHeader.

  2. Put the Style inside the <DataGrid.RowGroupHeaderStyles> tags.

    <sdk:DataGrid.RowGroupHeaderStyles>
        <!-- Style for groups at top level -->
        <Style TargetType="sdk:DataGridRowGroupHeader">
            <Setter Property="PropertyNameVisibility" Value="Collapsed" />
            <Setter Property="Background" Value="#FF112255" />
            <Setter Property="Foreground" Value="#FFEEEEEE" />
            <Setter Property="SublevelIndent" Value="15" />
        </Style>
        <!-- Style for groups under the top level -->
        <Style TargetType="sdk:DataGridRowGroupHeader">
            <Setter Property="Background" Value="#44225566" />
        </Style>
    </sdk:DataGrid.RowGroupHeaderStyles>
    

When items are grouped in the DataGrid, you can use the following methods to manually collapse and expand the groups:

  • Click the collapse or expand arrow in the row group header.

  • Double-click anywhere in the row group header.

  • Press the left arrow key to collapse the group and the right arrow key to expand the group when the row group header has focus.

To programmatically collapse or expand a group, pass the CollectionViewGroup to the CollapseRowGroup or ExpandRowGroup method.

To collapse or expand row groups

  1. Get the CollectionViewGroup that represents the group to collapse or expand.

    NoteNote:

    You can get the CollectionViewGroup from the Groups collection. Alternatively, you can use the GetGroupFromItem method to get an individual CollectionViewGroup as shown here.

    Dim cvg = dataGrid1.GetGroupFromItem(dataGrid1.SelectedItem, 0)
    
    CollectionViewGroup cvg = dataGrid1.GetGroupFromItem(dataGrid1.SelectedItem, 0);
    
  2. To collapse the group, pass the CollectionViewGroup to the CollapseRowGroup method. Set the second argument to true to also collapse all sub groups.

  3. To expand the group, pass the CollectionViewGroup to the ExpandRowGroup method. Set the second argument to true to also expand all sub groups.

    The following example shows how to collapse or expand all groups in the PagedCollectionView.Groups collection.

    Private Sub CollapseButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        Try
            For Each group As CollectionViewGroup In pcv.Groups
                dataGrid1.ScrollIntoView(group, Nothing)
                dataGrid1.CollapseRowGroup(group, True)
            Next
        Catch ex As Exception
            ' Could not collapse group.
            MessageBox.Show(ex.Message)
        End Try
    End Sub
    
    private void CollapseButton_Click(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
        try
        {
            foreach (CollectionViewGroup group in pcv.Groups)
            {
                dataGrid1.ScrollIntoView(group, null);
                dataGrid1.CollapseRowGroup(group, true);
            }
        }
        catch (Exception ex)
        {
            // Could not collapse group.
            MessageBox.Show(ex.Message);
        }
    }
    
    Private Sub ExpandButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        Try
            For Each group As CollectionViewGroup In pcv.Groups
                dataGrid1.ExpandRowGroup(group, True)
            Next
        Catch ex As Exception
            ' Could not expand group.
            MessageBox.Show(ex.Message)
        End Try
    End Sub
    
    private void ExpandButton_Click(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
        try
        {
            foreach (CollectionViewGroup group in pcv.Groups)
            {
                dataGrid1.ExpandRowGroup(group, true);
            }
        }
        catch (Exception ex)
        {
            // Could not expand group.
            MessageBox.Show(ex.Message);
        }
    }
    

Sorting items in a DataGrid

To specify how items are sorted in a DataGrid, you use the SortDescription type to sort the items in the source view.

To sort items in a DataGrid

  1. Create a SortDescription and pass the name of the property to sort by to the constructor.

  2. Add the SortDescription to the PagedCollectionView.SortDescriptions collection.

  3. Add additional instances of SortDescription to the PagedCollectionView.SortDescriptions collection to sort by additional properties.

    If taskListView.CanSort = True Then
        '// By default, sort by ProjectName.
        taskListView.SortDescriptions.Add(New SortDescription("ProjectName", ListSortDirection.Ascending))
    End If
    
    if (taskListView.CanSort == true)
    {
        // By default, sort by ProjectName.
        taskListView.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
    }
    

Filtering items in a DataGrid

To filter items in a DataGrid, you create a method that provides the filtering logic and then you use the PagedCollectionView.Filter property to apply the filter.

To filter items in a DataGrid

  1. Create a method that provides the filtering logic. The method is used as a callback and accepts a parameter of type Object.

  2. Apply the filter to the data by setting the PagedCollectionView.Filter property.

  3. Remove the filter by setting the PagedCollectionView.Filter property to null.

    The following example applies the filter when the CheckBox is Checked, and removes it when the CheckBox is Unchecked.

    Private Sub CheckBox_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        If pcv IsNot Nothing & pcv.CanFilter = True Then
            ' Apply the filter.
            pcv.Filter = New Predicate(Of Object)(AddressOf FilterCompletedTasks)
        End If
    End Sub
    
    Private Sub CheckBox_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        If pcv IsNot Nothing Then
            ' Remove the filter.
            pcv.Filter = Nothing
        End If
    End Sub
    
    Public Function FilterCompletedTasks(ByVal t As Object) As Boolean
        Dim _task As New Task
        _task = t
        Return _task.Complete = False
    End Function
    
    private void CheckBox_Checked(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
        if (pcv != null && pcv.CanFilter == true)
        {
            // Apply the filter.
            pcv.Filter = new Predicate<object>(FilterCompletedTasks);
        }
    }
    
    private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
    {
        PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
        if (pcv != null)
        {
            // Remove the filter.
            pcv.Filter = null;
        }
    }
    
    public bool FilterCompletedTasks(object t)
    {
        Task task = t as Task;
        return (task.Complete == false);
    }
    

Example

The following example demonstrates grouping, sorting, and filtering data in a PagedCollectionView and displaying the grouped, sorted, and filtered data in a DataGrid. A collection of Task objects is wrapped in a PagedCollectionView. The PagedCollectionView is used as the ItemsSource for the DataGrid. Grouping, sorting, and filtering are performed on the PagedCollectionView and are displayed in the DataGrid UI.

Run this sample

<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<UserControl x:Class="DataGridGrouping.MainPage"
    xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    Width="600" Height="500">
    <Grid x:Name="LayoutRoot" Background="White" Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <sdk:DataGrid x:Name="dataGrid1">
            <sdk:DataGrid.RowGroupHeaderStyles>
                <!-- Style for groups at top level -->
                <Style TargetType="sdk:DataGridRowGroupHeader">
                    <Setter Property="PropertyNameVisibility" Value="Collapsed" />
                    <Setter Property="Background" Value="#FF112255" />
                    <Setter Property="Foreground" Value="#FFEEEEEE" />
                    <Setter Property="SublevelIndent" Value="15" />
                </Style>
                <!-- Style for groups under the top level -->
                <Style TargetType="sdk:DataGridRowGroupHeader">
                    <Setter Property="Background" Value="#44225566" />
                </Style>
            </sdk:DataGrid.RowGroupHeaderStyles>
        </sdk:DataGrid>
        <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center">
            <TextBlock Text="Filter Completed Tasks " />
            <CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
            <Button Content="Expand All Groups" Margin="10,0,0,0" Click="ExpandButton_Click" />
            <Button Content="Collapse All Groups" Margin="5,0,0,0" Click="CollapseButton_Click" />
        </StackPanel>
    </Grid>
</UserControl>
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Windows.Data


Partial Public Class MainPage
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
        ' Create a collection to store task data.
        Dim taskList As ObservableCollection(Of Task) = New ObservableCollection(Of Task)

        ' Generate some task data and add it to the task list.
        For index = 1 To 14
            taskList.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), _
                           .Notes = "Task " & index.ToString() & " is due on " & Date.Now.AddDays(index) & ". Lorum ipsum..." _
                         })
        Next

        Dim taskListView As New PagedCollectionView(taskList)
        Me.dataGrid1.ItemsSource = taskListView
        If taskListView.CanGroup = True Then
            ' Group tasks by ProjectName...
            taskListView.GroupDescriptions.Add(New PropertyGroupDescription("ProjectName"))
            ' Then group by Complete status.
            taskListView.GroupDescriptions.Add(New PropertyGroupDescription("Complete"))
        End If
        If taskListView.CanSort = True Then
            '// By default, sort by ProjectName.
            taskListView.SortDescriptions.Add(New SortDescription("ProjectName", ListSortDirection.Ascending))
        End If
    End Sub
    Private Sub CheckBox_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        If pcv IsNot Nothing & pcv.CanFilter = True Then
            ' Apply the filter.
            pcv.Filter = New Predicate(Of Object)(AddressOf FilterCompletedTasks)
        End If
    End Sub

    Private Sub CheckBox_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        If pcv IsNot Nothing Then
            ' Remove the filter.
            pcv.Filter = Nothing
        End If
    End Sub

    Public Function FilterCompletedTasks(ByVal t As Object) As Boolean
        Dim _task As New Task
        _task = t
        Return _task.Complete = False
    End Function
    Private Sub ExpandButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        Try
            For Each group As CollectionViewGroup In pcv.Groups
                dataGrid1.ExpandRowGroup(group, True)
            Next
        Catch ex As Exception
            ' Could not expand group.
            MessageBox.Show(ex.Message)
        End Try
    End Sub
    Private Sub CollapseButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim pcv As PagedCollectionView = Me.dataGrid1.ItemsSource
        Try
            For Each group As CollectionViewGroup In pcv.Groups
                dataGrid1.ScrollIntoView(group, Nothing)
                dataGrid1.CollapseRowGroup(group, True)
            Next
        Catch ex As Exception
            ' Could not collapse group.
            MessageBox.Show(ex.Message)
        End Try
    End Sub
End Class

Public Class Task
    Implements System.ComponentModel.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
    Private m_Notes As String = String.Empty

    ' 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

    Public Property Notes() As String
        Get
            Return Me.m_Notes
        End Get
        Set(ByVal value As String)
            If Not value = Me.m_Notes Then
                Me.m_Notes = value
                NotifyPropertyChanged("Notes")
            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.Notes = Me.temp_Task.Notes
            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
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace DataGridGrouping
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            // Create a collection to store task data.
            ObservableCollection<Task> taskList = new ObservableCollection<Task>();
            // Generate some task data and add it to the task list.
            for (int i = 1; i <= 14; i++)
            {
                taskList.Add(new Task()
                {
                    ProjectName = "Project " + ((i % 3) + 1).ToString(),
                    TaskName = "Task " + i.ToString(),
                    DueDate = DateTime.Now.AddDays(i),
                    Complete = (i % 2 == 0),
                    Notes = "Task " + i.ToString() + " is due on "
                          + DateTime.Now.AddDays(i) + ". Lorum ipsum..."
                });
            }

            PagedCollectionView taskListView = new PagedCollectionView(taskList);
            this.dataGrid1.ItemsSource = taskListView;
            if (taskListView.CanGroup == true)
            {
                // Group tasks by ProjectName...
                taskListView.GroupDescriptions.Add(new PropertyGroupDescription("ProjectName"));
                // Then group by Complete status.
                taskListView.GroupDescriptions.Add(new PropertyGroupDescription("Complete"));
            }
            if (taskListView.CanSort == true)
            {
                // By default, sort by ProjectName.
                taskListView.SortDescriptions.Add(new SortDescription("ProjectName", ListSortDirection.Ascending));
            }

        }
        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
            if (pcv != null && pcv.CanFilter == true)
            {
                // Apply the filter.
                pcv.Filter = new Predicate<object>(FilterCompletedTasks);
            }
        }

        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = this.dataGrid1.ItemsSource as PagedCollectionView;
            if (pcv != null)
            {
                // Remove the filter.
                pcv.Filter = null;
            }
        }

        public bool FilterCompletedTasks(object t)
        {
            Task task = t as Task;
            return (task.Complete == false);
        }
        private void ExpandButton_Click(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
            try
            {
                foreach (CollectionViewGroup group in pcv.Groups)
                {
                    dataGrid1.ExpandRowGroup(group, true);
                }
            }
            catch (Exception ex)
            {
                // Could not expand group.
                MessageBox.Show(ex.Message);
            }
        }
        private void CollapseButton_Click(object sender, RoutedEventArgs e)
        {
            PagedCollectionView pcv = dataGrid1.ItemsSource as PagedCollectionView;
            try
            {
                foreach (CollectionViewGroup group in pcv.Groups)
                {
                    dataGrid1.ScrollIntoView(group, null);
                    dataGrid1.CollapseRowGroup(group, true);
                }
            }
            catch (Exception ex)
            {
                // Could not collapse group.
                MessageBox.Show(ex.Message);
            }
        }

    }

    public class Task : System.ComponentModel.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;
        private string m_Notes = string.Empty;

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

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

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

        [Display(Name = "Due Date")]
        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");
                }
            }
        }

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

        // 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;
                this.Notes = temp_Task.Notes;
                m_Editing = false;
            }
        }

        public void EndEdit()
        {
            if (m_Editing == true)
            {
                temp_Task = null;
                m_Editing = false;
            }
        }
    }
}

See Also

Reference