Prenons une simple classe "business" que l'on souhaite afficher dans une GridView:

public class DocumentAttache
{
    public int TypeDocument { get; set; }
    public string Nom { get; set; }
    public string Description { get; set; }
}

Créons la grille dans une page ASP.NET

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        List<DocumentAttache> liste = new List<DocumentAttache>();
        liste.Add(new DocumentAttache() { TypeDocument = 1, Nom = "Doc1", Description = "Description Doc1" });
        liste.Add(new DocumentAttache() { TypeDocument = 1, Nom = "Doc2", Description = "Description Doc2" });
        liste.Add(new DocumentAttache() { TypeDocument = 1, Nom = "Doc3", Description = "Description Doc3" });
        liste.Add(new DocumentAttache() { TypeDocument = 2, Nom = "Doc4", Description = "Description Doc4" });
        liste.Add(new DocumentAttache() { TypeDocument = 2, Nom = "Doc5", Description = "Description Doc5" });
        liste.Add(new DocumentAttache() { TypeDocument = 3, Nom = "Doc7", Description = "Description Doc7" });
        liste.Add(new DocumentAttache() { TypeDocument = 3, Nom = "Doc7", Description = "Description Doc7" });
        this.GridView1.DataSource = liste;
        this.GridView1.DataBind();
        MergeColumn(this.GridView1, 0);
    }
}

Comme vous avez pu le voir plus haut dans le remplissage de la gridview, il y a la dernière ligne du précédent code qui est "intéressante" pour le "merge des colonnes.

Mais qu'en est il derrière ?

Le merge des colonnes

Voici la réponse :

private static void MergeColumn(GridView gv, int position)
{
    Dictionary<string, int> hash = new Dictionary<string, int>();
    string currentText = string.Empty;
    int count = 0;
    int currentCell = 0;
    do
    {
        GridViewRow dataRow = gv.Rows[currentCell++];
        if (dataRow.RowType == DataControlRowType.DataRow)
        {
            bool sameText = dataRow.Cells[position].Text == currentText;
            if (currentCell == gv.Rows.Count)
            {
                if (!sameText)
                {
                    // Fin mais pas meme texte
                    if (count > 1)
                        hash.Add(currentText, count);
                }
                else
                {
                    // Fin meme texte
                    hash.Add(currentText, count + 1);
                }
            }
            else
            {
                // Pas meme texte
                if (!sameText)
                {
                    if (count > 1)
                        hash.Add(currentText, count);
                    currentText = dataRow.Cells[position].Text;
                    count = 0;
                }
            }

            ++count;
        }
    }
    while (currentCell < gv.Rows.Count);

    currentText = string.Empty;
    foreach (GridViewRow row in gv.Rows)
    {
        if (row.RowType == DataControlRowType.DataRow &&
            hash.ContainsKey(row.Cells[position].Text))
        {
            if (row.Cells[position].Text == currentText)
                row.Cells.Remove(row.Cells[position]);
            else
            {
                currentText = row.Cells[position].Text;
                row.Cells[position].RowSpan = hash[currentText];
                row.Cells[position].VerticalAlign = VerticalAlign.Middle;
            }

        }

    }
}

Voici le résultat:

Evidement, je suis preneur pour simplifier mon code de merge ;-). Mais ce soir ... la migraine m'empêche d'optimiser =S