Showing posts with label run. Show all posts
Showing posts with label run. Show all posts

Wednesday, March 28, 2012

Dynamic Controls in ASP.NET AJAX context - Controls seem to be disappearing.

I have a Web Application that I am developing and I have run across aserious problem. Due to the fact that the Application entails a lot ofdetails and code I am not able to give an actual sample of the problemI am running into. However, I have created the same problem on asmaller scale in a temporary Application.

Theissue revolves around ASP.NET AJAX and its ability to track Dynamicallycreated Web Controls. On the sample Application below I have a Pagethat contains 8 Buttons. Whenever one of those Buttons is pressed thePage performs an Async PostBack which causes the Page Load Event toexecute followed by the Event Handler for the Button pressed. In theEvent Handler the Button gets removed from the Page. The desiredeffect is that when someone pressed a Button it will in turn remove theappropriate Web Control (Button) from the Page. Everything works finewith one very big exception: ASP.NET AJAX seems to have a problemkeeping track of the Controls on the page.

For example, if youcopy and paste the code at the bottom of this post into Visual Studioand press the Buttons in the following order you will notice someButtons simply disappearing:
2 - 7 - PostBack - 4 - 1 - PostBack

Onevery important part of this demonstration is that the Button is notonly removed but its also prevented from Rendering. If you take outthis line of code everything works fine but in my actual Applicationthe Control is not Rendered and the results match that of what happensin the temp Application.

So my question is, whats happening inthe background? It seems that removing the Control does not prevent itfrom being Rendered and if you manually prevent it from Rendering bysetting its Visible Property to False then ASP.NET gets completelythrown off as to whats going on.

I know its rather difficult toanswer a question like this because you can't simply provide analternative to the problem so any help is greatly appreciated. I amsimply trying to figure out whats going on in the background so I canfind a solution to my real Application.

Code Behind:

Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

' Clearing any previous text.
label1.text = ""

' If its the first time loading the Page
If Not Page.IsPostBack Then
Page.Session.Add("AddButton1", "True")
Page.Session.Add("AddButton2", "True")
Page.Session.Add("AddButton3", "True")
Page.Session.Add("AddButton4", "True")
Page.Session.Add("AddButton5", "True")
Page.Session.Add("AddButton6", "True")
Page.Session.Add("AddButton7", "True")
Page.Session.Add("AddButton8", "True")

Page.Session.Add("RemovalTracker", "")

' Else the page is performing a PostBack
Else
label1.text = CStr(Page.Session.Item("RemovalTracker"))
End If

'Dynamically creating the Buttons and then adding them to the Page / Form.

If CBool(Page.Session.Item("AddButton1")) = True Then
Dim button1 As New Button
button1.Text = "Button1"
button1.ID = "Button1"
AddHandler button1.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button1)
End If

If CBool(Page.Session.Item("AddButton2")) = True Then
Dim button2 As New Button
button2.Text = "Button2"
button2.ID = "Button2"
AddHandler button2.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button2)
End If

If CBool(Page.Session.Item("AddButton3")) = True Then
Dim button3 As New Button
button3.Text = "Button3"
button3.ID = "Button3"
AddHandler button3.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button3)
End If

If CBool(Page.Session.Item("AddButton4")) = True Then
Dim button4 As New Button
button4.Text = "Button4"
button4.ID = "Button4"
AddHandler button4.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button4)
End If

If CBool(Page.Session.Item("AddButton5")) = True Then
Dim button5 As New Button
button5.Text = "Button5"
button5.ID = "Button5"
AddHandler button5.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button5)
End If

If CBool(Page.Session.Item("AddButton6")) = True Then
Dim button6 As New Button
button6.Text = "Button6"
button6.ID = "Button6"
AddHandler button6.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button6)
End If

If CBool(Page.Session.Item("AddButton7")) = True Then
Dim button7 As New Button
button7.Text = "Button7"
button7.ID = "Button7"
AddHandler button7.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button7)
End If

If CBool(Page.Session.Item("AddButton8")) = True Then
Dim button8 As New Button
button8.Text = "Button8"
button8.ID = "Button8"
AddHandler button8.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button8)
End If

End Sub

Protected Sub testSub(ByVal sender As Object, ByVal e As System.EventArgs)

