Dynamically added Templated Columns (Implementing ITemplate) in DataGrid - How do I wire up events?

D

David

Hi,

I have a VB.NET / ASP.NET program I'm trying to debug in which the columns in the DataGrid change depending on user selections. The data is sourced from a database, so the number of rows varies with the selection parameters. The columns are being added dynamically at runtime. There are 4 columns that are templated columns implementing the ITemplate interface. These columns are TextBoxes in which I want to be able to handle the TextChanged event. (No Edit / Update button on the grid, just a blank TextBox that should raise the TextChanged event when it loses focus.)

I have enabled AutoPostback, as well as ViewState. However, the TextChanged event is never fired. After researching a bit on the internet, it appears as if it won't be fired because the control doesn't exist on the Page_Load event. I've seen numerous examples of how to deal with dynamically created controls on a WebForm, and numerous questions on how to do this in a DataGrid, but no explicit answers on how to accomplish it.

Any suggestions (especially code examples!) would be greatly appreciated!

Thanks!
David
 
J

Jeffrey Tan[MSFT]

Hi David,

Thank you for posting in the community!

Based on my understanding, your column implement the ITemplate interface,
which contains TextBox. Then you dynamicly add this column into the
datagrid control. But the TextBox's changed event does not fire.

=================================================
Can you paste some code snippet to reproduce out your issue?

Actually, I have writen a sample project for you like this:

Dim ds As DataSet
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim tc As TemplateColumn = New TemplateColumn
Dim tbc As TextBoxColumn = New TextBoxColumn
tc.ItemTemplate = tbc
DataGrid1.Columns.Add(tc)

getsource()
dodatabinding()
End Sub

