Génération automatique d'un gridview et ses templates

Martin TREMBLAY

Cet article a pour but de vous présenter une technique pour générer dynamiquement un gridview avec un template sur mesure avec la méthode databind sur un formulaire à partir d'une structure de données. Soit une BD soit un fichier XML. ♪

- Titre : Génération automatique d'un gridview et ses templates

- Auteur : Martin TREMBLAY

- Parution : 6 mars 2003

- Licence : Les sources présentés sur cette pages sont libre de droits, et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright Martin Tremblay . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérets.

Sommaire

- I. Introduction

- II. Avant de commencer

- III. Création de la gridview dynamiquement

- IV. Ajout d'une classe Itemplate

- V. Utilisation de ITemplate

- VI. Conclusion

I - Introduction

Dans de nombreuses situations, il est nécessaire de créer une gridview à la volée avec des données qui risquent de changer en cours de route. Cet article montre comment créer un gridview, y rajouter ses évènements de modification, maj et cancel. Et surtout la création de colonnes templates à la volée avec la méthode databind personnalisée selon le type de contrôle dans le template.

II - Avant de commencer

Vous démarrez idéalement d'une page vierge que nous appellerons Visualisation.Aspx. Placez dans cette page, un contrôle PlaceHolder qu'on nommera Placeholder1. Dans votre code HTML il devrait y avoir :

<asp:PlaceHolder ID=" PlaceHolder1" runat=" server"></asp:PlaceHolder>

Assurez-vous d'avoir une base de donnée SQL pour tester (NorthWind sera utilisée dans cet exemple). Ceci étant la base utilisée dans cet exemple. Maintenant que nous avons la base, nous pouvons nous concentrer sur le « code behind ».

III - Création de la gridview dynamiquement

Le gridview, comme n'importe quel autre contrôle du framework, peut être créé à l'aide d'un simple Dim Dans un premier temps, on va créer le contrôle, modifier ses propriétés et le lier à un datasource. Ensuite, vous allez voir, que les colonnes créées ne seront pas associées au gridview, mais au DataSource. Dans cet exemple, je me sers de la base exemple NorthWind sur SQL server 2000.

Dim cnn As New System.Data.SqlClient.SqlConnection ("Data Source=Serveur;Initial Catalog=Northwind;User ID=userId;password=MotDePasse")

Dim myDataset As New DataSet
Dim myDataset2 As New DataSet
Dim MyTable As DataTable = New DataTable
Dim MyDatarow As DataRow

Dim dtr As New SqlClient.SqlDataAdapter("SELECT Products.ProductID, Products.ProductName, Products.CategoryID Products ON Categories.CategoryID = Products.CategoryID", cnn)
Dim dtrCategory As New SqlClient.SqlDataAdapter("SELECT Categories.CategoryID, Categories.CategoryName FROM Categories ", cnn)

dtr.Fill(myDataset)
dtrCategory.Fill(myDataset2)

Dim GrdDynamic As New GridView

GrdDynamic.DataSource = myDataset.Tables(0)
GrdDynamic.DataBind()
PlaceHolder1.Controls.Add(GrdDynamic)

OK, le résultat ressemble à ceci :

Figure 1

Simple non? Mais, comment modifier les lignes ? Et également avoir plusieurs choix pour la catégorie ? C'est là que la création dynamique des templates est obligatoire.

IV - Ajout d'une classe Itemplate

Un template, pour vulgariser, est une description particulière d'un rendu au moment de l'exécution. Il détermine l'aspect « layout » et la partie liée « binding » d'un contrôle. Parce que nous ne savons pas, a priori, quels champs seront dans notre datagrid, nous devons utiliser des templates dynamiques. Ceci se fait avec l'implémentation de Itemplate. Nous devons générer le ItemTemplate et le EditItemTemplate dynamiquement pour chacun des champs.

ITemplate a une méthode InstantiateIn que je décrirai plus bas. La classe GridViewTemplate qui implémente cette interface, expose n'importe quelle propriété de la gridview comme la sienne, alors le « layout » par défaut est surpassé. Pour pouvoir réutiliser la classe dans d'autres situations, j'ai décidé de l'extentier dans une classe à part.

Voici la structure générale de la classe GridViewTemplate :

Public Class GridViewTemplate
Implements ITemplate

Dim _templateType As ListItemType
Dim _columnName As String
Dim _TypeControl As String = "TextBox"
Dim _Dts As DataSet

Public Sub New(ByVal Type As ListItemType, ByVal colname As String, ByVal TypeControl As String)

_templateType = Type
_columnName = colname
_TypeControl = TypeControl

End Sub

Public Sub InstantiateIn
End Sub

Sub DataBinding (ByVal sender As Object, ByVal e As EventArgs)
End Sub

_templateType est le type de template (Item, Edit, etc.), _columnName le nom de la colonne, et _TypeControl le type de contrôle qui sera rendu dans notre template. Vous pouvez avoir plusieurs constructeurs, moi j'ai choisi d'en avoir deux. Ceci pour maîtriser si un template contient une liste de valeurs passée comme un dataset que je peux associer à un listbox ou un autre contrôle.

Public Sub New(ByVal Type As ListItemType, ByVal colname As String, ByVal TypeControl As String)

_templateType = Type
_columnName = colname
_TypeControl = TypeControl

End Sub

Public Sub New(ByVal Type As ListItemType, ByVal colname As String, ByVal TypeControl As String, ByVal dts As DataSet)

_templateType = Type
_columnName = colname
_TypeControl = TypeControl
_Dts = dts

End Sub

La méthode InstantiateIn est la première partie de ma classe. Cette méthode s'assure que le type de template est créé par rapport à son contrôle prédéfini. Je vais dans cet exemple m'occuper type ItemTemplate et de EditTemplate. Il y en a d'autres : HeaderTemplate etc. Regardons mon exemple :

Select Case _templateType

Case ListItemType.Item

Select Case UCase (_TypeControl)
Case "BUTTON"
Dim tb1 As Button = New Button()
AddHandler tb1.Click, AddressOf tb1_EditClik
tb1.CommandName = "EDIT"
container.Controls.Add(tb1)
Case "LABEL"
Dim tb1 As Label = New Label()
tb1.BorderStyle = BorderStyle.None
AddHandler tb1.DataBinding, AddressOf tb1_DataBindingLabel
container.Controls.Add(tb1)
Case "LISTBOX"
Dim mylistbox As DropDownList = New DropDownList
mylistbox.DataSource = _Dts.Tables(0)
AddHandler mylistbox.DataBinding, AddressOf tb1_DataBindingListBox
mylistbox.DataTextField = _columnName
mylistbox.DataValueField = _columnName2
mylistbox.ID = ("lstDynamique")
mylistbox.Enabled = False
container.Controls.Add(mylistbox)
End Select
End Select

Un point important à ne pas manquer c'est que j'initialise la propriété CommandName de mon bouton à « EDIT ». Ceci aura pour effet de démarrer la méthode associée au gridview.

L'autre point important c'est que j'ai rajouté des AddHandlers. C'est là que j'effectue mon databinding. Remarquez bien la partie DataBinder.Eval.

Pour l'étiquette :

Sub tb1_DataBindingLabel(ByVal sender As Object, ByVal e As EventArgs)

Dim lbldata As Label = CType(sender, Label)
Dim container As GridViewRow = CType(lbldata.NamingContainer, GridViewRow)
Dim dataValue As Object = DataBinder.Eval(container.DataItem, _columnName)

If Not IsNothing(dataValue) Then
lbldata.Text = WrappableText(dataValue.ToString)
End If

End Sub

Pour le listBox :

Sub tb1_DataBindingListBox(ByVal sender As Object, ByVal e As EventArgs)

Dim lst As DropDownList = CType(sender, DropDownList)
Dim container As GridViewRow = CType(lst.NamingContainer, GridViewRow)
Dim dataValue As Object = DataBinder.Eval(container.DataItem, _columnName)

If Not IsNothing(dataValue) Then
lst.SelectedValue = dataValue.ToString
End If

End Sub

Aucune différence !!!!!!!!!!!!!!! OK cela c'est pour le ITEMTEMPLATE, celui du EDIT est un peut différent. Regardez :

Case ListItemType.EditItem

Select Case UCase (_TypeControl)
Case "BUTTON"
Dim tb1 As Button = New Button()
tb1.Visible = True
tb1.CommandName = "Update"
If HttpContext.Current.Session ("InsertFlag") = 1 Then
tb1.Text = "Ajouter"
Else
tb1.Text = "MAJ"
End If
tb1.OnClientClick = "return confirm('Êtes-vous sûr de MAJ ?')"
container.Controls.Add(tb1)
Dim tb2 As Button = New Button()
tb2.CommandName = "Cancel"
container.Controls.Add(tb2)
Case "LABEL"
Dim tb1 As Label = New Label()
tb1.BorderStyle = BorderStyle.None
AddHandler tb1.DataBinding, AddressOf tb1_DataBindingLabel
tb1.Visible = True
container.Controls.Add(tb1)
Case "LISTBOX"
Dim mylistbox As DropDownList = New DropDownList
mylistbox.DataSource = _Dts.Tables(0)
AddHandler mylistbox.DataBinding, AddressOf tb1_DataBindingListBox
mylistbox.DataTextField = _columnName
mylistbox.DataValueField = _ColumnName2
mylistbox.ID = ("lstDynamique")
mylistbox.Enabled = True
container.Controls.Add(mylistbox)
Case "TEXTBOX"
Dim tb1 As TextBox = New TextBox()
tb1.BorderStyle = BorderStyle.None
AddHandler tb1.DataBinding, AddressOf tb1_DataBindingTextBox
tb1.Columns = 4
tb1.Visible = True
container.Controls.Add(tb1)
End Select
End Select

