I needed to rotate the content of my GridView control. I was databinding using business objects. Why is there no boolean pivot switch?
So I made my own control that inherits GridView. This is just a C# file; to use it, put it in the App_Code folder, and add <%@ Register Namespace="CustomControls" TagPrefix="Custom" %> to the ASPX file.
Not sure if it's buggy or not but it currently passes initial tests.
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
namespace CustomControls {
public partial class PivotDataGrid : System.Web.UI.WebControls.DataGrid
{
public PivotDataGrid() : base() { }
public enum Pivot
{
/// <summary>
/// Rotates the grid by rotating the data source in a replacement matrix. (not implemented)
/// </summary>
PreRender,
/// <summary>
/// Rotates the grid by converting the output to XML and rotating the cells.
/// </summary>
PostRender
}
private Pivot _PivotMode = Pivot.PostRender;
public Pivot PivotMode
{
get { return _PivotMode; }
set { _PivotMode = value; }
}
protected override void Render(HtmlTextWriter writer)
{
switch (PivotMode)
{
case Pivot.PostRender:
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.IO.StringWriter sw = new System.IO.StringWriter(sb);
HtmlTextWriter htw = new HtmlTextWriter(sw);
base.Render(htw);
System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();
xDoc.LoadXml(sb.ToString().Replace(" ", " "));
System.Xml.XmlDocument xDoc2 = new System.Xml.XmlDocument();
xDoc2.LoadXml(xDoc.OuterXml);
XmlNodeList rowNodes = xDoc.SelectNodes("//tr");
XmlNode trParent = null;
if (rowNodes.Count > 0) trParent = rowNodes[0].ParentNode;
rowNodes = xDoc2.SelectNodes("//tr");
XmlNode trParent2 = null;
if (rowNodes.Count > 0) trParent2 = rowNodes[0].ParentNode;
for (int i = rowNodes.Count - 1; i >= 0; i--)
{
rowNodes[i].ParentNode.RemoveChild(rowNodes[i]);
}
RotateRows(trParent, trParent2, xDoc2);
writer.Write(xDoc2.OuterXml);
break;
default:
base.Render(writer);
return;
}
}
protected override void DataBind(bool raiseOnDataBinding)
{
switch (PivotMode)
{
case Pivot.PreRender:
RotateDataSource();
base.DataBind(raiseOnDataBinding);
break;
case Pivot.PostRender:
base.DataBind(raiseOnDataBinding);
break;
}
}
void RotateDataSource()
{
//object src = base.DataSource;
//System.Reflection.PropertyInfo[] srcProps = src.GetType().GetProperties(System.Reflection.BindingFlags.GetProperty);
//Dictionary<string, Dictionary<string, object>> newSource = new Dictionary<string, object>();
//DataKeyArray keys = this.DataKeys;
throw new NotImplementedException("PivotMode of Pivot.PreRender has not yet been implemented.");
}
//Cell[,] RowsToCells
void RotateRows(XmlNode sourceParentNode, XmlNode targetParentNode, // bool rotate
XmlDocument targetContext)
{
bool rotate = true;
XmlNodeList trNodes = sourceParentNode.SelectNodes("tr");
int rowLen = trNodes.Count;
int colLen = 0;
if (rowLen > 0) colLen = trNodes[0].SelectNodes("td").Count;
Cell[,] cells;
List<XmlAttribute>[] rowAttribs = new List<XmlAttribute>[rowLen];
if (!rotate) cells = new Cell[rowLen, colLen];
else cells = new Cell[colLen, rowLen];
for (int r = 0; r < rowLen; r++)
{
rowAttribs[r] = new List<XmlAttribute>();
for (int a = 0; a < trNodes[r].Attributes.Count; a++)
{
XmlAttribute attrib = targetContext.CreateAttribute(trNodes[r].Attributes[a].Name);
attrib.Value = trNodes[r].Attributes[a].Value;
rowAttribs[r].Add(attrib);
}
for (int c = 0; c < colLen; c++)
{
XmlNode tdNode = trNodes[r].SelectNodes("td")[c];
Cell cell = new Cell();
//cell.Attributes = tdNode[c].Attributes;
cell.Attributes = new List<XmlAttribute>();
for (int a = 0; a < tdNode.Attributes.Count; a++)
{
XmlAttribute attrib = targetContext.CreateAttribute(tdNode.Attributes[a].Name);
attrib.Value = tdNode.Attributes[a].Value;
cell.Attributes.Add(attrib);
}
cell.InnerXml = trNodes[r].SelectNodes("td")[c].InnerXml;
if (rotate) cells[c, r] = cell;
else cells[r, c] = cell;
}
}
//return cells;
for (int cR = cells.GetLowerBound(0); cR <= cells.GetUpperBound(0); cR++)
{
XmlNode rowNode = targetContext.CreateElement("tr");
for (int cC = cells.GetLowerBound(1); cC <= cells.GetUpperBound(1); cC++)
{
Cell cell = cells[cR, cC];
XmlNode colNode = targetContext.CreateElement("td");
for (int a = 0; a < cell.Attributes.Count; a++)
{
colNode.Attributes.Append(cell.Attributes[a]);
}
colNode.InnerXml = cell.InnerXml;
if (rotate)
for (int rA = 0; rA < rowAttribs[cC].Count; rA++)
{
if (colNode.Attributes[rowAttribs[cC][rA].Name] != null)
{
colNode.Attributes[rowAttribs[cC][rA].Name].Value = rowAttribs[cC][rA].Value;
}
else
{
XmlAttribute newAttrib = targetContext.CreateAttribute(rowAttribs[cC][rA].Name);
newAttrib.Value = rowAttribs[cC][rA].Value;
colNode.Attributes.Append(newAttrib);
}
}
rowNode.AppendChild(colNode);
}
if (!rotate)
for (int rA = 0; rA < rowAttribs[cR].Count; rA++)
{
rowNode.Attributes.Append(rowAttribs[cR][rA]);
}
targetParentNode.AppendChild(rowNode);
}
}
class Cell
{
private List<XmlAttribute> _Attributes;
public List<XmlAttribute> Attributes
{
get { return _Attributes; }
set { _Attributes = value; }
}
private string _InnerXml;
public string InnerXml
{
get { return _InnerXml; }
set { _InnerXml = value; }
}
}
}
}