Private Sub getsource()
Dim adapter As SqlDataAdapter = New SqlDataAdapter("select * from
jobs", "server=localhost;database=pubs;uid=sa;pwd=")
ds = New DataSet
adapter.Fill(ds)
End Sub

Private Sub dodatabinding()
DataGrid1.DataSource = ds
DataGrid1.DataBind()
End Sub

Public Class TextBoxColumn
Implements ITemplate

Public Sub InstantiateIn(ByVal container As System.Web.UI.Control)
Implements System.Web.UI.ITemplate.InstantiateIn
Dim tb As TextBox = New TextBox
tb.AutoPostBack = True
tb.ID = "tb"
AddHandler tb.TextChanged, AddressOf TextChangedHandler
container.Controls.Add(tb)
End Sub

Private Sub TextChangedHandler(ByVal sender As Object, ByVal e As
EventArgs)
CType(sender, TextBox).Page.Response.Write("TextChangedHandler")
End Sub
End Class

This works well.
Note: I use the SqlServer's default "jobs" table in "pubs" database.

=============================================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.
Have a nice day!!

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
D

David

Hi Jeffrey

I'm doing something a bit different in my program where I dynamically determine what columns are to be displayed (user selectable). I'm going to try your example to see if I can make it work. If not, I'll come up with a sample of my own to better illustrate what I'm trying to do in my code

Thanks
David
 
D

David

Hi Jeffrey

Here's an example of what I'm trying to do in my program. I stripped out most of the functionality so I could just test the events on the dynamically generated TextBox. I think all you have to do to recreate this is create a new WebForm, add a DataGrid to it (DataGrid1), set the AutoGenerateColumns property for the DataGrid to False, cut / paste this code into the code-behind page, and try it out

I'm using the Sales table in the Pubs database. You will need to go and set a few of the "qty" fields to 0 to see the textbox show up in the rendered page

I'd appreciate any comments / suggestions you have

Thanks
Davi

********* Code Below ************

Imports System.Data.SqlClien

Public Class WebForm
Inherits System.Web.UI.Pag

Dim CONN_STR As String = "server=localhost;Integrated Security=SSPI;Initial Catalog=pubs

#Region " Web Form Designer Generated Code

'This call is required by the Web Form Designer
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent(

End Su
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGri

'NOTE: The following placeholder declaration is required by the Web Form Designer
'Do not delete or move it
Private designerPlaceholderDeclaration As System.Objec

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Ini
'CODEGEN: This method call is required by the Web Form Designe
'Do not modify it using the code editor
InitializeComponent(
End Su

#End Regio

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Loa
'Put user code to initialize the page her
If Not IsPostBack The
bindGrid(
Els
'Need to do something here? Calling bindGrid() again in the Page_PreRender() even
'to show the updated data (should be updated in the TextChanged event, but it's no
'firing...??
End I

End Su

Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRende

bindGrid(

End Su

'Note using the Pubs database, Sales table

Protected Sub bindGrid(
Dim da As SqlDataAdapte
Dim sqlStr As Strin
Dim ds As DataSe

sqlStr = "Select * from Sales
da = New SqlDataAdapter(sqlStr, CONN_STR
ds = New DataSe

da.Fill(ds

DataGrid1.DataSource = ds.Tables(0
getColumns(
DataGrid1.DataBind(
End Su

'Here I'm generating my columns I want to display. I'm not giving any chance to
'hide / show columns in this demonstration but in the real application, I woul
'selectively generate the list of columns
Protected Sub getColumns(
Dim li As ArrayLis
Dim b As BoundColum
Dim colTem As TemplateColum

li = New ArrayList(10
DataGrid1.Columns.Clear(

b = New BoundColum
b.DataField = "stor_id
b.HeaderText = "Store ID
li.Add(b

b = New BoundColum
b.DataField = "ord_num
b.HeaderText = "Order Number
li.Add(b

b = New BoundColum
b.DataField = "ord_date
b.HeaderText = "Order Date
li.Add(b

colTem = New TemplateColum
colTem.ItemTemplate = New SupplierColum
colTem.HeaderText = "Quantity
li.Add(colTem

b = New BoundColum
b.DataField = "payterms
b.HeaderText = "Payment Terms
li.Add(b

b = New BoundColum
b.DataField = "title_id
b.HeaderText = "Title ID
li.Add(b

'Add our columns to our DataGrid now..
For Each c As DataGridColumn In l
DataGrid1.Columns.Add(c
Nex

End Su

End Clas

'*******************************

' SupplierColumn Class Definitio

'*******************************
Public Class SupplierColum
Implements System.Web.UI.ITemplat

Public Sub textChanged(ByVal sender As Object, ByVal e As EventArgs
'TODO: Complete update process here!

CType(sender, TextBox).Page.Response.Write("TextChangedHandler")

End Sub

'In the bindData event, we check to see if the "qty" field of the Sales
'table in the Pubs database is 0. If it is, then we want to display a
'TextBox so that someone can update the value. Otherwise, we display
'a literal with the actual value from the DB.
'
'Note: You will have to go and change a few values to 0 to test this out!
Public Sub bindData(ByVal sender As Object, ByVal e As EventArgs)
Dim t As TextBox
Dim dg As DataGridItem
Dim dv As DataRowView
Dim val As String
Dim li As Literal
Dim qty As Integer

t = CType(sender, TextBox)
dg = CType(t.NamingContainer, DataGridItem)
dv = CType(dg.DataItem, DataRowView)
li = CType(t.Parent.Controls(0), Literal)

val = dv("qty").ToString()
qty = Integer.Parse(val)
If qty = 0 Then
t.Visible = True
li.Visible = False
Else
li.Visible = True
li.Text = val
t.Visible = False
End If

End Sub

'Required by the ITemplate Interface. I am adding my handlers here, as well as setting
'the AutoPostBack property to True for the TextBox.
Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) Implements System.Web.UI.ITemplate.InstantiateIn

Dim tb As TextBox
Dim li As Literal
tb = New TextBox
li = New Literal
li.ID = "LI1"
tb.ID = "TB1"
tb.AutoPostBack = True
AddHandler tb.TextChanged, AddressOf textChanged
AddHandler tb.DataBinding, AddressOf bindData

container.Controls.Add(li)
container.Controls.Add(tb)

End Sub
End Class
 
D

David

Hi Jeffrey

I think I got my code working. I moved my bindGrid() call from the Page_Load event to the Page_Init event, and that seemed to take care of the problem

Thanks for your help
David
 
J

Jeffrey Tan[MSFT]

Hi David,

Thanks very much for your feedback.

I am glad you find a way to get it work. After reviewing your original
code, I will explain the original "Error" behavior for you. Hope it can
help you.

===================================================
When you dynamicly add TemplateColumn into the datagrid control,
ITemplate.InstantiateIn will be called when your datagrid is
databinding.(That is DataGrid1.DataBind() method is called). So you must
place the TempalteColumn creation and column adding code before the
DataGrid.DataBind method.(In your original code, you have done this, at
this point, your code is correct)

When page postback(In your case, your page postback is caused by
TextChanged), the DataGrid.DataBind method will not be called(Because you
have judged the Page.IsPostBack property). In this situation, the whole
datagrid columns will be re-created from the ViewState. So LoadViewState
method acts as the DataBind() method's position. So ITemplate.InstantiateIn
will be called after LoadViewState method. To make your TemplateColumn work
well, you must place your column into the datagrid before LoadViewState
method is called.(Or the ITemplate.InstantiateIn will not be called) While
in asp.net LifeCyle, LoadViewState method is called before the Load event
and after the Init event, so when you place your code into the Init event,
all works well.

I think this explains your un-expected behavior of your Asp.net application.

===================================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.
Have a nice day!!

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top