' Appending the Button Text to SessionState.
Page.Session.Item("RemovalTracker") = Page.Session.Item("RemovalTracker") & " - " & CType(sender, Button).Text

' Building the ID for the Session Item that needs to be modified.
Dim buttonToRemove As String
buttonToRemove = "Add" & CType(sender, Button).ID
Session.Item(buttonToRemove) = "False"

' Preventing the Buttom from Rendering - this is CRUCIAL to my demonstration.
CType(sender, Button).Visible = False

Page.Form.Controls.Remove(CType(sender, Button))

End Sub

End Class

Default.aspx Page:
<%@dotnet.itags.org. Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body
<form id="form1" runat="server">

<asp:ScriptManager ID="ScriptManager1" runat="server" />

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
<br />
<br />
<br />
<asp:Button ID="ButtonPostBack" runat="server" Text="PostBack" />
<br />
<br />
</ContentTemplate>
</asp:UpdatePanel
</form>

</body>
</html>

After 3+ days of brainstorming, testing, debugging and everything I could throw at ASP.NET I have found a solution.

Theproblem lies not within ViewState, not with ASP.NET's Control Tree (nottechnically), not within the Code itself but in how ASP.NET handlesdynamic Controls in regards to their ID's.

If I modify my codeabove as demonstrated below everything works just fine. You wouldthink that incrementing the ID's instead of assinging the same ID's tothe same Controls would do nothing but for some bizarre reason thatshow it works.

My problem in my real Application is very similiarto this problem and I have in fact solved my problem. Its funny how ifI had been able to pin-point this problem several days ago I would beway beyond this. Oh well, thats ASP.NET.

ASPX Page:
<%@. Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body
<form id="form1" runat="server">

<asp:ScriptManager ID="ScriptManager1" runat="server" />

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
<br />
<br />
<br />
<asp:Button ID="ButtonPostBack" runat="server" Text="PostBack" />
<br />
<br />
</ContentTemplate>
</asp:UpdatePanel
</form>

</body>
</html
Code Behind:
Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

' Clearing any previous text.
label1.text = ""

Dim counter As Integer = 0

' If its the first time loading the Page
If Not Page.IsPostBack Then
Page.Session.Add("AddButton1", "True")
Page.Session.Add("AddButton2", "True")
Page.Session.Add("AddButton3", "True")
Page.Session.Add("AddButton4", "True")
Page.Session.Add("AddButton5", "True")
Page.Session.Add("AddButton6", "True")
Page.Session.Add("AddButton7", "True")
Page.Session.Add("AddButton8", "True")

Page.Session.Add("RemovalTracker", "")

' Else the page is performing a PostBack
Else
label1.text = CStr(Page.Session.Item("RemovalTracker")

)
End If

'Dynamically creating the Buttons and then adding them to the Page / Form.

If CBool(Page.Session.Item("AddButton1")) = True Then
Dim button1 As New Button
button1.Text = "Button1"
button1.ID = counter.ToString
counter += 1
AddHandler button1.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button1)
End If

If CBool(Page.Session.Item("AddButton2")) = True Then
Dim button2 As New Button
button2.Text = "Button2"
button2.ID = counter.ToString
counter += 1
AddHandler button2.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button2)
End If

If CBool(Page.Session.Item("AddButton3")) = True Then
Dim button3 As New Button
button3.Text = "Button3"
button3.ID = counter.ToString
counter += 1
AddHandler button3.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button3)
End If

If CBool(Page.Session.Item("AddButton4")) = True Then
Dim button4 As New Button
button4.Text = "Button4"
button4.ID = counter.ToString
counter += 1
AddHandler button4.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button4)
End If

If CBool(Page.Session.Item("AddButton5")) = True Then
Dim button5 As New Button
button5.Text = "Button5"
button5.ID = counter.ToString
counter += 1
AddHandler button5.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button5)
End If

If CBool(Page.Session.Item("AddButton6")) = True Then
Dim button6 As New Button
button6.Text = "Button6"
button6.ID = counter.ToString
counter += 1
AddHandler button6.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button6)
End If

If CBool(Page.Session.Item("AddButton7")) = True Then
Dim button7 As New Button
button7.Text = "Button7"
button7.ID = counter.ToString
counter += 1
AddHandler button7.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button7)
End If

