Maintaining usercontrol state on dynamically added usercontrols - c#

I have a page that dynamically creates multiple usercontrols on the page_init event, and adds it to a placeholder on the page.
The usercontrols themselves databind to a repeater on page_init to a collection of about 10 strings, which outputs a div for each item.
There's also a "view more" link button on the user control. When I click the "view more" button it databinds another collection to a second repeater, with even more divs.
The problem: After clicking "view more" on one of the usercontrols, if I click "view more" on another usercontrol, the "view more" data is lost on the first usercontrol. I suspect it's because I'm not re-adding the controls, so viewstate isn't re-loaded.
Anyone have any ideas or am I just way off on this one? Thank you.

Problem is you need to re-create the dynamic controls on each postback and recreate their viewstate. Take a look at this article Dynamic Web Controls, Postbacks, and View State

Stan is right.
When you click in the link a postback occurs and you lost everything
I ran across the same problem, my aproach was recreate the dinamics UserControls on every postback.
this article http://www.codeproject.com/KB/user-controls/DynamicUC.aspx shows a example, but i implement a diferent code like this:
my page have the following method which dinammicaly add the controls to an PlaceHolder.
private void AdicionarControlesDinamicamente(int idPergunta)
{
if (idPergunta > 0)
{
this.IdPerguntaAtual = idPergunta;
PerguntaAtual = new Pergunta(this.IdPerguntaAtual);
UserControl uc = LoadControl(PerguntaAtual.TipoResposta.CaminhoUserControl, PerguntaAtual.IdPergunta);
phResposta.Controls.Add(uc);
ViewState["ControlesDinamicosPerguntaCarregados"] = true;
}
}
note this line of code ViewState["ControlesDinamicosPerguntaCarregados"] = true;
i store an information tha says that the controls already have been added to page.
then a ovveride the CreateChildControls to recreate the controls
protected override void CreateChildControls()
{
base.CreateChildControls();
// CHeck if the controls have been added to page, case true, i call IncluirControlesDinamicamente() again
// The Asp.Net will look into viewstate and wil find my controls there, so "he" will recreate their for me
if (ViewState["ControlesDinamicosPerguntaCarregados"] != null)
if (Page.IsPostBack)
AdicionarControlesDinamicamente(this.IdPerguntaAtual);
}
I think this help you.
PS: Sorry my english.

Related

Dynamic Tab Pages cannot be found within Tab Control

Good afternoon my fellow propeller heads; I come to you with a very strange issue today.
In my C# Winforms application, I have a form with a Tab Control. The Tab Control has 4 Tab Pages which I added during design. When my application runs, I am adding x amount of Tab Pages dynamically.
My issue lies not with any of the Tab Pages I added at design time, but only with the dynamic Tab Pages. The dynamic Tab Pages have buttons, which fire fine when clicked. However, when I iterate through the Tab Control’s Tab Pages, only the static Tab Pages are present, none of the dynamic pages are in the control.
I have a class that creates my Tab Pages and it all works fine and adds the Tab Pages as it should. The class name is TabTree and the method name is Add_Tree_View_Tab. "test tab" is the text being displayed on the tab. The null parameter is irrelevant at this time as it is input to another control docked on the Tab Page.
tabRecords.TabPages.Add(TabTree.Add_Tree_View_Tab("test tab", null));
I have also added just a Tab Page to see if I could find it within the Tab Control, but still no luck.
tabRecords.TabPages.Add("this page", "this page");
Trying to find either of my dynamic Tab Pages yield no result?
internal void Expand_Treeview(object sender, EventArgs e)
{
try
{
Button button = (Button)sender;
foreach (TabPage tab_page in tabRecords.TabPages)
{
if (tab_page.Name == "tab_" + button.Tag.ToString())
{
foreach (Control control in tab_page.Controls)
{
if (control is TreeView && control.Name == "treeview_" + button.Tag.ToString())
{
TreeView Tree_view = (TreeView)control;
}
}
}
}
}
catch (Exception ex)
{ }
}
What am I doing wrong or what am I missing? I have spent a good couple of ours trying to figure this out with no solution as yet…
Thanks for the help from everyone, you definitely put me on the right track. It was a case of different instances and destroyed objects.
There are two classes that create the dynamic tabs. The first class is instantiated, but also inherits from the second class. Also, the second class inherits from the actual form, in order to be able to add the dynamic buttons event handler that exist on the tab page. The tabs are created but as soon as the first instantiated class is destroyed, all reference to the dynamic tabs are lost. Which makes perfect sense. I didn't pick up on it as it is code I inherited.
Once again thanks for everyone's input to get me sorted.

Response.Redirect will Lost ViewState?

Suppose, on Page_Load() of a page (WebForms) that I create this Control :
HtmlGenericControl optionBox = new HtmlGenericControl("div");
optionBox.Attributes["class"] = "class_1";
Than, a use will recall the page using a LinkButton. On the method called from this button, I change the class of my previous div :
protected void cmdCerca_Click(object sender, EventArgs e)
{
...
div.Attributes.Add("class", "class_2");
...
}
Well, if I watch on the rendered result, I'll see that the class of the div have been changed.
This means that, in the next call to this page (from this context, example calling cmdCerca_2_Click), that object will be recovered from the View, getting class_2, not class_1.
But, this doesnt happens if, at the end of cmdCerca_Click, I call the same page with Response.Redirect(). Seems that the View will be lost.
Why? And how can I fix it?
Hope the question is clear.
You need to add the controls in the page init event, rather than load, in order to get them into the control tree.
You must be recreating this control on every postback? In this case, your default class will be set every time.

Changing a ascx content from different code-behind

Say I have tvo pages, page1.ascx and page2.ascx. Both pages have code-behind(page1.ascx.cs and page2.ascx.cs respectively).
So page1 and page2 are rendered at the same time in the browser, side by side.
Now page1.ascx has a ListView and its code-behind has a method to populate it(PopulateListbox()). How can I call PopulateListbox() from the page2.ascx code-behind?
page1 p1 = new page1();
p1.PopulateListbox();
...does not work, and findController to find the ID of the listbox returns a null value.
Any guidance would be of great help, thanks.
var p1 = this.Page.FindControl("page1Id") as page1;
if (p1 != null)
p1.PopulateListbox();
You can do this in a different way though. Create an event on the first control for a specific action. In the parent page add an event handler and that event handler will contain the following call
p1.PopulateListbox();
Here a link for how to create your own events
Accessing one user control method directly from another user control doesn't sound like a great design.
What you could do is create a Delegate in your page2.ascx that gets called when your refresh action in the other user control needs to happen.
Your aspx page subscribes to that delegate and makes the call to the page1.ascx PopulateListBox method.
So, your page orchestrates the interaction between both user controls and they don't know about each other.

ASP.NET Modifying Control Tree Dynamically

I am trying to drop and add controls dynamically to my control tree. For example:
Under a specific condition, I am calling:
private void resetPanel()
{
Panel.Controls.Clear();
Panel.Controls.Add(Image);
Panel.Controls.Add(HiddenField);
}
My main objective is how do I get the added controls to persist across postbacks?
When I call another similar function using textboxes and titles, it persists perfectly. However, with the image it loses its URL and properties.
I understand that for dynamic controls to persist, you must add it on the Init, and you must be responsible for the control tree thus needing to add the dynamic control to the tree on every postback.
So why does it work for textboxes and labels persisting across post backs but you cannot do the control add for images and hiddenfields?
Thanks,
Brian
--Update and Solution--
I have found a mistake in my code, and the HiddenField values do persist across post backs. The solution I have opted for is to use the ViewState to save the values, then restore my dynamic controls on each post back.
--Edit--
Thank you for the replies, and since there may be a better solution to my problem, here is some code that will hopefully show how I am calling the method and why I would need to.
public void resetTitlePanel()
{
// Restylize the panel to initial state
TitlePanel.Controls.Clear();
TitlePanel.BorderColor = System.Drawing.Color.Maroon;
TitlePanel.BorderStyle = BorderStyle.Dashed;
TitlePanel.Enabled = true;
// Set the new control properties to initial state
Label TitleLabel = new Label();
TitleLabel.ID = "TitleLabel";
TextBox TitleTxtBox = new TextBox();
TitleTxtBox.ID = "TitleTxtBox";
// Add the new controls to the container
TitlePanel.Controls.Add(TitleLabel);
TitlePanel.Controls.Add(TitleTxtBox);
// Set the reference of this to the new dynamic control
this.TitleLabel = TitleLabel;
this.TitleTxtBox = TitleTxtBox;
}
public void resetImagePanel()
{
// Restylize the panel to initial state
ImagePanel.Controls.Clear();
ImagePanel.BorderColor = System.Drawing.Color.Blue;
ImagePanel.BorderStyle = BorderStyle.Dashed;
ImagePanel.HorizontalAlign = HorizontalAlign.NotSet;
// Set the new control properties to initial state
Image AddImage = new Image();
AddImage.ImageUrl = "~/Resources/Icons/picture_add.png";
AddImage.ID = "AddImage";
HiddenField HiddenImage = new HiddenField();
HiddenImage.ID = "HiddenImage";
// Add the new controls to the container
ImagePanel.Controls.Add(AddImage);
ImagePanel.Controls.Add(HiddenImage);
// Set the reference of this to the new dynamic control
this.AddImage = AddImage;
this.HiddenImage = HiddenImage;
}
The Calling Method:
private void copyFromSlide(TemplateControl destination, Template source)
{
// Reset the template
destination.resetTitlePanel();
destination.resetImagePanel();
destination.Title = source.Title;
// Find the path from the database and assign it to the control
destination.ImagePath = modData.getImagePath((int)source.ImageID);
}
So... I understand that the code is complex, perhaps more than it should be. Further, I am just a beginner so it may be of worse quality, and I apologize for that.
Key notes are:
There are 2 user controls that are interacting with each other.
This works completely fine on !IsPostback.
The ViewStateEnable is true on default, even if I assign it true explicitly, I get the same results.
This works completely for the title panel which consists of a label and textbox, both of which retains its value.
I know I am mixing static and dynamic controls together. I am used to C, so I am unsure if I could just move the object pointer to the new dynamic object.
The problem is, when assigning the image path, the value does not retain on postback.
I need to drop and re-add controls because under specific conditions I will drop the controls and add labels, which as noted, have no problem. The reason why I believe that I do not need to initialize the controls over again is because I am adding to a rooted panel as demonstrated by:
http://weblogs.asp.net/infinitiesloop/archive/2006/08/30/TRULY-Understanding-Dynamic-Controls-_2800_Part-3_2900_.aspx
I hope this adds some clarity.
Thanks once again,
-Brian
ViewState does not track changes until the InitComplete event fires. If you make changes and store them in ViewState before then (for example in Init) these changes will not generate a new viewstate key. Init should be used to reconstruct your control from viewstate. Events after the Init event is where you should set new values for these controls in view state so that they will be persisted back.
In response to your second question: The Image and HiddenField controls do not respond to any events (e.g. click event) and therefore do not need to be reconstructed. This is why you are seeing the different behavoir between the TextBox and Label controls vs the Image and HiddenField controls.
It may be easier to store the data that determines when to add and remove those controls in the page view state and just recreate the controls on every page load.
Have you enabled ViewState on these controls?
There are some things in the Remarks section of this document you may want to check out here
What you need to do is to always have all your controls added to the page (or user control) before OnLoad() happens (typically on OnInit()).
This is because ASP.NET loads control values from ViewState or Request.Form on OnLoad().
In fact between postbacks none of the "control"s are persisted, it's only control values which are preserved between postbacks either in ViewState or Request.Form. (No matter if they are added in Markup or Dynamically) as I said above, the only thing that is important is that the control is added to the page before OnLoad().
I should add even if you add your element after OnLoad you still can read control's value (in postback) like this:
Request.Form[txtName.UniqueID]
for more information about controls life cycle in ASP.NET see here;
http://msdn.microsoft.com/en-us/library/ms178472.aspx

