I have added a user control into a form view item template, but I have added a public property to the user control, and I can't seem to figure out how to set the property. I have tried the following:
<uc1:OfacControl id="OfacControl1" runat="server" AssetEvictionId='<%# Bind("AssetEvictionId") %>' />
But the value never gets set correctly.
I have also tried doing it in the codebehind of the form in the preRender method like so:
var assetEvictionIdHiddenField = (HiddenField)oFormView.FindControl("AssetEvictionIdHiddenField");
var OfacControl1 = (Ofac)oFormView.FindControl("OfacControl1");
if (OfacControl1 != null && assetEvictionIdHiddenField != null)
OfacControl1.AssetEvictionId = Convert.ToInt32(assetEvictionIdHiddenField.Value);
This doesn't work either.
It appears to be a timing issue. It looks like the control is being rendered at a different time than the value is being set. If the bind syntax doesn't work, and I am forced to use the code behind, which form view event should I be using to set the value. I have also tried the OnItemCreated event. This didn't work either.
It seems you are trying to set the control in the wrong place. You should be setting it in Init.
ASP.NET Page Life Cycle Overview states the following:
Init
Raised after all controls have been initialized and any skin settings
have been applied. The
Init
event of individual controls occurs before the
Init
event of the page. Use this event to read or initialize control
properties.
Related
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.
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.
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
Ok I'm trying to understand how best to handle ViewState, for the programmatic setting of default values using C#. I understand that the construction of the ViewState hidden field is based on every value that is set after the OnInit event is triggered. What I'm not clear about is if there is a difference between using the control's constructor or the OnInit event to set default values.
public MyControl(){
this.Text = "SomeDefaultValue";
}
versus
protected override void OnInit(EventArgs e){
this.Text = "SomeDefaultValue";
}
I've seen some places that suggest testing the ViewState value for null in the get of the given property, like so:
public string Text {
get {
return this.ViewState["Text"] == null ?
"SomeDefaultValue" :
this.ViewState["Text"] as string;
}
set { this.ViewState["Text"] = value; }
}
I don't like that because it makes clearing the value confusing.
So, Is there any functional difference between using the constructor vs OnInit to set default ViewState values?
In terms of minimizing ViewState, there is no difference, as ViewState starts tracking after the OnInit method is run.
There are some functional differences, however: until the control is initialized, you cannot access other properties like the Page. For this reason, I usually prefer to use either OnInit or some handler tied to the Init event.
Also, be careful about overriding OnInit: you should call base.OnInit() to make sure that other event handlers for the Init event still get called.
I highly recommend that you read this excellent article on the topic: http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
Edit
To clarify, the ViewState starts tracking for a given control after the OnInit method is run for that control. So in the given example, you are safe to override OnInit like this:
protected override void OnInit(EventArgs e){
this.Text = "SomeDefaultValue"; // Make sure this happens before base.OnInit
base.OnInit();
}
This works because the Text property is saving the value to the ViewState of this control. However, let's say you have another child control (I'll use a Label as an example). That Label's OnInit will already have been run by the time your control's OnInit method is called. So if you want to change the Label's Text value, you'll need to do it during that label's OnInit phase (or sooner).
You could do it in the constructor of the current control:
public MyControl(){
this.Label.Text = "SomeDefaultValue";
}
... but as mentioned earlier you won't have access to the external control structure, which may be necessary in some cases. A good alternative in these cases is to use an Init event handler on the label itself. You can hook up the event handler itself in your constructor:
public MyControl(){
this.Label.Init +=
(sender, e) => this.Label.Text =
((TextBox)Page.FindControl("SomeControl")).Text;
}
... but this will only work if the control is declared directly as a member of your class. If the label is inside a template (like in a Repeater), you'll need to use markup to hook it up:
<asp:Label runat="server" OnInit="Label_Init" />
with the code-behind:
public void Label_Init(object sender, EventArgs e)
{
var label = (Label)sender;
label.Text = ((TextBox)Page.FindControl("SomeControl")).Text;
}
This latter example has the advantage of working in just about every circumstance I can think of, but it requires more boilerplate code, as well as a change in markup. So pick your poison based on your specific situation.
There is quite a detailed document on the ViewState over at MSDN:
...server controls don't begin tracking
view state changes until right at the
end of the initialization stage.
Second, when adding dynamic controls
that need to utilize view state, these
controls will need to be added during
the Page's Init event as opposed to
the Load event.
Just from this alone, I would say, if you're utilising the ViewState, use OnInit.
1) I assume Themes can be set programatically only inside Page.PreInit event handler due to the following reasons:
if we’d set a Theme inside Page.Init event handler, then by that time ViewState would already be tracked and thus any data applied by Theme would be tracked and marked as dirty ( which would consume lot of bandwidth ) ?
and if we’d set it after Init event, then Themes could also override deserialized ViewState data applied to individual controls?
Are there any other reasons why Themes can’t be set after Page.PreInit?
2) Also, why can't Master pages be applied after Page.PreInit?
thanx
According to this:
http://odetocode.com/articles/450.aspx
The 'MasterPageFile' property can only
be set in or before the 'Page_PreInit'
event.
This exception makes sense, because we
know the master page has to rearrange
the page’s control hierarchy before
the Init event fires
The article also includes this example:
using System;
using System.Web.UI;
public class BasePage : Page
{
public BasePage()
{
this.PreInit += new EventHandler(BasePage_PreInit);
}
void BasePage_PreInit(object sender, EventArgs e)
{
MasterPageFile = "~/Master1.master";
}
}
Or, an approach I've used before:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
if (Request.QueryString["Master"] == "Simple")
MasterPageFile = "~/Masterpages/Simple.Master";
}
Are there any other reasons why Themes
can’t be set after Page.PreInit?
Yes. Themes includes skins, which can specify properties for controls. Those properties need to be set during the Init event, so the desired theme needs to be selected before then.
ViewState tracking may be an issue, but I think it's a minor one compared to the above.
Note that a StyleSheetTheme (preferable to a regular Theme, IMO), is actually set from an overridden property on the Page, not by setting the value of the property itself (unless you set it from an HttpModule).
why can't Master pages be applied
after Page.PreInit?
Controls determine their IDs and various other characteristics and properties based on their location in the control tree (including things like accessing the form control, etc). A Master Page acts as what amounts to a set of parent controls, so controls can fully initialize themselves until that parent structure is in place. Initialization happens during the Init event, so the Master Page needs to be selected before then.