If CBool(Page.Session.Item("AddButton8")) = True Then
Dim button8 As New Button
button8.Text = "Button8"
button8.ID = counter.ToString
counter += 1
AddHandler button8.Click, AddressOf testSub
UpdatePanel1.ContentTemplateContainer.Controls.Add(button8)
End If

End Sub

Protected Sub testSub(ByVal sender As Object, ByVal e As System.EventArgs)

' Appending the Button Text to SessionState.
Page.Session.Item("RemovalTracker") = Page.Session.Item("RemovalTracker") & " - " & CType(sender, Button).Text

' Building the ID for the Session Item that needs to be modified.
Dim buttonToRemove As String
buttonToRemove = "Add" & CType(sender, Button).ID
Session.Item(buttonToRemove) = "False"

' Preventing the Buttom from Rendering - this is CRUCIAL to my demonstration.
CType(sender, Button).Visible = False

Page.Form.Controls.Remove(CType(sender, Button))

End Sub

Dynamic Ajax Tab Container raises unhandled exception: Specified argument was out of the r

Hi -

I've a problem with the ajax tab container: I try to add additional ajax tab panels dynamically at run time. This works fine, but as soon as I do it, any button I click on the page throws the exception below.

So, if I comment these 2 lines out:

Dim tpAsNew AjaxControlToolkit.TabPanel

Me.TCSpecialties.Tabs.Add(tp)

everything works fine (TCSpecialties is a Tab Container). Once I leave them in, this exception is thrown:

Specified argument was out of the range of valid values.
Parameter name: value

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.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: value

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:

[ArgumentOutOfRangeException: Specified argument was out of the range of valid values.Parameter name: value] AjaxControlToolkit.TabContainer.set_ActiveTabIndex(Int32 value) +381 AjaxControlToolkit.TabContainer.LoadClientState(String clientState) +160 AjaxControlToolkit.ScriptControlBase.LoadPostData(String postDataKey, NameValueCollection postCollection) +73 AjaxControlToolkit.TabContainer.LoadPostData(String postDataKey, NameValueCollection postCollection) +32 AjaxControlToolkit.ScriptControlBase.System.Web.UI.IPostBackDataHandler.LoadPostData(String postDataKey, NameValueCollection postCollection) +11 System.Web.UI.Page.ProcessPostData(NameValueCollection postData, Boolean fBeforeLoad) +718 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3776

Does anyone have a clue as to what may be going on??

Any help highly appreciated

I think you need to set the activetab which is null

somthing like TCSpecialties.ActiveTab = tp

This will set the active tab to the newly added tab.


I did that - still the same behavior as before. The tabs get displayed correctly, as a matter of fact. Any ideas?


By "adding tabs dynamically at runtime" are you doing this client side or server side? Can you file a bug at codeplex at attach a simple repro?


I am doing this on the server side ... what I meant is that the controls are not dropped in the form at designtime but rather created based on database fields (one Tab per record, with 3 records in the database). Will try to create a repro


You may need to recreate the tab panels when the postback occurs in the Page_init because the tabpanels do not exist on post backs until they have been made...

So in Page_init you should call a function that makes the default tab collection and then later on the page you can add a new tab panel to the collection.

Hope this makes sense.


That fixed itSmile

Thanks very much!!!!

Oliver


Thanks guys for the thread. It has saved us HEAPS of time.

After reading it though I still had a few problems getting it to work (which it is now).
These are probably pretty obvious to most of you out there, but it may be helpful for those reading this thread that still can't get it working.

We are now creating the tabbed panels dynamically in Page_Init as instructed.
One thing we did wrong was to only do this if it was not a postback. In hindsight we obviously we needed to do this all the time.

Also for those who are wondering how to save the selected values on the page if you are recreating the tab panels everytime, I think that the save view state and load view state events are doing this for you automatically; I think they just need to have the controls created (which you are now doing in page_init) and then they will do the rest.

Isn't life grand :)

Thanks again for sharing this info with everyone.

Mark


Can you please post a code example here of what must be written inside the page_init function ?