How do I prevent my treeview from collapsing?

I am using ASP.NET with C# 2.0 and Visual Studio 2005. I am using a Master page and content pages. I have a treeview menu in the master page and when a user selects any menu item I redirect to that content page.
My problem is that after a user navigates to the content page all the treenodes refresh and the structure is collapsed. I want the selected treenode to stay expanded.
Can anybody help me out?
When you refresh the treeview you want to call treeView1.ExpandAll();
Also add an event for the BeforeCollapse and set the event's Cancel property to true, to prevent the user from collapsing your treenodes.
private void treeView1_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
{
e.Cancel = true;
}
Hope this helps.
-jeremy
This is a common enough problem that is automatically handled by ASP.NET if you use a SiteMapDataSource control as the datasource for your TreeView. In this case, you haven't mentioned what the Datasource of your TreeView is.
You also haven't mentioned if the TreeView contains links (the NavigateUrl property is set) or Text items that postback for each click. If it is the former, then as far as I know, you are out of luck! This is because none of the Selection events are raised for TreeNodes which have their NavigateUrl set. They just function as regular hyperlinks.
If however, it is the latter, then you can try out the following steps :
a. Handle the SelectedNodeChanged event of the TreeView. In this event handler, retrieve the current value of the SelectedNode.ValuePath property and store it in ViewState/Session. Use the Value of the of the SelectedNode to conditionally redirect the page to URL mapped to it.
Something like the following:
protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
TreeNode selNode = TreeView1.SelectedNode;
string pathToNode = selNode.ValuePath;
Session.Add("SelPath", pathToNode);
switch (selNode.Value)
{
//Redirect to URL accordingly.
}
}
b. On subsequent load of the Master page (the page to which you redirected), retrieve the value of the ValuePath set earlier and find the previously Selected node and Expand it.
Something like the following:
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
string pathToNode = (string)Session("SelPath");
Session.Remove("SelPath");
TreeNode selNode = TreeView1.FindNode(pathToNode);
if (selNode != null)
{
selNode.Expand();
}
}
}
Note that I haven't had an opportunity to test the code so this is mostly hypothetical.
Try using the OnTreeNodeDataBound event and the treeView.SelectedNode property
Also, might want to check how/ when you're binding your TreeView to it's DataSource. You might be rebinding it on IsPostBack which will re-render the tree.
The TreeView should maintain its nodes on PostBack.
Even though you are using a Master page, once the user navigates to the content page it is rendered as a new/different page. Because of the Master page the same treeview is loaded but not the same instance. You will need to store and load what nodes were expanded.

Categories