As this is another control which has not been included as a build-in control, so the best way is to create this kind of controls by ourselves, but it need a lot of labor time. By the way, there are many third-part Silverlight control manufactures have already create it, if you need good support, good built-in styles for your control and also you have the budget, then buy one and you don't need to read the later part of this blog.
This is a simple sample which talked about how to extend the build-in DataGrid to be a PivotTable for those people who has no spare money or a Silverlight-lover.
So let's start the topic and show the preview image.
Step 1, please open the Visual Studio, and create a Silverlight Application project, and also you need to install the Silverlight toolkit(http://silverlight.codeplex.com/)
Step 2, please create a Silverlight Class Library for the PivotDataGrid class, name it and change the namespace.
Step 3, let me post all the codes and please read the comments in the codes.
{
public class PivotDataGrid : DataGrid
{
// tempdata used to make a new pivoted collection and bind it
// to the datagrid.
private ObservableCollection<PivotDataGridItem> tempdata;
// columnHeaderValues used temporarily to store the all the data
// which used to be the new headers.
private List<object> columnHeaderValues;
// rowdata used temporarily to store the other data which used
// to be the left rows.
private Dictionary<object, List<object>> rowdata;
private IEnumerable _workSource;
// we hide the base ItemsSource because we need to make them two
// available at the same time.
public new IEnumerable ItemsSource
{
get
{
return _workSource;
}
set
{
_workSource = value;
// we pivot the collection here and
// create the new collection to bind.
ApplyWorkDataSource(value);
}
}
// we should know which property will be changed to column header.
public string HeaderProperty { get; set; }
public PivotDataGrid()
: base()
{
this.HeadersVisibility = DataGridHeadersVisibility.All;
this.LoadingRow += new EventHandler<DataGridRowEventArgs>(PivotGrid_LoadingRow);
}
private void ApplyWorkDataSource(IEnumerable source)
{
// generate the new source
this.convertItemsSourceForPivotGrid(source);
this.AutoGenerateColumns = false;
int i = 0;
// generate the new columns according to the new source
foreach (object obj in columnHeaderValues)
{
this.Columns.Add(new DataGridTextColumn()
{
Header = obj.ToString(),
Binding = new Binding("Cells[" + i.ToString() + "]")
});
i++;
}
// generate the new collection
tempdata = new ObservableCollection<PivotDataGridItem>();
foreach (object objkey in rowdata.Keys)
{
PivotDataGridItem item = new PivotDataGridItem();
foreach (object obj2 in rowdata[objkey])
{
item.Cells.Add(obj2);
}
item.RowHeader = objkey;
tempdata.Add(item);
}
// bind the source to the base datagrid
base.ItemsSource = tempdata;
}
private void convertItemsSourceForPivotGrid(IEnumerable original)
{
this.columnHeaderValues = new List<object>();
this.rowdata = new Dictionary<object, List<object>>();
// the principle is to use Reflection to get all the public
// properties which need to be show in the columns.
// we can extend this part to meet different requirements.
foreach (object obj in original)
{
if (obj == null)
continue;
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
if (pinfo != null)
{
if (pinfo.Name == this.HeaderProperty)
{
this.columnHeaderValues.Add(pinfo.GetValue(obj, null));
}
else
{
if (!rowdata.ContainsKey(pinfo.Name))
{
this.rowdata.Add(pinfo.Name, new List<object>());
}
this.rowdata[pinfo.Name].Add(pinfo.GetValue(obj, null));
}
}
}
}
}
private void PivotGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
// set the new row header.
if (tempdata != null && tempdata.Count >= e.Row.GetIndex() + 1)
e.Row.Header = tempdata[e.Row.GetIndex()].RowHeader;
}
}
}
namespace System.Windows.Controls.Extend
{
public class PivotDataGridItem
{
private ObservableCollection<object> _items = new ObservableCollection<object>();
public ObservableCollection<object> Cells { get { return _items; } }
public object RowHeader { get; set; }
}
}
Step 4, then we can test the control.
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
{
public MainPage()
{
InitializeComponent();
// generate a sample data
ObservableCollection<PivotGridSampleItem> source = new ObservableCollection<PivotGridSampleItem>();
DateTime nextMonday = DateTime.Now.AddDays((double)((8 - (int)DateTime.Now.DayOfWeek) % 7));
for (int i = 0; i < 10; i++)
{
source.Add(new PivotGridSampleItem()
{
Date = nextMonday.AddDays(28 * i),
RowA = "RowA_" + i,
RowB = new Random(i).Next(100, 9999),
RowC = Convert.ToBoolean(i % 2)
});
}
// create a PivotDataGrid
PivotDataGrid pGrid = new PivotDataGrid();
pGrid.HeaderProperty = "Date";
pGrid.ItemsSource = source;
pGrid.Loaded += (se, ev) =>
{
foreach (DataGridColumn dgc in pGrid.Columns)
{
dgc.Header = DateTime.Parse(dgc.Header.ToString()).ToString("yyyy-MM-dd");
}
};
Grid.SetColumn(pGrid, 0);
this.LayoutRoot.Children.Add(pGrid);
// create a common DataGrid
DataGrid dg = new DataGrid();
dg.ItemsSource = source;
Grid.SetColumn(dg, 1);
this.LayoutRoot.Children.Add(dg);
}
}
public class PivotGridSampleItem
{
public DateTime Date { get; set; }
public string RowA { get; set; }
public int RowB { get; set; }
public bool RowC { get; set; }
}
Attach the sample project: