Sign in to follow this  

[.net] Strange problem with adding buttons dynamically to webforms

This topic is 4807 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have this function (as well as another that is almost just like it). This function draws a table with some info, then I add a button. It worked while I actually had a table on the webpage, but when I try to add the table dynamically, it quits working:
Public Sub DrawSingleSystemWithButton(ByVal Position As Integer, ByVal Systems1 As MainSystems, ByVal NestedSystems1 As NestedSystems, ByVal ButtonText As String)
	Dim SystemTable As Web.UI.WebControls.Table = New Web.UI.WebControls.Table
	Dim ButtonRow As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow
	Dim ButtonCell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell
	Dim MyButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button

	DrawHeaderRow(SystemTable)
	DrawSystem(Position, Systems1, NestedSystems1, SystemTable)

	MyButton.Text = ButtonText
	MyButton.Width = Unit.Percentage(100)

	ButtonCell.ColumnSpan = 2
	ButtonCell.Controls.Add(MyButton)

	ButtonRow.Cells.Add(ButtonCell)
	SystemTable.Rows.Add(ButtonRow)

	AddHandler MyButton.Command, AddressOf OnSelect

	Me.Page.Controls.Add(SystemTable)
End Sub

It used to be this (I just put a blank table on my asp.net page and passed it in, rather than declaring a table in the function and then doing Page.Controls.Add()). It worked fine like this:
Public Sub DrawSingleSystemWithButton(ByVal Position As Integer, ByVal Systems1 As MainSystems, ByVal NestedSystems1 As NestedSystems, ByVal ButtonText As String, ByRef SystemTable As System.Web.UI.WebControls.Table)
	Dim ButtonRow As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow
	Dim ButtonCell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell
	Dim MyButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button

	DrawHeaderRow(SystemTable)
	DrawSystem(Position, Systems1, NestedSystems1, SystemTable)

	MyButton.Text = ButtonText
	MyButton.Width = Unit.Percentage(100)

	ButtonCell.ColumnSpan = 2
	ButtonCell.Controls.Add(MyButton)

	ButtonRow.Cells.Add(ButtonCell)
	SystemTable.Rows.Add(ButtonRow)

	AddHandler MyButton.Command, AddressOf OnSelect
End Sub

