Why does DropDownList.SelectedValue is relied on viewstate? - asp.net

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.

Related

Data Bind Asp .Net Drop Down List SelectedValue to Page Property

I have a property in my Page
Public Property Status as String
I set up the status list manually by calling
list.Items.Add(New ListItem("Open", "Open"))
and then I call
list.DataBind()
On my page I want to set the selected value to the value in that property and I want the value in that property to contain the value of the list on each post back.
I tried SelectedValue='<%# Bind("Status") %>' but I get the following error:
Databinding methods such as Eval(), XPath(), and Bind() can only be
used in the context of a data bound control.
Is there something I am missing? The end goal is to have the state of the DropDownList persisted to the Page property between post backs.
Thank you.
You can get the selected value of dropdownlist by
status=DropDownList1.SelectedItem.Value.ToString();
In order to get selected value after each postback,you need to add event in your dropdownlist onselectedindexchanged="DropDownList1_SelectedIndexChanged" and in that event you can get selected value as follows.
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
status=DropDownList1.SelectedItem.Value.ToString();
}
you can persist the value of your status property by creating a view state as follows(in c#)
public string status
{
get
{
if (ViewState["status"] != null)
return ViewState["status"].ToString();
else
return null;
}
set
{
ViewState["status"] = value;
}
}
The server control has viewstate that persist the value of the control between postbacks automatically as long as you don't set the viewstate property of a control to false.
Second thing is you dont need to call it the DataBind() function in your case.

validate a template text box with database value

I'm developing a website and have a textbox in gridview template field. This textbox collects the input for the "order quantity" which has to be compared against the "available quantity"(stored in the database).
I use RowEditing, RowUpdating and RowCanceling events for edit operations in gridview.
I tried implementing the above requirement with "TextChanged" Event of text box. But the issue is, when the user edits the quantity and clicks on "Update" button, text box event fires first and update button event doesn't fire. So, user is forced to click the "Update" button again.
Is there any work around for the above scenario or any other event used to implement above requirement?
Thanks,
Balaji g
You can Use RangeValidator For That...
<asp:RangeValidator ID="RangeValidator1" ControlToValidate="YourTexBoxId"
runat="server" MinimumValue="0" Type="Integer"
MaximumValue='<%# GetQuantity(Eval(TotalQaunt).ToString(),Eval(ItemId).ToString()) %>'
ErrorMessage="RangeValidator"></asp:RangeValidator>
write a function lyk this ON CODE BEHIND
string GetQuantity(string totalquantity,string itemid)
{
DO OPERATRION TO GET QUANTITY.....
return quantity;
}
I think I'd try the CustomValidator. With the CustomValidator, you can define the validation code in the "_ServerValidate" event handler.
In your validation code, you can call the database to get the current value and then compare it to the submitted value (which would be available via the event handler's ServerValidateEventArgs paramenter if you specify a ControlToValidate).
In order to look up the current value in the database, you'll need to know which record was clicked. For that, you could either use the row button's CommandArgument property or include a hidden field in your row to store some type of ID. Then you could capture that ID and use it to look up the desired record in the database.
Updated with Code
Protected Sub cusCustom_ServerValidate(source As Object, args As System.Web.UI.WebControls.ServerValidateEventArgs)
Dim cv As CustomValidator = CType(source, CustomValidator)
Dim HiddenField_ID As HiddenField = CType(cv.Parent.FindControl("HiddenField_ID"), HiddenField)
*** Use ID to look up value in database ***
End Sub

Error with dynamically adding controls

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.

ASP.Net DropDownList SelectedIndex not changing

I am working on a project where I have an OdbcDataReader that reads in the data from a query and populates a DropDownList with the items from the query. When I run the web app and choose different items in the list the selected value never changes whether postback is enabled or not, the smartpaging is enabled, or if the EnableViewState
protected void populateGrid(OdbcDataReader reader)
{
ClientDropDownList.DataSource = reader;
ClientDropDownList.DataTextField = "company";
ClientDropDownList.DataBind();
}
First you should set the AutoPostBack = true for the Dropdownlist control.
Second thing is You should bind the values in the Page_Load event by checking Ispostback
If(! IsPostBack())
{
Bind your dropdownlist here.
}
Thrid thing is As per the Asp.Net Life cycle process.Page load will fire every time.When ever the page getting refresh.
Check that you are not calling PopulateGrid on each PostBack. One of the most common issues people run into.
If your calling PopulateGrid in your Page_Load wrap it in a if (!IsPostBack).
you can use this code:
foreach (ListItem item in YourDropDownList.Items)
{
if (item.Text == defaultText)
{
item.Selected = true;
break;
}
}

persist dropdownlist on paging

I have a GridView and I populate it via a List . One of its columns is a DropDownList and AllowPaging is set to true. My problem is when I choose a value on the ddl and after a paging the selected value is lost. Is there any way/idea to persist the selected values?
Thanks for your help.
You could use a Dictionary object within view state to save multiple values i.e.
Dictionary<int, string> ddlValues = new Dictionary<int, string>()
where int is the row index and string is the ddl selected value. Of course this could be an int/guid or whatever depending on the actual value stored in the ddl or an int if you want to work with selectedIndex instead.
on the page event you would need to do
protected void MyGridView_PageIndexChanging(Object sender, GridViewPageEventArgs e)
{
for(int rowIndex = 0; rowIndex < myGridView.Rows.Length; rowIndex++)
{
DropdownList ddl = myGridView.Rows[rowIndex].FindControl("ddlId") as DropDownList
if(ddl != null)
{
if(ddl.SelectedIndex > 0) //.. or sensible check appropriate to you
{
int ddlIndex = rowIndex * e.NewPageIndex + 1;
//.. add pageIndex and selectedValue to dictionary
ddlValues.Add(ddlIndex, ddl.SelectedValue);
}
}
}
}
Don't worry about the current page ddl values. These will be persisted with viewstate in the normal way. It is the 'hidden' pages that we are accounting for. Hence we are repopulating the dictionary when the grid pages.
The Dictionary could then be saved in session/viewState and used to rehydrate the dropdownlist by doing the process in reverse. For instance when the page loads (checking !isPostBack) or when the grid rebinds depending on exactly how you have set things up
You will probably want to persist the Data in the ViewState. Check out this MSDN article
http://msdn.microsoft.com/en-us/library/ms972976.aspx
After you save it in the ViewState, you can retrieve the data on PostBack like this:
if (!Page.IsPostBack)
{
//do some stuff
}
else
{
//retrieve the viewstate information
selectedValue= ViewState["dropdownlistValue"].ToString();
}
Alternatively, you could also maintain the information in a Session variable but that may introduce other issues depending on what exactly you are doing.

Resources