J'ai deux boutons au lieu d'un : Update et Cancel. Les deux sont associés aux CommandName Update et Cancel. Le Listbox est actif et il y a un Textbox. De plus, remarquez que ce sont les mêmes AddHandlers… C'est parce que le databinding est bidirectionnel. C'est la même méthode qui gère la récupération et la sauvegarde !

OK l'implémentation de l'ITemplate est terminée. Allons voir comment s'en servir.

V - Utilisation de ITemplate

Vous avez créé la gridview et créé la classe qui implémente ITemplate. Maintenant on va voir comment s'en servir !

En premier lieu il faut modeler la table et les colonnes qui vont structurer notre gridview.

Dim MyTable As DataTable = New DataTable
Dim MyDatarow As DataRow

Dim dcol As Data.DataColumn = New Data.DataColumn("ProductId", GetType(System.Int32))
MyTable.Columns.Add(dcol)

dcol = New Data.DataColumn("ProductName", GetType(System.String))
MyTable.Columns.Add(dcol)

dcol = New Data.DataColumn("CategoryID", GetType (System.Int32))
MyTable.Columns.Add(dcol)

Ensuite, y rajouter les lignes :

For Each MyDatarow In myDataset.Tables(0).Rows
Dim drow As Data.DataRow = MyTable.NewRow

drow("ProductId") = MyDatarow("ProductId")
drow("ProductName") = MyDatarow("ProductName")
drow("CategoryID") = MyDatarow("CategoryID")
MyTable.Rows.Add(drow)
Next

Notre table est maintenant modélisée. Maintenant, attardons-nous au gridview. Premièrement, il faut lui dire que les colonnes ne seront pas générées automatiquement . Et ensuite, lui rajouter les méthodes Update, Edit et Cancel:

GrdDynamic.AutoGenerateColumns = False

AddHandler GrdDynamic.RowEditing, AddressOf GrdDynamic_RowEditing
AddHandler GrdDynamic.RowUpdated, AddressOf GrdDynamic_RowUpdated
AddHandler GrdDynamic.RowCancelingEdit, AddressOf GrdDynamic_RowCancelingEdit

OK tout est prêt pour la magie… Il faut rajouter dans notre table (et ensuite dans notre gridview) les champs Template. C'est assez facile. Commençons par les boutons de commande :

Dim cmdfield As TemplateField = New TemplateField

cmdfield.ItemTemplate = New GridViewTemplate(ListItemType.Item, "...", "button")
cmdfield.EditItemTemplate = New GridViewTemplate(ListItemType.EditItem, "...", "button")

GrdDynamic.Columns.Add (cmdfield)

Et maintenant pour chaque colonne de la table :

For Each col As DataColumn In MyTable.Columns

Dim bfield As TemplateField = New TemplateField

If col.ColumnName = "CategoryID" Then
Dim ColumnName2 As String = "CategoryName"
bfield.ItemTemplate = New GridViewTemplate(ListItemType.Item, col.ColumnName, ColumnName2, "ListBox", myDataset2)
bfield.EditItemTemplate = New GridViewTemplate(ListItemType.EditItem, col.ColumnName, ColumnName2, "ListBox", myDataset2)
ElseIf col.ColumnName = "ProductId" Then
bfield.ItemTemplate = New GridViewTemplate(ListItemType.Item, col.ColumnName, "Label")
ElseIf col.ColumnName = "ProductName" Then
bfield.ItemTemplate = New GridViewTemplate(ListItemType.Item, col.ColumnName, "Label")
bfield.EditItemTemplate = New GridViewTemplate(ListItemType.EditItem, col.ColumnName, "textbox")
End If

GrdDynamic.Columns.Add(bfield)

Next

Et finalement je lie le tout au Gridview

GrdDynamic.DataSource = myDataset. Tables (0)
GrdDynamic.DataBind()
PlaceHolder1.Controls.Add(GrdDynamic)

Il ne faut surtout pas oublier de créer les méthodes GrdDynamic_RowUpdated, GrdDynamic_RowEdited et GrdDynamic_RowCancelingEdit. Dans la méthode GrdDynamic_RowEditing ne pas oublier de modifier l'index et d'effectuer le databind du gridview :

Sub GrdDynamic_RowEditing (ByVal sender As Object, ByVal e As GridViewEditEventArgs)

CType(sender, GridView).EditIndex = e.NewEditIndex
CType(sender, GridView).DataBind()
End Sub

Vous devriez maintenant avoir ceci :

VI - Conclusion

La création d'un gridview avec ses évènements traditionnels et des templates de manière dynamique peut s'avérer parfois très utile. J'ai, dans cet article essayé de vous démontrer de manière simple comment le faire. J'ai volontairement mis de côté des bouts qui auraient amené de la confusion. L'essentiel est pourtant la. Vous pouvez jouez du côté du datasource, des évènements et rajouter n'importe quel contrôle dans les templates.

Amusez-vous bien !