I've a custom property on a user control which has multiple state/modes. If this property is set in the parent page: I would like for my control to update automatically. Using the property in the page load does not work because it is not initiated.
I can imagine 3 methods to do this:
On the property, I could add a code block that would call this.DataBind().
I could add the code by overriding the virtual method DataBind.
I could create a public proprietary Update method.
I would like any input into what is the best practice in general. More to the point, I've chosen to override the virtual method DataBind. My pseudo code is as such:
public override void DataBind()
{
if (SpecialMode)
{
.. load from database
}
base.DataBind()
}
I'm interested in the ordering of the base.DataBind(). I've seen it typically placed first but after I load the data from the database: I will need to databind to get the data to display.
Any input into these considerations will be greatly appreciated.
To be clear:
This control is a Poll widget. It will typically search and load the poll to display from the Page_Load event. But, it also has a reports mode which allows the page in which the control is embedded to change the Id of the poll to display. This property will not be initiated in Page_Load. Okay, part of this mess is that I've a property for an object, and I've also a duplicated property for the ViewState but only the Id.
You could have your user control handle the automatic databinding in its PreRender event. When the Id property is changed (or whatever property it is that triggers the automatic databinding) you could set a flag within the control that is later checked in PreRender to indicate that data needs to be loaded.
Alternatively, instead of setting a flag you could possibly clear any data that you've already loaded from ViewState and implement logic in PreRender to say "If there is no data, then load it".
This avoids potentially loading data multiple times within a single request due situations where there happens to be code that sets the Id property multiple times.
Your control should not depend, in any way, on the page in which it is embedded. Instead, have an "AutoUpdate" property on the control, and have the page set it. The page should also set properties telling the control how to update when the AutoUpdate property is set.
Related
I am writing a composite control in c# asp.net as an experiment and would like to have all the instances of my composite control interact with one external control. Is this possible?
Things I have tried:
option 1: check from within the control whether an external control exists on the page, and add it if it doesn't
option 2: have the target control's id passed to the composite control at design time and then use this.Page.FindControl()
Obviously it was wishful thinking that it would be that simple :)
If I try do this from within the CreateChildControls Method, this.Page.FindControl(target control) always returns null. If I try to add the control to the page from within this method, it throws an exception:
"The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases."
is there another method / event where I can achieve this?
Why don't you expose a public property on your Composite Control of what output from them, then when rendering the Panel's contents, recurse through the page, find all instances of the composite control, grab the text, and add it to the panel?
You can create multiple instances on the same Web form by implementing the INamingContainer Interface. This basically helps prevent id clashes in the same namespace.
If you want to access another control set a property on it to expose the data you want made public.
Build Composite Controls
I want to add more controls to page based on some specific conditions. Those controls don't need any ViewState or binding data, it is as simple as a static link.
I wonder where I could write the code: inside OnLoad or OnInit method? and why? If I place it inside OnLoad, should I add following line: if (IsPostBack) return; before any initialization code?
You can add controls in either the OnInit method or OnLoad, whether they need view state or not. (The reason why is because as soon as you add a control to the Page the control loads its view state, even if you add it after the LoadViewState stage...)
should I add following line: if (IsPostBack) return; before any initialization code?
No. It is imperative that your dynamically added controls are added to the control hierarchy on every page load, not just the initial one.
If you are going to work with dynamically-added Web controls, I strongly suggest you read these two articles:
Dynamic Controls in ASP.NET (This is actually three articles, this being the first in a series.)
Creating Dynamic Data Entry User Interfaces
For a working, end-to-end example of dynamically loading controls based on some external conditions (such as configuration in a database), see Creating a Dynamic Data-Driven User Interface.
Happy Programming!
I would suggest just adding the controls to the page statically and toggling their visibility to "True" when the conditions are met. They won't render anything to the page when they're invisible, and this will save you a lot of headaches, especially since it sounds like you're fairly new to dynamic controls.
I'm not sure I fully understand, but I'd personally put an asp:Literal on the page (or several if you need them in different places) and then create the HTML you need in the OnLoad event.
If you do that, then the html you put into that literal will be saved in viewstate, and therefor you won't have redo it on postback.
http://chetanwarade.wordpress.com/2010/08/21/asp-net-add-dynamic-control-and-retrieve-dynamic-control-value-2/
Here is code that demonstrate how to add dynamic control and retrieve dynamic control value.
I am working on a project which creates controls dynamically for a form in the page_load event, loads in their current values from the database and saves their values (using FindControl) when the user clicks the continue button.
When I added a control statically in the .aspx page and followed their same procedure of loading the value in the page load and saving it on the button press I found that the value would not save correctly. It seems that it wouldn't save because the click event fires after the page_load, so the page_load of the post back reverted the value and the user entered value was not saved.
The strange thing is that by changing the control to be dynamically created just as all the other controls on the page and keeping the loading and saving the same it now works. Even though the page load still creates the control with the old database value.
It seems like a very fundamental asp .net feature here but i'm just unclear as to what is going on. I suspect it is to do with the timing of creation and maybe when the view state kicks in.
Static page controls are created just like dynamic page controls. The difference might be coming in your Page_Load. Whenever you postback all the controls are created afresh which means they are created with their initial values. This happens because after creating the controls asp.net throws away the controls/objects.
So, when the request comes, the first thing that asp.net does it to recreate the controls by looking at their definitions (in the designer files). On each postback they are created and initialized again losing their state in the process.
But after creating the controls Asp.Net loads any viewstate that is sent along with the request which makes people think that the state is always saved at the server.
What might be happening is that either the viewstate is not enabled for your control (in case they are created in designer), in which case you may try using EnableViewState property to true of the control.
Or, when you're doing a Page_Load, you're forcefully re-initializing everything. And in process losing all the control data. If you could post the logic of Page_Load, it might get clarified.
Make sure that:
you are not setting the value again for the static control in Page_Load. The dynamic control are probably getting around it by grabbing the ViewState and form values at a different stage in the lifecycle.
The dynamic controls are added After the static control. Or at least they are added in a different container. Placement in the control's collection can affect the ViewState, although it doesn't look like your scenario / since what you mention seems to be more about the values in the current post.
The save is happening After the Page_Load in response to the corresponding event.
I've run into similar problems in the past (quite a few times actually), but what helped me the most is understanding the ASP.NET Page Lifecycle.
Microsoft has an article on it which describes it pretty well, but this post by Solomon Shaffer really cleared up everything.
I suggest reading them both and coming back with additional questions regarding to a particular state, when to load/save data etc..
Hope this helps.
Marko
Note that you may want to use Page.IsPostBack property to avoid reinitializing values on button clicks and other events.
private void Page_Load()
{
if (!this.IsPostBack)
{
// Assign values to the controls.
}
}
I've got a strange problem concerning dynamically loaded controls in a asp.net application.
So there is a control where user have to select some items and/or do some text input (textboxes). The control depends on a single dropdown list element.
So user A chooses a certain value in this dropdownlist "controlselector" -> on of the many controls will be loaded. After that the user clicks on save and then it should save it to the database.
The problem is following that not every item is saved into the databse.
I create and recreate the control at every Page_Load, i've turned autopost back on the "controlselector" but the control is loading at the page_load event. When trying to save the elements are empty, but not every item :(
MyCustomControl:
FillElements(someParameter)
{
//fill some lists, dropdowns, checkboxes or whatever with some values from db
}
Foo Save()
{
//Save selected input(also some textboxes)
//and return an object
return foo;
}
Page:
Page_Load()
{
PlaceHolder.Clear();
//with Createpath the path to the control is created and loaded
PlaceHolder.Controls.Add(LoadControl(CreatePath(Selector.SelectedValue)));
//some methods are started to fill some lists in the control
((MyCustomControl)PlaceHolder.Controls[0]).FillElements(someParameter);
}
Save_Button_Click()
{
var myFoo = ((MyCustomControl)PlaceHolder.Controls[0]).Save();
myFoo.DoSomethingElse();
}
it seems that sometimes the page remembers values and sometimes not... ver strange everything
thanks
[EDIT]
The problem i see that, there is 2 time a dynamic fill action.
1.) deciding which and then loading the custom control
2.) filling the custom control with the parameters
Page_Load is too late in the life cycle to create dynamic controls, because state is restored to controls before the load event. This means you need to create your control earlier, or ASP.Net won't see it when it comes time to restore state. Try creating them in the Init event instead. Or, even better, try one of these options:
Create one custom control type that adapts itself as needed and have a normal instance of the control on the page.
Place all controls on the page but only set Visible to true for the one you care about.
You need to check for "IsPostBack" if it is you dont want to recreate those controls... its killing the values etc that you have in them.
try changing your code to something like this.
Page_Load()
{
if(IsPostBack == false){
PlaceHolder.Clear();
//with Createpath the path to the control is created and loaded
PlaceHolder.Controls.Add(LoadControl(CreatePath(Selector.SelectedValue)));
//some methods are started to fill some lists in the control
((MyCustomControl)PlaceHolder.Controls[0]).FillElements(someParameter);
}
}
thanks for your help, but the problem was on something totally different
the items which were loaded dynamically into the dropdowns which where also loaded dynamically, had some "\n" special character, but not every item
thats why not every item got lost just few
i don't know if i should/can mark this as answer, because the problem was on a other place
I'm trying to wrap my head around asp.net. I have a background as a long time php developer, but I'm now facing the task of learning asp.net and I'm having some trouble with it. It might very well be because I'm trying to force the framework into something it is not intended for - so I'd like to learn how to do it "the right way". :-)
My problem is how to add controls to a page programmatically at runtime. As far as I can figure out you need to create the controls at page_init as they otherwise disappears at the next PostBack. But many times I'm facing the problem that I don't know which controls to add in page_init as it is dependent on values from at previous PostBack.
A simple scenario could be a form with a dropdown control added in the designer. The dropdown is set to AutoPostBack. When the PostBack occur I need to render one or more controls denepending on the selected value from the dropdown control and preferably have those controls act as if they had been added by the design (as in "when posted back, behave "properly").
Am I going down the wrong path here?
I agree with the other points made here "If you can get out of creating controls dynamically, then do so..." (by #Jesper Blad Jenson aka) but here is a trick I worked out with dynamically created controls in the past.
The problem becomes chicken and the egg. You need your ViewState to create the control tree and you need your control tree created to get at your ViewState. Well, that's almost correct. There is a way to get at your ViewState values just before the rest of the tree is populated. That is by overriding LoadViewState(...) and SaveViewState(...).
In SaveViewState store the control you wish to create:
protected override object SaveViewState()
{
object[] myState = new object[2];
myState[0] = base.SaveViewState();
myState[1] = controlPickerDropDown.SelectedValue;
return myState
}
When the framework calls your "LoadViewState" override you'll get back the exact object you returned from "SaveViewState":
protected override void LoadViewState(object savedState)
{
object[] myState = (object[])savedState;
// Here is the trick, use the value you saved here to create your control tree.
CreateControlBasedOnDropDownValue(myState[1]);
// Call the base method to ensure everything works correctly.
base.LoadViewState(myState[0]);
}
I've used this successfully to create ASP.Net pages where a DataSet was serialised to the ViewState to store changes to an entire grid of data allowing the user to make multiple edits with PostBacks and finally commit all their changes in a single "Save" operation.
You must add your control inside OnInit event and viewstate will be preserved. Don't use if(ispostback), because controls must be added every time, event in postback!
(De)Serialization of viewstate happens after OnInit and before OnLoad, so your viewstate persistence provider will see dynamically added controls if they are added in OnInit.
But in scenario you're describing, probably multiview or simple hide/show (visible property) will be better solution.
It's because in OnInit event, when you must read dropdown and add new controls, viewstate isn't read (deserialized) yet and you don't know what did user choose! (you can do request.form(), but that feels kinda wrong)
After having wrestled with this problem for at while I have come up with these groundrules which seems to work, but YMMV.
Use declarative controls whenever possible
Use databinding where possible
Understand how ViewState works
The Visibilty property can go a long way
If you must use add controls in an event handler use Aydsman's tip and recreate the controls in an overridden LoadViewState.
TRULY Understanding ViewState is a must-read.
Understanding Dynamic Controls By Example shows some techniques on how to use databinding instead of dynamic controls.
TRULY Understanding Dynamic Controls also clarifies techniques which can be used to avoid dynamic controls.
Hope this helps others with same problems.
If you truly need to use dynamic controls, the following should work:
In OnInit, recreate the exact same control hierarchy that was on the page when the previous request was fulfilled. (If this isn't the initial request, of course)
After OnInit, the framework will load the viewstate from the previous request and all your controls should be in a stable state now.
In OnLoad, remove the controls that are not required and add the necessary ones. You will also have to somehow save the current control tree at this point, to be used in the first step during the following request. You could use a session variable that dictates how the dynamic control tree was created. I even stored the whole Controls collection in the session once (put aside your pitchforks, it was just for a demo).
Re-adding the "stale" controls that you will not need and will be removed at OnLoad anyway seems a bit quirky, but Asp.Net was not really designed with dynamic control creation in mind. If the exact same control hierarchy is not preserved during viewstate loading, all kinds of hard-to find bugs begin lurking in the page, because states of older controls are loaded into newly added ones.
Read up on Asp.Net page life cycle and especially on how the viewstate works and it will become clear.
Edit: This is a very good article about how viewstate behaves and what you should consider while dealing with dynamic controls: <Link>
Well. If you can get out of creating controls dynamicly, then do so - otherwise, what i whould do is to use Page_Load instead of Page_Init, but instead of placing stuff inside the If Not IsPostBack, then set i just directly in the method.
Ah, that's the problem with the leaky abstraction of ASP.NET web forms.
Maybe you'll be interested to look at ASP.NET MVC, which was used for the creation of this stackoverflow.com web site? That should be an easier fit for you, coming from a PHP (thus, pedal-to-the-metal when it comes to HTML and Javascript) background.
I think the answer here is in the MultiView control, so that for example the dropdown switches between different views in the multi-view.
You can probably even data-bind the current view property of the multiview to the value of the dropdown!
The only correct answer was given by Aydsman. LoadViewState is the only place to add dynamic controls where their viewstate values will be restored when recreated and you can access the viewstate in order to determine which controls to add.
I ran across this in the book "Pro ASP.NET 3.5 in C# 2008" under the section Dynamic Control Creation:
If you need to re-create a control multiple times, you should perform the control creation in the Page.Load event handler. This has the additional benefit of allowing you to use view state with your dynamic control. Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the Page.Load event, ASP.NET will apply any view state information that it has after the Page.Load event handler ends. This process is automatic.
I have not tested this, but you might look into it.