Saturday, March 21, 2009

Working with WPF DataGrid – Part 1

The WPF DataGrid released by the WPF team provides a way of displaying tabular data to the user, providing sorting and editing functionality. While playing around with the Grid, I thought to document it and come with this series that can be helpful for others who are planning to use the control in their applications.

You can download the latest version from the link.

I’ll try to create a simple application that allows me do the CRUD operations on the Employee data provided.

For using the DataGrid you need to add the namespace "http://schemas.microsoft.com/wpf/2008/toolkit" to the Window tag.

Well to start with, I have my Employee class created as

public class Employee

{

public int Id { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public string Occupation { get; set; }

public bool IsContracter { get; set; }

public DateTime DateOfJoining { get; set; }

public override bool Equals(object item)

{

Employee employee = item as Employee;

if (employee != null)

return employee.Id == this.Id;

return false;

}

public override int GetHashCode()

{

return this.Id;

}

}

To provide the data that the Grid needs to display, I’ve used a ObjectDataProvider. My data provider has a GetEmployees method that returns an Observable collection of employees

public class EmployeeDataProvider

{

public BaseTranslator<Employee, UIEmployee> Translator { get; private set; }

public EmployeeDataProvider()

{

Translator = new EmployeeTranslator();

}

public ObservableEmployee GetEmployees()

{

ObservableEmployee employeeCollection = new ObservableEmployee(EmployeeFactory.GetEmployees());

return employeeCollection;

}

}

As you can see from the code, I have an ObservableEmployee class which contains an ObservableCollection of UIEmployee which implements the INotifyPropertyChanged and a Translator implementation to Translate between Employee and UIEmployee.

The UIEmployee class is implemented as

public class UIEmployee : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

private int m_Id;

public int Id

{

get { return m_Id; }

set

{

m_Id = value;

OnPropertyChanged("Id");

}

}

private string m_FirstName;

public string FirstName

{

get { return m_FirstName; }

set

{

m_FirstName = value;

OnPropertyChanged("FirstName");

}

}

private string m_LastName;

public string LastName

{

get { return m_LastName; }

set

{

m_LastName = value;

OnPropertyChanged("LastName");

}

}

private string m_Occupation;

public string Occupation

{

get { return m_Occupation; }

set

{

m_Occupation = value;

OnPropertyChanged("Occupation");

}

}

private DateTime m_DateOfJoining;

public DateTime DateOfJoining

{

get { return m_DateOfJoining; }

set

{

m_DateOfJoining = value;

OnPropertyChanged("DateOfJoining");

}

}

private bool m_IsContracter;

public bool IsContracter

{

get { return m_IsContracter; }

set

{

m_IsContracter = value;

OnPropertyChanged("IsContracter");

}

}

private void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

public delegate void ItemEndEditEventHandler(IEditableObject sender);

public event ItemEndEditEventHandler ItemEndEdit;

}

The ObservableEmployee class

public class ObservableEmployee : ObservableCollection<UIEmployee>

{

private BaseTranslator<Employee, UIEmployee> m_Translator = new EmployeeTranslator();

public ObservableEmployee() { }

public ObservableEmployee(IEnumerable&lt;UIEmployee> employees) : base(employees) { }

public ObservableEmployee(IEnumerable<Employee> employees)

{

foreach (Employee employee in employees)

base.Add(m_Translator.BusinessToView(employee));

}

}

The translator implementations

public abstract class BaseTranslator<BType, VType> where BType : class where VType : class

{

public abstract BType ViewToBusiness(VType viewType);

public abstract VType BusinessToView(BType businessType);

}

public class EmployeeTranslator : BaseTranslator<Employee, UIEmployee>

{

public override Employee ViewToBusiness(UIEmployee viewType)

{

Employee businessType = new Employee();

businessType.Id = viewType.Id;

businessType.FirstName = viewType.FirstName;

businessType.LastName = viewType.LastName;

businessType.Occupation = viewType.Occupation;

businessType.DateOfJoining = viewType.DateOfJoining;

businessType.IsContracter = viewType.IsContracter;

return businessType;

}

public override UIEmployee BusinessToView(Employee businessType)

{

UIEmployee viewType = new UIEmployee();

viewType.Id = businessType.Id;

viewType.FirstName = businessType.FirstName;

viewType.LastName = businessType.LastName;

viewType.Occupation = businessType.Occupation;

viewType.DateOfJoining = businessType.DateOfJoining;

viewType.IsContracter = businessType.IsContracter;

return viewType;

}

}

Now we can create the XAML code for the sample. My XAML looks like

