I have a UserControl. I dynamically create some controls (Columns for SPGridView. Grid control was added in markup, ObjectDataSource, Button and Label) in CreateChildControl method and add them to Controls collection. Two of these controls are added well (Button and Label) in postback, but one of them (MenuTemplate) raise the exception with such content:
"Failed to load viewstate. The control tree into which viewstate is
being loaded must match the control tree that was used to save
viewstate during the previous request. For example, when adding
controls dynamically, the controls added during a post-back must match
the type and position of the controls added during the initial
request. "
When I move my code to OnInit method all controls are added successfully. So, I have a question: Could someone explain me why some controls are added to Controls collection successfully and others are failed in CreateChildControls in postback? I have read about ViewState here. Probably I didn't understand some moments.
Look at me code:
protected override void CreateChildControls()
{
Label l = new Label();
l.ID = "labelTest";
l.Text = "Hello test!";
Button b = new Button();
b.Text = "Press test";
b.ID = "buttonTest";
b.Click += b_Click;
Controls.Add(l);
Controls.Add(b);
ObjectDataSource gridDataSource = new ObjectDataSource();
gridDataSource.ID = "gridDataSource";
gridDataSource.SelectMethod = "GetDataSource";
gridDataSource.TypeName = this.GetType().AssemblyQualifiedName;
Controls.Add(gridDataSource);
SPMenuField colMenu = new SPMenuField();
colMenu.HeaderText = "Test";
colMenu.TextFields = "Test";
colMenu.MenuTemplateId = "ListMenu";
// it is my SPGridView that added in markup
customGridView.Columns.Add(colMenu);
MenuTemplate titleListMenu = new MenuTemplate();
titleListMenu.ID = "ListMenu";
// The exception occurs here
Controls.Add(titleListMenu);
base.CreateChildControls();
}
I think, you can only add items into a template in Page_Load or other events(ie, after Page_Init) so that it will be retained in postback, not the template itself.. Templates are need to be created before or at Page_Init stage, otherwise it may not load the controls into that templates from viewstate or may result in error.
Related
in asp.net TextBox will create pressure to button1's. Button2 to the pressure inside the TextBox data consisting of label1 my yazdırıca. I tried to do it like this gives an error.
Object reference not set to an instance of an object.
button1_click{
TextBox txt = new TextBox();
txt.ID = "a";
txt.EnableViewState = true;
Panel1.Controls.Add(txt);
}
Button2_click{
TextBox deneme= Panel1.FindControl("a") as TextBox;
Label1.Text = deneme.Text;
}
After you create your control, on the first click, the control shown on the page and exist.
On the second click the page is not know any more about that text control because you did not save that information somewhere and because only you know that some time in the past you create it.
So on this code you have:
Button2_click{
TextBox deneme= Panel1.FindControl("a") as TextBox;
// here the deneme is null ! and you get the exception !
// the deneme is not exist on the second click, not saved anywhere
Label1.Text = deneme.Text;
}
The solution is to keep on viewstate what control you create and how, and re-create them on PageInit. Alternative you can redesign your page and think a different approach to that, eg you can have the TextControl's all ready on page hidden, and just open them.
Button2_click{
TextBox txt = (TextBox)Panel1.FindControl("a");
Label Label1 = new Label();
Label1.Text=txt.Texxt;
}
If I set on my page : EnableViewState="true" ViewStateMode="Disabled" - Then - the Viewstate is disable for the page ( unless override...)
Then, trying to read from (assuming the control has been populated in the last dump to the screen and a value is selected):
MyDDL.SelectedValue will yield ""
That's because of disabled viewstate :
But my question is at a higher level :
If it's all about a form value (which I still can get from Request.Form[MyDDL.UniqueID]) - and we're talking about an input which doesn't need anything to save its value.
Why does the DropDownList property named (SelectedValue) Is relied on ViewState ?
p.s. the TextBox onchangeevent does rely on viewstate although the control is an input (which doesnt need viewstate) - it saves the value of the text and then it compare it when postback.But it only relies on viewstate when you set onchange event ( and autopostback)
The SelectedValue relies on ViewState because on PostBack it rebuilds its ListItems from the ViewState and then sets the selected value on the DropDownList from the Request Object.
It is not taking the Request value as the SelectedValue directly. This in turn is because, ASP.Net can check if the posted DropDownList has not been tampered with at the client. It does so by first de-serializing the original items from the ViewState. It then finds the Request Value in the items and sets its Selected property as true. Only now, the SelectedValue property is available. (or SelectedIndex for that matter). It should be able to fire a SelectedIndexChanged event now.
This is also the reason that you do not need to bind the DropDownList again in PageLoad. The list items are automagically retreived from the ViewState.
If the ViewState is disabled, then there will be no original list items in the ViewState and will be empty. Hence it will not be able to mark any item as selected. Hence the SelectedValue will be 0 or the SelectedItem will be null. I think the SelectedIndexChanged event will also not fire. For things to work in this case databinding needs to be done, preferably on init.
There are workarounds to that however.
Complete Reference: http://msdn.microsoft.com/en-us/library/ms972976.aspx
Edit: (after Op's comments)
Following the page life cycle to see where SelectedValue relies on ViewState:
Stage 1 Init: The control heirarchy is built. If the DropDownList is bound here or the ListItems have been added declaratively, the List gets populated here.
Stage 2 Load ViewState: On PostBack, the ViewState is validated here and loaded into the DropDownList. There is no SelectedValue here.
Stage 3 Load PostBack Data: Here the Request Value (from the form request) is taken and then applied to the control. In this case of DropDownList it now sets the SelectedValue from the received Request Object Value, internal implementation is something like this:
string selectedValue = HttpContext.Current.Request.Form[DropDownList_Id];
Items.FindByValue(selectedValue).Selected = true;
What is important here is that if ViewState is not there and DropDownList is not data-bound, then the ListItem collection will be empty and hence SelectedValue property is 0. This has nothing to do with internal implementation of a property.
If the ViewState is not there (disabled) and DropDownList is data-bound, then the ListItem collection will exist and corresponding item will be marked as selected and hence SelectedValue property will return the correct value.
If the item collection is new (thru a re-binding with different data set or ViewState is invalidated), then the Request Form value would not be found in the item collection and again SelectedValue will be invalid.
Stage 4 Page Load: by this time the ViewState (or data-binding) and PostBack Data has already been loaded.
Stage 5 Raise PostBack Event: At this stage the OnSelectedIndexChanged event of DropDownList is fired if the index was changed in Stage 3.
Hence, the SelectedValue relies on ViewState at Stage 3. Of course, if the control is appropriately data-bound then it will not rely on ViewState as a corollary.
SelectedValue relies on ViewState to make sure the items collection has been populated prior to setting it. Data-binding / Re-binding is just another way to make sure the items collection is populated.
Hope that clarifies.
SUMMARY: If you want the control to work without ViewState, you need to populate/bind the Items collection on every postback. I recommend doing it in the Page_Init event (i.e. OnInit method).
First off, I always recommend this this awesome article: TRULY Understanding ViewState.
The SelectedValue doesn't require ViewState. Looking at the code for ListControl, which DropDownList inherits from, we see the code:
public virtual string SelectedValue
{
get
{
int selectedIndex = this.SelectedIndex;
if (selectedIndex >= 0)
return this.Items[selectedIndex].Value;
else
return string.Empty;
}
The important thing to take away from this code is that the Items list must be populated to get the SelectedValue.
If you utilize ViewState, the Items collection is persisted to / loaded from ViewState, which allows the SelectedValue property to work without rebinding the control.
protected void Page_Load(object sender, EventArgs e)
{
(!Page.IsPostBack)
{
string qry = "SELECT TOP(5)xxx, xxxx FROM dbo.xxxxxx ";
DataSet ds = new DataSet();
ds = SqlHelper.ExecInDS(qry);
drpDwn.DataSource = ds.Tables[0];
drpDwn.DataValueField = Convert.ToString(ds.Tables[0].Columns["xxx"]);
drpDwn.DataTextField = Convert.ToString(ds.Tables[0].Columns["xxx"]);
drpDwn.DataBind();
}
//Here You will get selected value from dropdown
string sss= Request.Form["drpDwn"];
}
If you want the DropDownList to work without ViewState, you can bind the control in page_load only once as given below:
protected void Page_Load(object sender, EventArgs e)
{
//whatever you use declarative binding (in aspx page), or define data source here
if (!IsPostBack)
{
ddl.DataBind(); //fire databinding events and fill items, and selectedvalue has a value.
}
//you can get the selectedvalue
var sv=ddl.SelectedValue ; //
}
In the case ( ViewState is disabled), Asp.net FrameWork retrieve the items from the back end with every PostBack.
In the case (ViewState is enabled), Asp.net FrameWork retrieve the items from the ViewState without hitting the back end with every PostBack
Normally, the Asp.net FrameWork fire the data binding events in PreRender event: Read ASP.NET Page Life Cycle Overview
You can confirm that behavior by enabling Trace.
SelectedValue doesn't rely directly on ViewState from source code of ListControl , BUT depend on items as described above.
Object reference not set to an instance of an object.
protected void cmdSave_Click(object sender, EventArgs e)
{
string strNames = string.Empty;
CheckBoxList Chkboxx = (CheckBoxList)PlaceHolder1.FindControl("Chkbox");
foreach (ListItem em in Chkboxx.Items) //-------- (Showing error)
{
if (em.Selected)
{
strNames += em.Value + ", ";
}
}
string final_name = strNames.Substring(0, strNames.Length - 2);
lblNames.Text = final_name;
}
Actually I am adding Checkbox control dynamically :
protected void ddl_varient_SelectedIndexChanged1(object sender, EventArgs e)
{
string query = "select prd_vrtyvalue_id,varient_value from tbl_ProductVariety_Value where varient='" + ddl_varient.SelectedItem.Text + "' " +
" order by varient_value asc ";
DataTable abc = new DataTable();
SqlDataAdapter ada = new SqlDataAdapter(query, new CommonClass().connection());
ada.Fill(abc);
ChkboxList.ID = "Chkbox";
for (int i = 0; i < abc.Rows.Count; i++)
{
ChkboxList.Items.Add(new ListItem(abc.Rows[i]["varient_value"].ToString(), abc.Rows[i]["prd_vrtyvalue_id"].ToString()));
}
ChkboxList.RepeatColumns = 2;
PlaceHolder1.Controls.Add(ChkboxList);
}
Can Anybody tell me, what exactly i am doing wrong !
The way ASP.NET WebForms work is that the entire page is re-built during each post back. So, I imagine this is what is occuring:
Page gets "built" and includes only controls defined within your ASCX/ASPX file.
User clicks on DDL_VARIENT checkbox and the ChkboxList is added to PlaceHolder1
Form is rendered back to the user so they can see ChkboxList
Save button is clicked, causing another postback.
Page is re-built, setting all the controls back to what is defined within your ASPX/ASCX code. This does not include ChkboxList.
Your code is hit, ChkboxList no longer exists and you get your problem.
To fix, you could re-add your ChkboxList on Page_Load depending on the value of your DDL_VARIENT checkbox. If I were you though, I'd be tempted to define the ChkboxList within your ASPX/ASCX code and then set the visibility of the list depending on the value of the DDL_VARIENT checkbox within Page_Load.
I should add, the entire of the above is dependant upon you using ASP.NET WebForms. If you're using MVC then it's probably wrong.
I am building many textboxes programmatically in my ASP.NET page, after I've clicked a button, I would like to process those values, is there any possibility to retrieve them from their ID in ViewState ?
Here is my code :
Reference of the table in the aspx :
<asp:Table ID="Distances" runat="server" ViewStateMode="Inherit"></asp:Table>
Then in code behind after creating all the rows and cells, I add a textbox into some of them :
Distances.Rows[j].Cells[i].Controls.Add(CreateTB(distance.ToString(), (i + j * rows).ToString(), false));
protected TextBox CreateTB(string text, string id, bool ebanled = true)
{
TextBox tb = new TextBox() { Text = text, ID = id, Enabled = ebanled};
tb.TextChanged += new EventHandler(OnTBChanged);
return tb;
}
ViewState is enabled by default, so it should already work that the Text property is persisted across postbacks.
So you could for example use FindControl("TextBoxID") or enumerate them to get the refernce to the TextBox (assuming that they are added to a container-control like Panel):
foreach(TextBox txt in MyPanel.Controls.OfType<TextBox>())
{
String text = txt.Text;
}
or
TextBox txt = (TextBox)MyPanel.FindControl("TextBox1")
String text = txt.Text;
I assume you're not recreating those TextBoxes on postbacks. Therefore you need to use the same ID as before and recreate them in Page_Load at the latest stage in page's life-cycle. So you can create them in an event, but you cannot recreate them there.
You should show your code where you create them dynamically, then i could be more specific.
TRULY UNDERSTANDING DYNAMIC CONTROLS
I have this code:
Control ctrl = Page.LoadControl("~/UserControls/ReportControl.ascx");
IReport rpt = (IReport)ctrl;
rpt.LoadData();
Panel.Controls.Add(ctrl);
So far everything is working as expected.
Now I need on Button click postback event to get the loaded control and cast to the interface to use a method, and tried this:
if (Panel.Controls.Count > 0) {
Control ctrl = Panel.Controls[0] as Control;
IReport rpt = ctrl as IReport;
string result = rpt.AMethodToInvoke();
}
This cast cannot happen and the control I get from the panel is a LiteralContol.
Any ideas? Thank you.
Have you got any other controls in your panel?
Maybe give your control an ID so
Control ctrl = Page.LoadControl("~/UserControls/ReportControl.ascx");
ctrl.ID = "UniqueID";
IReport rpt = (IReport)ctrl;
rpt.LoadData();
Panel.Controls.Add(ctrl);
And then user FindControl on the panel
Control ctrl = Panel.FindControl("UniqueID");
Also as you are adding the controls dynamically you need to make sure you are re-adding them on postback otherwise when you run the FindControl() it will return null.