Just create whatever dynamic controls you have. If you add a text box with the ID="tb1" dynamically (I know, it's more likely you created something data driven), just add the following

dim l as new textbox

l.id="tb1"

me.page.controls.add(l)

That's it. Just do this for any controls you added. Note: Do not reset the values, viewstate will take care of that for you.

Now, you can simply access the text property somewhere else in your code behind as

l.text

and it will have the text the user entered. Hope that helps


Hello,

I am doing it according to this post but still getting "Index out of range" exception when I try to add a second TabPanel by clicking a button...

Any ideas?...

public partial class TestPage : System.Web.UI.Page
{
private TabContainer _tabContainer;

protected void Page_Init(object sender, EventArgs e)
{
_tabContainer = new TabContainer();
_tabContainer.Height = Unit.Pixel(250);

TabPanel tab = new TabPanel();
tab.HeaderText = "New Tab 1";

_tabContainer.Tabs.Add(tab);

tab = new TabPanel();
tab.HeaderText = "New Tab 2";

_tabContainer.Tabs.Add(tab);
_tabContainer.ActiveTab = tab;

ContainerPanel.Controls.Add(_tabContainer);
}
protected void Button1_AddTab(object sender, EventArgs e)
{
TabPanel tab = new TabPanel();
tab.HeaderText = "New Tab 3";

_tabContainer.Tabs.Add(tab);
_tabContainer.ActiveTab = tab;
}
}


Hey can you clarify the sentence:

"

So in Page_init you should call a function that makes the default tab collection and then later on the page you can add a new tab panel to the collection."

What's a default tab collection? I'm having the same problem and I don't really understand the solution.

Thanks


I can't really clarify better than I did before... Just look at the code I posted and you'll understand...

Thanks,

David


Can some please explain what is the reason behind this error?

Is it a feature or a bug?

I thank all for the answers, but eventually they either unclear or imply that if I want to add tabs dynamically then I have to create all the other controls on page dynamically (which seems to me somehow unpractical if I have a control rich page).

Any ideas?


This is what I found as a solution to this mess.

Assume you have a TabContainer with a TabPanel at design time. You also have a button which when clicked adds a new TabPanel to the TabContainer at runtime.

On postback, every TabPanel already added dynamically must be recreated before the Button_Click and the Page_Load are processed. In other words, this applies to all TabPanels except for the first one. This can be done in the Page_Init handler which I do not recommend for a very good reason: As soon as you mess with it you have to recreateall other dynamic controls your page may contain!

On the other hand, the TabContainer control has an Init handler itself so why not use it insteadWink

The following example adds a new TabPanel every time the button is clicked. Each tab has a header "Tab X" where X = 1 ... tabIndexName. You also need the tabIndexName static member to keep count of how many TabPanels have been created so far. Last but not least, have in mind that you cannot use the ViewState object inside the Init method.

public partialclass TabContainerTest : System.Web.UI.Page
{
public static int tabIndexName;

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
tabIndexName = 2;
}
}

protected void Button1_Click(object sender, EventArgs e)
{
AjaxControlToolkit.TabPanel tab =new AjaxControlToolkit.TabPanel();
tab.HeaderText ="Tab " + Convert.ToString(tabIndexName++);
TabContainer1.Tabs.Add(tab);
}

protected void TabContainer1_Init(object sender, EventArgs e)
{
if (IsPostBack)
{
for (int i = 1; i < tabIndexName - 1; i++)
{
AjaxControlToolkit.TabPanel tab =new AjaxControlToolkit.TabPanel();
tab.HeaderText ="Tab " + Convert.ToString(i + 1);
TabContainer1.Tabs.Add(tab);
}
}
}
}

Hope this helps.

Monday, March 26, 2012

DropShadowExtender not attached to its panel

HELP PLEASE!!! I have put a DropShadowExtender on my login panel but when I run it, the dropshadow is completely detached from the panel itself! It is the correct size, shape, opacity, but the shadow is appearing a few inches down and to the right of the panel.

I've been banging on this for a WEEK and my head's about to explode.

