Event Handler Lost when Dynamic Controls are added in a DataGrid

  • Thread starter Ashish Shridharan
  • Start date
A

Ashish Shridharan

Hi All
I have been trying to add a control to the header cell of
a datagrid on my ASP.NET page. These controls are defined
in the HTML as ASP.NET web controls.
They are being added into the header of the datagrid in
the "ItemDataBound" method of the grid. However, once,
they are added in the grid, i seem to lose the event
handler for the same control. Is there anyone who has
tried this before and knows why this is hapenning ??

Thanks in Advance
Ashish Shridharan
 
A

Angela

Yes, I have done something like that. It is a PAIN.

You're "losing" your event handler because the control is
dynamic and if you do a postback it doesn't know that the
control exists, even though it may be in the viewstate.

you will have to recreate the control in the OnInit.
 
A

Ashish Shridharan

Yes, i guessed as much, which is why i have also defined
those controls in the html as independent web server
controls. All i am doing in the ItemDataBound of the
datagrid is positioning those controls in the header.

Now the page posts back for each of these controls. looks
like i'll have to set up a different mechanism to figure
out which control posted back. Any ideas ??
 
A

Angela

you might need to add a HtmlInputHidden control to your
form. Write a JavaScript function and hook it up to the
OnClick event of your control. In the JavaScript set the
value of the HtmlInputHidden control to be the ID of the
control that was clicked. Then when you recreate
everything on PostBack you will know what control was
clicked and your app will know what to do next.
 
A

Angela

it took me about a month to figure it out. I kept posting
to forums and everyone kept asking me why I was doing what
I was doing and no one had a solution.
After a lot of trial and error I got something to work.

Now I watch for posts like yours on forums and I try to
help people who are using dynamic controls. Maybe I can
save someone else some time and headaches. : )

Good luck!!
 
J

Jim Mitchell

Hello Angela -

I am actually sheding tears reading this thread. I also have spent at least
5 sleepless nights trying to figure this out. Also kept posting to the web
and was ignored or people thought I was crazy.

My solution was to use HTML controls with an onclick event setting a flag in
a hidden variable using client side javascript exactly as you defined. I am
a little curious how you can "hook up to the onclick event of your control".
It was exactly this onclick event handler that would not exists and would
not be fired if the control was created dynmamically. Using HTML image
controls solved my problem, but if you get a second, could you please let me
know what you meant by the "hookup"?

Also, thanks for the tid bit on using HtmlInputHidden control. I have to
read up on that. I was using a textbox web form control. One interesting
detail is that I could obviously not set visible to false for the textbox,
but even if I set enabled to fales, the value in the textbox set from
Javascript did not survive the post back.

Thanks again.

Jim
 
J

Jim Mitchell

See my post further down the thread, but.....

I had two work arounds to address the issue.

The first and the one I have stuck with is to use HTML controls with
javascript setting a flag as to which control was clicked. On post back I
do the processing. I am not sure how Angela would trap the onclick event of
a web form imagebutton as this was the event handler that would go away on
me and never fire. Let me know if you get it in the end.

The second approach you might find interesting. The reason the event does
not fire is that the load_page fires before the onclick event. Since the
control was created dynamically, the control did not exist in the load page
and I guess it could never call the event once the event was suppose to
fire. Only you could follow this as you have lived with the pain.

Anyway the work around I did not like was to create a funcing...
MakeControls(). Call Make controlls in the proper location, but call it
again on every postback. Even if the properties of the control were wrong
in the load_page event, you will find that the event handler is available
and your event will fire.

I kept posting the question... How can I make an event hander public and
not tied to a control that is loaded dynamically to find a way around
calling MakeControls twice (once in code and every time I did a postback),
but no one seemed to understand the problem.

I did read one obscure post that talked about Java being way ahead of .NET
when it came to persistance. (sp). I think that may be a clue to the
problem.

I am still sort of waiting for some one to post an answer to .... How do
you create a control at run time and preserve the event handler without
using client side java script.

Jim
 
J

Jim Mitchell

You can check out this thread as well from the datagrid forum.....

That looks like it will work. I am going to give it a shot in a day or two
once I sort out my other challenge. Thanks again. When you see the other
challenge, you do not have to reply. You can wait a month before answering
another of my posts :). I will reply for a final note once this all works.

Stevie_mac said:
You know, the bit that was getting me was having AutoGenerateColumns
off, this just makes this perticular job harder. There is a very easy way
(now that i understand your requirements) to achieve this

