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 :
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 !