<Window x:Class="DataGridSamples.EmployeeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EmployeeView" Height="300" Width="501"
xmlns:DLL="clr-namespace:DataGridSamples.DLL"
xmlns:WPF="http://schemas.microsoft.com/wpf/2008/toolkit" >
<Window.Resources>
<ObjectDataProvider x:Key="_employeeProvider" ObjectType="{x:Type DLL:EmployeeDataProvider}" />
<ObjectDataProvider x:Key="_employeeFactory" ObjectInstance="{StaticResource _employeeProvider}" MethodName="GetEmployees" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource _employeeFactory}}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="WPF DataGrid Samples" FontFamily="Calibri" FontSize="20" FontWeight="Bold" />
<WPF:DataGrid Name="_employeeDataGrid" DockPanel.Dock="Top" Grid.Row="1" Margin="12 0 12 0" VerticalAlignment="Top" ItemsSource="{Binding}" AutoGenerateColumns="False" >
<WPF:DataGrid.Columns>
<WPF:DataGridTextColumn Binding="{Binding Path=FirstName}" Header="FirstName" Width="100" />
<WPF:DataGridTextColumn Binding="{Binding Path=LastName}" Header="LastName" Width="100" />
</WPF:DataGrid.Columns>
</WPF:DataGrid>
</Grid>
</Window>


The output generated from the sample is a very simple datagrid with the firstname and lastname displayed.

In the next part of the series we’ll see how to include template columns and show data in drop down list, checkboxes and datepicker control.

2 comments:

John Hamm said...

Where is the source for the EmployeeFactory class?

Prajeesh said...

Hi john,
The employee factory creates a collection of employees and returns to the view. It's just passing a hard coded list of employees
public class EmployeeFactory
{
private static List&gtEmployee&lt EmployeeCollection = default(List&gtEmployee&lt);
private static int m_Id;

public EmployeeFactory()
{
EmployeeCollection = CreateCollection();
m_Id = 0;
}

public static bool Remove(Employee employee)
{
return EmployeeCollection.Remove(employee);
}

public static List&gtEmployee&lt GetEmployees()
{
if (EmployeeCollection == null)
EmployeeCollection = CreateCollection();
return EmployeeCollection;
}

private static List&gtEmployee&lt CreateCollection()
{
return new List&gtEmployee&lt
{
new Employee { Id = 1, FirstName = "Prajeesh", LastName = "Prathap", Occupation = "Engineer", DateOfJoining=new DateTime(2005, 8, 1), IsContracter = false, AddressInfo = AddressFactory.GetAddress(1, 3), Project = ProjectFactory.Create(1, "CCH") },
new Employee { Id = 2, FirstName = "Rahul", LastName = "Bose", Occupation = "Student", DateOfJoining=new DateTime(2009, 5, 4), IsContracter = true, AddressInfo = AddressFactory.GetAddress(1), Project = ProjectFactory.Create(1, "CCH") },
new Employee { Id = 3, FirstName = "Steve", LastName = "Roberts", Occupation = "Manager", DateOfJoining=new DateTime(1994, 8, 23), IsContracter = false, AddressInfo = AddressFactory.GetAddress(4), Project = ProjectFactory.Create(1, "CCH") },
new Employee { Id = 4, FirstName = "Micheal", LastName = "Clarke", Occupation = "Engineer", DateOfJoining=new DateTime(2003, 1, 14), IsContracter = true, AddressInfo = AddressFactory.GetAddress(3, 4), Project = ProjectFactory.Create(1, "CCH") },
new Employee { Id = 5, FirstName = "Rachel", LastName = "Green", Occupation = "Professional", DateOfJoining=new DateTime(2006, 3, 8), IsContracter = true, AddressInfo = AddressFactory.GetAddress(2, 3), Project = ProjectFactory.Create(1, "CCH") }
};
}

public static void Update(Employee employee)
{
Employee employeeToUpdate = EmployeeCollection.Find((x) => x.Id == employee.Id);
if (employeeToUpdate != null)
{
employeeToUpdate.FirstName = employee.FirstName;
employeeToUpdate.LastName = employee.LastName;
employeeToUpdate.Occupation = employee.Occupation;
employeeToUpdate.DateOfJoining = employee.DateOfJoining;
employeeToUpdate.IsContracter = employee.IsContracter;
}
else
{
m_Id += 1;
employee.Id = m_Id;
EmployeeCollection.Add(employee);
}
}

public static string Details()
{
StringBuilder details = new StringBuilder();
foreach (Employee employee in EmployeeCollection)
details.Append(employee.ToString() + "\n");
return details.ToString();
}
}