The easiest way to do this is...

DG -
* 1st switch on AutoGenerateColumns
* Add a 'Detail' Link Button (in templated col)

Code
* Generate your 1st SQL string (ie Contact View button was clicked)
* Get data, Set DG.Source & Bind it
* In ItemDataBound (for top level), setup the 'Detail' Link
buttons CommandName to 'Contact' - when the LinkButton is
clicked, you'll get it in ItemCommand.
* In ItemCommand, rebuild you SQL based on CommandName
being 'Contact'
* Rebind grid with new SQL query
* In ItemDataBound, setup the 'Detail' Link
buttons CommandName to 'AccountDetail' - when the LinkButton is
clicked, you'll get it in ItemCommand.
* In ItemCommand, rebuild you SQL based on CommandName
being 'AccountDetail'

Does this make sense?

This way, no matter how many columns/fields you have, all will be displayed
with a 'Detail' Link button adjacent (ofcourse you can hide this column once
you reach a 'lowest drill down point'

The only down side is the separate 'Detail' column but if theres room, why sweat it?

I can show you how if you need me too - i dont mind. But you know that the fun is
in the chase, otherwise, i'm gonna leave off for a bit - let you get in to it.

I'll keep track of the post (or you can email me) if i forget

Good luck

Steve - My last post probably confused you because I said "I know I could do
it with 2 datagrids". Let me explain the application a bit further....

I have two "views" I can use controlled by a toolbar. Contact View and
Account View. The screen looks almost the same to the user. There is one
datagrid. In "Contact View" clicking on the datagrid displays the name and
address of the contact. In "Account View", clicking on the datagrid
displays the Account name and Account Address.

So the account datagrid does not populate a datagrid of contacts, there are
just tow different views (modes) the page can be in at any time.

When I said, "I can do it with two datagrids, I meant a datagrid for
Accounts and a datagrid for contacts that I turn visibility off and on.
Even though this would work here, I want to have more views down the road on
the same page for reports etc.. I just want to use a single datagrid for
all views and it seems like I should be able to do so.

Again, I will get back to you once I test your options.

Thanks, Jim


Stevie_mac said:
Jim, I'm not entirely sure what you are doing here so heres my best guess...

You intend to have one grid filled with Company names (link buttons)
1. When one of the Company names is clicked, you want to re-populate
the
grid
grid with Contacts based on your selection??
OR
2. When one of the Company names is clicked, you want to populate a
2nd
grid
with Contacts based on your selection?
Ok, this is what i did...
Tried adding handler DataGrid ItemCommand manually (in BindGrid) - No Good!
Tried adding handler DataGrid ItemSelctionChanged manually (in
BindGrid) -
No
Good!
Tried adding handler button manually (in ItemCreated) - No Good!
Tried adding handler button manually (in ItemDataBound) - No Good!
Tried other tricks - direct from my sleive - No Good.
I Figure i could add a JavaScript handler to the link buttons to
call __DoPostBack() manually but I gave up & went for the EASY OPTION
Templates!!!...

I dont understand (though there usually a reason) why you want to dynamically
create the
LinkButtons in this particular excersize ???


Any hoo, heres my solution (option 2) ***How i got there - The actual code is
below if you wanna skip this)***
Create 1 grid called CompaniesGrid
* add 1 Select Button Column.
* Convert it to a template item + apply
* Right click the grid & choose to Edit Tempate Col 0 - you'll see your
linkbutton
* Open the databinding options for the LinkButton & set custom binding on
the Text property to DataBinder.Eval(Container, "DataItem.Company")
* Set custom binding on the CommandArgument property also. Set this
to DataBinder.Eval(Container, "DataItem.ID")
* OK the changes to your grid
* Add a event handler for ItemCommand
(In ItemCommand, Call function that Binds 2nd grid based on
e.Item.CommandArgument - which by the way will be you ID from
the Companys table - this keys into the Contacts table!)

Create a 2nd grid called ContactsGrid
* Open the property builder & add 1 Bound Column
* Set the Data Field to 'Contact'
* OK this

*************************************
Heres my code (this assumes 2 SQL Tables)
*************************************

*************************************
SQL Tables...
-------------------------------------------------
Table 1 : tblCompanys (ID = Pri Key)
-------------------------------------------------
ID int (Not Null)
Company varchar(50) [what ever!]