So I changed very little. Yet after the changes, it no longer works, and I get this error message:
Quote:
Control '_ctl4' of type 'Button' must be placed inside a form tag with runat=server. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Web.HttpException: Control '_ctl4' of type 'Button' must be placed inside a form tag with runat=server. Source Error: An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. Stack Trace: [HttpException (0x80004005): Control '_ctl4' of type 'Button' must be placed inside a form tag with runat=server.] System.Web.UI.Page.VerifyRenderingInServerForm(Control control) System.Web.UI.WebControls.Button.AddAttributesToRender(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.RenderBeginTag(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.Render(HtmlTextWriter writer) System.Web.UI.Control.RenderControl(HtmlTextWriter writer) System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) System.Web.UI.Control.Render(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.RenderContents(HtmlTextWriter writer) System.Web.UI.WebControls.TableCell.RenderContents(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.Render(HtmlTextWriter writer) System.Web.UI.Control.RenderControl(HtmlTextWriter writer) System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) System.Web.UI.Control.Render(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.RenderContents(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.Render(HtmlTextWriter writer) System.Web.UI.Control.RenderControl(HtmlTextWriter writer) System.Web.UI.WebControls.Table.RenderContents(HtmlTextWriter writer) System.Web.UI.WebControls.WebControl.Render(HtmlTextWriter writer) System.Web.UI.Control.RenderControl(HtmlTextWriter writer) System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) System.Web.UI.Control.Render(HtmlTextWriter writer) System.Web.UI.Control.RenderControl(HtmlTextWriter writer) System.Web.UI.Page.ProcessRequestMain() -------------------------------------------------------------------------------- Version Information: Microsoft .NET Framework Version:1.1.4322.2032; ASP.NET Version:1.1.4322.2032
Does anybody know what the hell might be going on here?

Share this post


Link to post
Share on other sites
I also note that by commenting out any of the following lines the program works (or course I don't get a button, and in the last case I get no table). If I comment out any other line it still throws the exception.

ButtonCell.Controls.Add(MyButton)
SystemTable.Rows.Add(ButtonRow)
Me.Page.Controls.Add(SystemTable)

Share this post


Link to post
Share on other sites
Do you know how to use the command window?

If you have a try catch block around the code causing the error, you can step through one line at a time to the place where you catch the error. Then if you have a

catch ex as exception
throw new exception("bad things happened") <------------

Step to that line (after the exception happens). Bring up the command window and type ?ex.message, and you'll get the exact error message, which might shed a little more light on the subject.

Share this post


Link to post
Share on other sites
Ohhhhhhh, wait a minute. How are you calling that function? Since you're trying to add a server side button, the message is telling you that it must be placed within the form tag of your asp page, which it isn't doing. If you use a script tag within your html page where you want that button to appear, you shouldn't get the same error.

Like this:

<form>
.....stuff
<%DrawSingleSystemWithButton%>

.....more stuff

</form>

I don't know if that will work with the rest of your page functionality though...

Share this post


Link to post
Share on other sites
Any web control which causes a postback (Button, LinkButton etc) must be placed inside a <form> tag with runat="server" this caused the buttons to post back correctly.

So essentially what Zul said above but runat server must be set as well.

Share this post


Link to post
Share on other sites
I'm unable to do that with the way I have it working. Can't I do like GenericButton.attributes("RUNAT", "SERVER") or something like that?

I have n number of buttons. The number of buttons is decided by how many items I have in my dataset. This is a selection screen where you hit the button below an entry. I don't know the number of buttons beforehand, so I can't really add them to my page except how I have it. There is no way around this?

Share this post


Link to post
Share on other sites
Its not an issue of your button having runat="server". Its the fact that your button needs to be inside a form with it.

You can encase you entire aspx page in this form. The default page from Visual Studio looks like this.
<body MS_POSITIONING="FlowLayout">
<form id="Form1" method="post" runat="server">
... your content here ....
</form>
</body>

Share this post


Link to post
Share on other sites
This is really odd, because I've done this sort of thing before. The code is the same, except I don't pass the table in. When I had the table as an argument and put the table on my page, it worked just fine (it didn't have a problem with my dynamic buttons at all). When I make the table dynamically and add it dynamically, the button doesn't work (what does this table have to do with the button?). Like I said, if I put a table on my aspx page and pass it into the function, this works just fine. I have several pieces of code where I have a button that isn't part of the page. I don't see how this is any different from my other code (you can see it in my first post) other than I don't pass the table in anymore. Weird.

Share this post


Link to post
Share on other sites
Quote:
Original post by xg0blin
This is really odd, because I've done this sort of thing before. The code is the same, except I don't pass the table in. When I had the table as an argument and put the table on my page, it worked just fine (it didn't have a problem with my dynamic buttons at all). When I make the table dynamically and add it dynamically, the button doesn't work (what does this table have to do with the button?). Like I said, if I put a table on my aspx page and pass it into the function, this works just fine. I have several pieces of code where I have a button that isn't part of the page. I don't see how this is any different from my other code (you can see it in my first post) other than I don't pass the table in anymore. Weird.


This is exactly the problem. When the table is placed on the page, it is already encased within the <form> tag. If you're trying to add it dynamically, Response.Write won't add it within the <form> tag unless your script call is placed within the <form> tag.

Now, what you can do if you don't want to have a table automatically on the page (if it doesn't have any data) or whatever, why not use panels on the page? I use this all the time. Set up the panels on the page the way you want each part to be, and then use your server side code to add tables whereever you need them.

Since I'm pretty curious on the matter, and also would like to use the functionality, I'm going to do a little research on adding page items dynamically to be server side.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zul
Quote:
Original post by xg0blin
This is really odd, because I've done this sort of thing before. The code is the same, except I don't pass the table in. When I had the table as an argument and put the table on my page, it worked just fine (it didn't have a problem with my dynamic buttons at all). When I make the table dynamically and add it dynamically, the button doesn't work (what does this table have to do with the button?). Like I said, if I put a table on my aspx page and pass it into the function, this works just fine. I have several pieces of code where I have a button that isn't part of the page. I don't see how this is any different from my other code (you can see it in my first post) other than I don't pass the table in anymore. Weird.


This is exactly the problem. When the table is placed on the page, it is already encased within the <form> tag. If you're trying to add it dynamically, Response.Write won't add it within the <form> tag unless your script call is placed within the <form> tag.

Now, what you can do if you don't want to have a table automatically on the page (if it doesn't have any data) or whatever, why not use panels on the page? I use this all the time. Set up the panels on the page the way you want each part to be, and then use your server side code to add tables whereever you need them.

Since I'm pretty curious on the matter, and also would like to use the functionality, I'm going to do a little research on adding page items dynamically to be server side.


Ok, I get it now. Duh. Thanks Zul, I wish I had more rating points to give you.

It sucks they made it that way, because I don't want all the hastle of having to add this crap to my page, when it is simpler to just call a function. Oh well I guess.

Share this post


Link to post
Share on other sites
4GuysFrom Rolla has a great article on just this subject. There's an <asp:placeholder> which you can use when you add the control to the page to tell the page where to render the control. That would be even easier than using the panel solution I mentioned.

Share this post


Link to post
Share on other sites
I got a solution, but it gives me a problem as well.

I just make a usercontrol, place it on the page, and then add the controls to it rather than the page. Since it is inbetween the form tags, it works.

The problem? Before I inherited the page class for my class, and then my class in the .aspx page (my class basically was an extension of the page class). I then have a function you override to handle your button command events.

Now I need some way to pass this to my function to use in addhandler, rather than having a handler that you override. Anybody know a good way to go about this?

OnSelect (the function you override) looked like this:


Public Overridable Sub OnSelect(ByVal obj As Object, ByVal e As CommandEventArgs)

End Sub




So basically, you inherit my class and override the function. That is no longer the case. I have my function using the addhandler keyword, they look like this:


Public Sub DrawSingleSystemWithButton(ByVal Position As Integer, ByVal Systems1 As MainSystems, ByVal NestedSystems1 As NestedSystems, ByVal ButtonText As String)
Dim SystemTable As System.Web.UI.WebControls.Table = New System.Web.UI.WebControls.Table
Dim ButtonRow As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow
Dim ButtonCell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell
Dim MyButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button

DrawHeaderRow(SystemTable)
DrawSystem(Position, Systems1, NestedSystems1, SystemTable)

MyButton.Text = ButtonText
MyButton.Width = Unit.Percentage(100)

ButtonCell.ColumnSpan = 2
ButtonCell.Controls.Add(MyButton)

ButtonRow.Cells.Add(ButtonCell)
SystemTable.Rows.Add(ButtonRow)

Me.Controls.Add(SystemTable)

AddHandler MyButton.Command, AddressOf OnSelect
End Sub




I need a way to pass the function I want to use to AddressOf. I tried delegates and all that stuff, but I just can't seem to figure it out.

EDIT: Nevermind, I got it.

[Edited by - xg0blin on October 12, 2004 10:17:09 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Zul
Out of curiousity, was it:

AddHandler MyButton.ServerClick, AddressOf OnSelect

?


No. My code is at work. I'll show it to you tomorrow. Just check this thread in the morning.

Share this post


Link to post
Share on other sites
Ok, here is what I did:

Public Sub DrawSingleSystemWithButton(ByVal Position As Integer, ByVal Systems1 As MainSystems, ByVal NestedSystems1 As NestedSystems, ByVal ButtonText As String, ByVal CommandClicked As System.Web.UI.WebControls.CommandEventHandler)
Dim SystemTable As System.Web.UI.WebControls.Table = New System.Web.UI.WebControls.Table
Dim ButtonRow As System.Web.UI.WebControls.TableRow = New System.Web.UI.WebControls.TableRow
Dim ButtonCell As System.Web.UI.WebControls.TableCell = New System.Web.UI.WebControls.TableCell
Dim MyButton As System.Web.UI.WebControls.Button = New System.Web.UI.WebControls.Button

DrawHeaderRow(SystemTable)
DrawSystem(Position, Systems1, NestedSystems1, SystemTable)

MyButton.Text = ButtonText
MyButton.Width = Unit.Percentage(100)

ButtonCell.ColumnSpan = 2
ButtonCell.Controls.Add(MyButton)

ButtonRow.Cells.Add(ButtonCell)
SystemTable.Rows.Add(ButtonRow)

Me.Controls.Add(SystemTable)

AddHandler MyButton.Command, CommandClicked
End Sub



Notice I pass in a System.Web.UI.WebControls.CommandEventHandler and then that is what gets called in the AddHandler section. Then I make a method (in whatever class I want, it doesn't matter) and pass AddressOf NameOfMethodIWantToUseForHandler as the argument for the function.

Share this post


Link to post
Share on other sites

This topic is 4807 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this