1<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
2 <asp:ScriptManager ID="sm" runat="server" EnablePartialRendering="true" />
3 <asp:Panel ID="pnlLogin" runat="server" CssClass="LoginShadow">
4 <div style="padding:10px ; z-index:115">
5 User Name: <asp:TextBox ID="UserName" runat="server"></asp:TextBox><br />
6 Password: <asp:TextBox ID="Password" runat="server"></asp:TextBox><br />
7 <hr />
8
9 <asp:Button ID="LoginButton" runat="server" BackColor="#FFFBFF" BorderColor="#CCCCCC"10 BorderStyle="Solid" BorderWidth="1px" CommandName="Login" Font-Names="Verdana"11 Font-Size="0.8em" ForeColor="#284775" Text="Log In" ValidationGroup="Login"
12 OnClick="LoginButton2_Click">
13 </asp:Button>
14 </div>
15 </asp:Panel>
16 <ajaxToolkit:DropShadowExtender ID="dse1" runat="server"17 TargetControlID="pnlLogin"
18 Width="5"19 Rounded="true"20 Radius="6"21 Opacity=".7"22 TrackPosition="true"></ajaxToolkit:DropShadowExtender>
23</asp:Content>
What happens if you remove the HR tag? (Which forces line breaks...)

Saturday, March 24, 2012

DropDownList, UpdatePanel, ControlEventTrigger and TimerControl affecting other UpdatePane

I've run across a problem so I've created some sample code below that replicates what I'm seeing.

I've got an UpdatePanel(up_Test) that has a control in it. I've got a TimerControl that fires every 2 seconds and my UpdatePanel has a Trigger to refresh on my TimerControl's Tick event.

I've got a DropDownList on the page(ddl_Test), with an AutoPostBack="true", I've got another UpdatePanel on the page that has a Trigger to refresh on my DropDownList's SelectedIndexChanged event. In this UpdatePanel I've got another DropDownList that gets its SelectedIndex set to the SelectedIndex of the first DropDownList.

Everything works as expected, except when I test it with IE6, when the TimerControl fires, the DropDownList in the second UpdatePanel gets refreshed. It doesn't seem to happen(or at least isn't noticable) with IE7(which is on the machine I'd been writing this on).

Basically, I wrote this big page that has a ton of different updatepanel's on it each with triggers to specific controls and that whole section worked great, but then on my masterpage, when I put a gridview with a timercontrol, suddenly, the page practically became unusable as all of the updatepanels suddenly trigger when the TimerControl fires. Is there some work-around I can do, or do I just need to drop the TimerControl and scrap the whole layout?

<

atlas:TimerControlID="tc_Test"runat="server"Interval="2000"Enabled="true"OnTick="tc_Test_Tick"/><atlas:UpdatePanelID="up_Test"runat="server"><ContentTemplate><asp:DropDownListID="ddl_Test"runat="server"></asp:DropDownList></ContentTemplate><Triggers><atlas:ControlEventTriggerControlID="tc_Test"EventName="Tick"/></Triggers></atlas:UpdatePanel><asp:DropDownListID="ddl_Test1"runat="server"AutoPostBack="true"OnSelectedIndexChanged="ddl_Test1_SelectedIndexChanged"/><atlas:UpdatePanelID="up_Test2"runat="server"><ContentTemplate><asp:DropDownListID="ddl_Test2"runat="server"></asp:DropDownList></ContentTemplate><Triggers><atlas:ControlEventTriggerControlID="ddl_Test1"EventName="SelectedIndexChanged"/></Triggers></atlas:UpdatePanel>

protected

void Page_Load(object sender,EventArgs e)

{

if (!Page.IsPostBack)

{

for (int i = 0; i < 10; i++)

{

string itemtext ="This is a long string to make it noticable " + i.ToString();

ddl_Test.Items.Add(itemtext);

ddl_Test1.Items.Add(itemtext);

ddl_Test2.Items.Add(itemtext);

}

}

}

protectedvoid ddl_Test1_SelectedIndexChanged(object sender,EventArgs e)

{

ddl_Test2.SelectedIndex = ddl_Test1.SelectedIndex;

}

protectedvoid tc_Test_Tick(object sender,EventArgs e)

{

Random r=newRandom();

ddl_Test.SelectedIndex = r.Next(9);

}

Mode="Conditional" on the update panels fixes the problem. I guess I just assumed putting in the <trigger> lines caused it to use the triggers but you have to change the updatepanel's mode too.