-------------------------------------------------
Table 2 : tblContacts (ID + Contact = Pri Key)
-------------------------------------------------
ID int (not Null)
Contact varchar(50) [what ever!]
*************************************


*************************************
VB Code...
-------------------------------------------------
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
If Not IsPostBack Then
BindGrid("Company")
End If
End Sub

Sub BindGrid(ByVal field_name As String, _
Optional ByVal sID As String = "")
Dim mycn As SqlClient.SqlConnection
Dim cmd As SqlClient.SqlCommand
Dim mysql As String
mycn = New SqlClient.SqlConnection( _
System.Configuration.ConfigurationSettings.AppSettings( _
"ConnectString"))
If field_name = "Company" Then
mysql = "Select * from tblCompanys"
mycn.Open()
cmd = New SqlClient.SqlCommand(mysql, mycn)
CompaniesGrid.DataKeyField = "ID"
CompaniesGrid.DataSource = cmd.ExecuteReader()
CompaniesGrid.DataBind()
cmd.Dispose()
mycn.Close()
End If

If field_name = "Contact" Then
mysql = "Select * from tblContacts"
If sID <> "" Then
mysql &= " WHERE ID='" & sID & "';"
End If
mycn.Open()
cmd = New SqlClient.SqlCommand(mysql, mycn)
ContactsGrid.DataKeyField = "ID"
ContactsGrid.DataSource = cmd.ExecuteReader()
ContactsGrid.DataBind()
cmd.Dispose()
mycn.Close()
End If
End Sub

Private Sub CompaniesGrid_ItemCommand( _
ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _
Handles CompaniesGrid.ItemCommand
BindGrid("Contact", e.CommandArgument)
End Sub
*************************************

*************************************
And heres my ASPX code (paste this after <Form...> and before
-------------------------------------------------
<asp:datagrid id="CompaniesGrid"
style="Z-INDEX: 100; LEFT: 16px; POSITION: absolute; TOP: 16px"
runat="server" AutoGenerateColumns="False" Width="216px"
BorderColor="#CCCCCC" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="3">
<SelectedItemStyle Font-Bold="True" ForeColor="White"
BackColor="#669999"></SelectedItemStyle>
<ItemStyle ForeColor="#000066"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="White"
BackColor="#006699"></HeaderStyle>
<FooterStyle ForeColor="#000066" BackColor="White"></FooterStyle>
<Columns>
<asp:TemplateColumn>
<HeaderTemplate>
Companies
</HeaderTemplate>
<ItemTemplate>
<asp:LinkButton id=LinkButton1 runat="server"
Text='<%# DataBinder.Eval(Container, "DataItem.Company") %>'
CommandName="Select" CausesValidation="False"
CommandArgument='<%# DataBinder.Eval(Container, "DataItem.ID") %>'>
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
<PagerStyle HorizontalAlign="Left" ForeColor="#000066"
BackColor="White" Mode="NumericPages"></PagerStyle>
</asp:datagrid>
<asp:datagrid id="ContactsGrid"
style="Z-INDEX: 104; LEFT: 240px; POSITION: absolute; TOP: 16px"
runat="server" AutoGenerateColumns="False" Width="264px"
BorderColor="#CCCCCC" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="3">
<SelectedItemStyle Font-Bold="True" ForeColor="White"
BackColor="#669999"></SelectedItemStyle>
<ItemStyle ForeColor="#000066"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="White"
BackColor="#006699"></HeaderStyle>
<FooterStyle ForeColor="#000066" BackColor="White"></FooterStyle>
<Columns>
<asp:BoundColumn DataField="Contact"
HeaderText="Contacts"></asp:BoundColumn>
</Columns>
<PagerStyle HorizontalAlign="Left" ForeColor="#000066"
BackColor="White" Mode="NumericPages"></PagerStyle>
</asp:datagrid>
*************************************


Hello Steve - Thanks for the inspiration, but I may never get it :).


SNIP <<<<<<<<<<<<<
 
A

Ashish Shridharan

Hi Jim,

The idea is to (via javascript) save an identifier (for
the control that invoked the postback) in the hidden field.

Now, in the pageload event of the page, check the value of
the hidden field and based on that, call the corresponding
method (which would have been called by that particular
control under normal circumstances).

A sample code snippet for your pageload event

Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
if Page.IsPostBack then
if htmlhidden.value = "textbox3" then
textbox3_OnClick(nothing, nothing)
end if
end if
end sub

Hope this helps

Ashish Shridharan
 

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

Top