Tuesday, March 24, 2009

Working with WPF DataGrid - Part 2

WPF DataGrid supports customized data display via Stock columns and template columns. The current version of DataGrid is shipped with support for 4 stock columns.

· DataGridTextColumn

· DataGridCheckBoxColumn

· DataGridComboBoxColumn

· DataGridHyperlinkColumn

Each column type corresponds to the UIElement that it will show in each of the cells in that column. The DataGridTemplateColumn allows you to customize the template with a custom UIElement or tree of Elements for each of the cells in the column. Template columns derive from DataGridColumn, whereas stock columns derive from DataGridBoundColumn which derives from DataGridColumn. In this part of the series I will show how to customize our sample to support stock columns and template columns. For adding additional columns to the Grid, we need to change the Employee class and add some extra properties for the employee.

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; }

- - -

}

Similarly the EmployeeFactory class method Create will change to incorporate the new fields.

private static List<Employee> CreateCollection()

{

return new List<Employee>

{

new Employee { Id = 1, FirstName = "Prajeesh", LastName = "Prathap", Occupation = "Engineer", DateOfJoining=new DateTime(2005, 8, 1), IsContracter = false },

new Employee { Id = 2, FirstName = "Rahul", LastName = "Bose", Occupation = "Student", DateOfJoining=new DateTime(2009, 5, 4), IsContracter = true },

new Employee { Id = 3, FirstName = "Steve", LastName = "Roberts", Occupation = "Manager", DateOfJoining=new DateTime(1994, 8, 23), IsContracter = false },

new Employee { Id = 4, FirstName = "Micheal", LastName = "Clarke", Occupation = "Engineer", DateOfJoining=new DateTime(2003, 1, 14), IsContracter = true },

new Employee { Id = 5, FirstName = "Rachel", LastName = "Green", Occupation = "Professional", DateOfJoining=new DateTime(2006, 3, 8), IsContracter = true }

};

}

In the XAML for including the Stock columns and template columns I need to change the DataGrid.Columns settings.

<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:DataGridTemplateColumn Header="Occupation">
<WPF:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Path=Occupation, Mode=TwoWay}" ItemsSource="{Binding Source={StaticResource _occupationFactory}}" IsEditable="True" IsReadOnly="True" />
</DataTemplate>
</WPF:DataGridTemplateColumn.CellEditingTemplate>
<WPF:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Occupation}" />
</DataTemplate>
</WPF:DataGridTemplateColumn.CellTemplate>
</WPF:DataGridTemplateColumn>
<WPF:DataGridCheckBoxColumn IsThreeState="False" Binding="{Binding Path=IsContracter, Mode=TwoWay}" Header="Is Contractor" />
<WPF:DataGridTemplateColumn Header="Date Of Joining">
<WPF:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<WPF:DatePicker SelectedDate="{Binding Path=DateOfJoining, Mode=TwoWay}" BorderThickness="0" />
</DataTemplate>
</WPF:DataGridTemplateColumn.CellEditingTemplate>
<WPF:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DateOfJoining, Converter={StaticResource _dateTimeConverter}}" />
</DataTemplate>
</WPF:DataGridTemplateColumn.CellTemplate>
</WPF:DataGridTemplateColumn>
</WPF:DataGrid.Columns>
</WPF:DataGrid>

As you can see from the code, I have added three extra columns each having a combobox, checkbox and datepicker columns respectively. For binding the ComboBox with a default collection, I added a new class OccupationFactory that returns the collection of Occupations that are available. Also we need to create a DateTimeConverter class to convert DateTime to string and viceversa.

public class OccupationFactory

{

public static List<String> CreateCollection()

{

return new List<string> { "Engineer", "Doctor", "Professional", "Student", "Manager", "Not working" };

}

}

public class DateTimeConverter : IValueConverter

{

#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

return ((DateTime)value).ToShortDateString();

}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

DateTime dateOfJoining;

if (DateTime.TryParse(value.ToString(), out dateOfJoining))

return dateOfJoining;

else

return string.Empty;

}

#endregion

}

Add these object references to the Window.Resources section of the XAML

<ObjectDataProvider x:Key="_occupationFactory" ObjectInstance="{StaticResource _employeeProvider}" MethodName="GetOccupations" />
<DLL:DateTimeConverter x:Key="_dateTimeConverter" />

Compile and run the solution and now you can see the DataGrid with the additional columns. The next part of the series we'll see how to perform CRUD operations on the DataGrid.

Output


4 comments:

Joe said...

Could you provide a complete working sample for Datagrid part 2?

Unknown said...

Code is messing, for example there is no _employeeProvider or GetOccupations that are refereced by the ObjectDataProvider...

A complete listing would be nice

RV said...

As the XAML says, in EmployeeDataProvider needs the GetOccupations.

public ObservableCollection> GetOccupations()
{
var occupations = new ObservableCollection> { OccupationFactory.GetOccupations() };
return occupations;
}


And GetOccupations can be:

public static List GetOccupations()
{
return OccupationCollection ?? (OccupationCollection = CreateCollection());
}


But the combo doesn't get the list: http://bit.ly/quNPWM.

Tod said...

Terrific, thanks. One thing I had to do though was add the DataConverter to both the CellTemplate AND the CellEditingTemplate.