ASP.NET Modifying Control Tree Dynamically - c#

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

Related

RadAjaxManager in a user control with at most one such manager per page

It is a well known error if you try to add the RadAjaxManager to your page twice:
Only one instance of a RadAjaxManager can be added to the page
Telerik explains how you can solve this for design-time issues with a proxy control.
For most of the controls we use on pages, this error does not fire, even though each of these controls have a RadAjaxManager on them, sometimes even inside a repeater (accidentally, but still, the error doesn't throw). However, with one such control (a dynamic button) we have added it to several places on the page with no problem, possibly because this was all the same control, but nested in another control we receive the error above again, as soon as we add it to the page.
I have tried to solve it by:
adding the control dynamically to the page, but because control events fire before the page events, this leads to some dynamic behavior to not occur anymore.
adding the RadAjaxManager dynamically only once to the control, with built-in extra checks, like so:
private RadAjaxManager GetAjaxManager()
{
var ctl = this.FindControl("ajaxManager");
if (ctl != null)
{
return (RadAjaxManager)ctl;
}
// alternative method
var mgr = RadAjaxManager.GetCurrent(this.Page);
if (mgr != null)
{
return mgr;
}
// control is never found, always returns null
return null;
}
protected override void OnInit(EventArgs e)
{
if (this.GetAjaxManager() == null)
{
// ajax mgr is never found, and this always throws
// "cannot add multiple times" error
var ajaxManager = new RadAjaxManager();
ajaxManager.ID = "ajaxManager";
this.Controls.Add(ajaxManager);
}
}
Several variants of the above
The result is either: the control is never found and is therefore added more than once, resulting in the above error, or the control is added but too late in the process for the other controls in the usercontrol, resulting in several AJAX events not happening.
How should I add the RadAjaxManager to a user-control that is itself used inside several other user-controls, such that the manager only occurs once on the page and/or such that RadAjaxManager.GetCurrent actually works?
Plane A - add the RadAjaxManager to the page level, not to the user controls. Thus, the user controls can have RadAjaxManagerProxy controls, the static GetCurrent() method will work.
Plan B - use RadAjaxPanel controls if you want self-contained user controls. They have an ajaxRequest() client-side method and a server side event for that, and since user controls are usually smallish, you will likely be able to get away with a single panel for them.
Plan C - leave AJAX setup to the parent page. If a parent user control is already AJAX-enabled, its entire content will travel with the postback, so neesting more AJAX settings on inner user controls may not bring you a performance benefit.
Plan D - use only asp:UpdatePanel controls with UpdateMode=Conditional so you will have extremely fine-grained control of your partial rendering.

Is it possible to add more than one panel in same location

i'm using telerik control.
So i want to ask,
In winforms application ,Is it possible to add more than one panel in same location and display one at a time just like show/hide property.
Make sure you have placed all panel control in same container or form. then you can use Visible property to show and hide panel. BringFront and SendToBack function will be used to bring panel on top or send it to back. If you have placed any panel in another panel then that will be disappeared when you Hide parent panel. So, Make sure all panels' parent control must be same. To determine the parent control simply select that panel and press escape key to select their parent.
private void LoadPanels()
{
panel1.Location = new Point(10,10);
panel2.Location = new Point(10,10);
panel3.Location = new Point(10,10);
panel4.Location = new Point(10,10);
panel5.Location = new Point(10,10);
VisiblePanel("panel1");
}
private void VisiblePanel(string panelName)
{
string[] panels = new string[]{"panel1","panel2","panel3","panel4","panel5"};
for (int i=0;i<panels.Length;i++)
this.Controls[panels[i]].Visible = (panels[i] == panelName);
this.Controls[panelName].BringToFront(); //Not required you can remove this line.
}
Here's a slightly different approach you might want to consider...
Are you wanting to be able to programmatically select the contents of a rectangular area at runtime, selecting among various controls to display? If so, you could use a custom TabControl which has its tabs (not the pages) hidden.
Then you can select which page is displayed by programmatically changing its SelectedIndex property at runtime.
Doing it like this means that your form editor will show a normal tab control, which allows you to much more easily add the content to each page - but at runtime the tabs will be hidden from the user; they will just see the contents of the currently selected page.
See Hans Passant's answer here for how to create such a custom tab control.
(However, you might also want to override the OnKeyDown for the custom tab control in order to ignore Ctrl-Tab.)

C# Using a form to load other user controls and have access to a base control or property

Currently I have a C# program with a windows form and then a user control template put onto the form. The user control template is really just used as a placeholder. I have a series of other controls which inherit from this user control template.
Each of those controls have navigation buttons like 'Continue' and 'Back' on them and each control knows which control needs to be loaded next. However what I need to figure out is an easier way to have variables that are global to these controls.
The only workaround I have is that I pass the form to each control when they are loaded and use variables inside of the form to read and write to. What would be the proper way to have each of these user control screens be built off of a base control which contained objects all of the controls could get to?
Sorry for the rambling nature of the post but I've been thinking about this problem all morning.
Here is some of the code:
Most of what I have written was based on hiding and showing the user controls so that content in the controls wouldn't be lost during navigation. I won't be needing to do that as eventually it will be loading the fields of data from a database.
Code for initially loading control from form click:
conTemplate1.Controls.Clear();
conInbound Inbound = new conInbound(this);
Inbound.Dock = DockStyle.Fill;
Inbound.Anchor = (AnchorStyles.Left | AnchorStyles.Top);
conTemplate1.Controls.Add(Inbound);
Code for Continue button inside of one of the controls:
if ((Parent.Controls.Count - 1) <= Parent.Controls.IndexOf(this))
{
UserControl nextControl = new conPartialClear();
nextControl.Dock = DockStyle.Fill;
Parent.Controls.Add(nextControl);
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
else
{
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
The best-practice for communicating from a control to a parent is to use events, and for communicating from a parent to a control is to call methods.
However, if you don't want to or can't follow this practice, here's what I would recommend.
Each UserControl has a ParentForm property that returns the Form that contains the control. If you know that the UserControl will always be attached to MyParentForm, you just cast the ParentForm and then you can access all public controls, methods, etc.
Here's what I mean:
public class conTemplate
{
public MyParentForm MyParentForm
{
get
{
return (MyParentForm)this.ParentForm;
}
}
}
This way, you can easily access any public members of MyParentForm. Your conInbound class could have code such as this.MyParentForm.GlobalSettings.etc..., and could even have access to any public controls.
I'm not totally sure I understand your problem. It sounds like you want the user control to "do something" with it's parent form. If that's the case, you may want to consider adding events to the UC and then handle them on the form itself.
Basically, for your UC's "continue", you'll have an event that's fired when it's pressed. You'll want to handle that in your form. I'm not real sure about the syntax from memory, or I'd work something out for you code-wise. But I think that's the route you'll want to take. Think of your UC like any other windows form control. If you add a button to your form, you assign it it's event method. Do the same with the UC.
I found this and thought it may be helpful. Scroll down to where it talks about UC's and events.
http://www.akadia.com/services/dotnet_user_controls.html
Hope this helps.
EDIT after new info from OP.
You could declare a global variable inside the UC of type yourForm and then set that variable to the ParentForm at run-time, if I'm understanding you correctly.
So, inside your UC Class, you could do:
private parentFormInstance;
then inside the constructor of the UC, you could set it as such:
parentFormInstance = this.ParentForm; (or whatever the property name is).
This allows you at design-time to use:
parentFormInstance.DoSomething();
without the compiler yelling at you.
Just basic advice, but if you can go back and make it easier on yourself, even if it takes some additional time re-working things, it'd be worth it. It may save you time in the long run.

Accessing a public property from with Repeater_ItemCommand

I've got a user control which has a public property like so:
public string[] ImageIDArray {
get;
set;
}
Within my control I have a repeater, and inside that repeater I have a standard asp.net button. When the button is clicked I need to manipulate the ImageIDArray property. Im using the ItemCommand event handler.
The problem is when the ItemCommand handler runs the ImageIDArray is null. So, there's 2 questions here really.
1) At what point in the page lifecycle do properties get set (unless you programatically set them)
2) How do I get round this problem. Do I need to store the value in the viewstate?
Thanks in advance
Al
Please take a look at this article. Repeater.ItemCommand Event
I believe it contains the relevant infromation that you required.
The whole page life cycle is really stateless, apart from viewstate which gives a perception of state. The controls on the page get recreated every time you do a postback. This MSDN article does a good job explaining it. The data that you loaded previously to the rendered controls are kept in viewstate (for the vs enabled controls) and are loaded back to them, unless user has changed them before posting back. If the user has changed them and you do a postback, those values are loaded in the LoadPostBackData event, which runs after the viewstate has been populated.
I do not know where and when you are loading the data into your ImageIDArray but I'm assuming you are not loading it on every postback which is why the data is empty on your itemCommand event handler.
To answer your first question, just having a property does not do anything, it matters where you are setting the value to it. If you are assigning a value to the property in the ascx file, it stays with the control. Otherwise, you have to load it everytime.
Load the ImageIdArray on the Page_Load event of the user control. The page_Load event runs before your ItemCommand event is fired. If you don't want to load the ImageArray on every postback, you can consider storing that in the session or viewstate. Storing in the session means that the data is available for you for the session duration of the user. ViewState only lasts as long as you are on that page.
You can add data to the ViewState manually as ViewState["ImageIdArray"] = ImageIdArray. Or you can change your property as,
public string[] ImageIdArray
{
get { return (string[])(ViewState["ImageIdArray"] ?? null); }
set { ViewState["ImageIdArray"] = value; }
}
Here is an article that explains a little more about state management in asp.net.

Maintaining usercontrol state on dynamically added usercontrols

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.

Categories