Databinding problems with postback - asp.net

I've two issues currently preventing me from finishing two projects properly. I'll put them both here as I believe they're connected to the asp.net page lifecycle, but I can't find a way around them.
First I have a DropDownList which I must sort in codebehind. It only contains text, so I should be able to do that with the following method called in page load:
Dim alist As ArrayList = New ArrayList
For Each litem As ListItem In ltEsittelyDropDownList.Items
alist.Add(litem.Text)
Next
alist.Sort()
Dim uusiDDList As New DropDownList
For i As Integer = 0 To alist.Count - 1
Dim litem As New ListItem
litem.Text = alist(i).ToString
litem.Value = alist(i).ToString
uusiDDList.Items.Add(litem)
' Response.Write(alist(i).ToString)
Next
ltEsittelyDropDownList = uusiDDList
ltEsittelyDropDownList.DataBind()
As you can see, there's a commented response.write in there, which shows the list is actually sorted. So why, when I load the page, can't I see any effect?
The other problem, which is more critical and difficult, is as follows:
In the aspx page I'm binding a SQL Server 2005 datasource to a gridview. And in the code-behind I catch on to the RowDataBound event in which I handle some links and properties inside the gridviews' cells. But I cannot get this to work on the first page load, only after the first extra postback.
So, what is there to do? And thanks for all advice in front!

Your first problem is calling DataBind on a control you have filled manually. You likely have a DataSource specified in the control declaration, which is being used when DataBind is called. You can simplify the code by just adding the list items to the original control:
For i As Integer = 0 To alist.Count - 1
ltEsittelyDropDownList.Items.Add(New ListItem(alist(i).ToString())
Next
Alternatively, as you have a collection already, you can just bind it to the control:
ltEsittelyDropDownList.DataSource = alist
ltEsittelyDropDownList.DataBind()
For your second problem, some example code would help - specifically, where and how the control is databound and the code in RowDataBound.

Related

ASP.Net Get Values ArgumentOutOfRangeException Thrown

I'm trying to loop through a gridview and save all the items in it at once. However I have a problem getting the values from the dropdownlist and textbox. I get this error each time:
ArgumentOutOfRangeException was caught. Specified argument was out of the range of valid values.
Here's the code I'm using:
foreach (GridViewRow gvr in gvInvalidOrgs.Rows)
{
try
{
org_code = Convert.ToInt32(gvr.Cells[0].Text);
division = ((DropDownList)gvr.Cells[1].Controls[0]).SelectedValue;
org_description = (((TextBox)gvr.Cells[2].Controls[0]).Text);
}
...
}
Both the textbox and the dropdownlists are dynamically created on rowbound if that matters.
TIA
Without knowing precisely which line the exception is thrown on, it's hard to diagnose with equal precision, but what's likely happening is that you're looking for a control in a cell that doesn't have any controls.
How could this come about? Well, since you are creating the controls in RowDataBound, if you don't bind your grid on every postback (and I'm not recommending that you do) your dynamically-added controls won't be recreated on postback. The posted data will include values for those controls, and ViewState will contain data for those controls, but if you don't recreate those controls on every postback, those controls won't exist when you attempt to access their values.
But you don't have to rebind to recreate the controls. As Mr. Schmelter says in his comment, if you move your creation of the controls from the RowDataBound to the RowCreated event handler, they will be created when the grid rows are re-created on postback.

GridView Loses DataKeys

Why is it that when I try to set my GridView's sorting using session states that suddenly my GridView no longer has DataKeys? All I did was put the following code into my Page_Init;
Dim SortDirection As String = Session("SortDir")
Dim sortExpression As String = Session("SortExp")
If Not SortDirection Is Nothing AndAlso Not sortExpression Is Nothing Then
If (SortDirection = "asc") Then
GridView1.Sort(sortExpression, WebControls.SortDirection.Ascending)
Else
GridView1.Sort(sortExpression, WebControls.SortDirection.Descending)
End If
End If
But if I comment this out than my other methods don't crash out any more as my GridView now has it's DataKeys. Why is this?
UPDATE
This is the exact line that stops working when the above code is in place.
Dim UserID = GridView1.DataKeys(e.RowIndex).Value.ToString
According to debugger GridView1 has columns but it's DataKeys Count is 0. The error I receive is;
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
You want to perform those actions in the Page_Load (possibly in a If Not Page.IsPostBack block) event, not in the Page_Init event. Init is for initializing or reading control properties; Load is where you generally set properties (like sort direction, etc).
Basically, your ViewState hasn't been loaded in Page_Init yet. So, you modify the controls properties in Init, then some properties get filled out from the ViewState, and this leads to unexpected behavior when your Page does the Load event (which recursively calls each server control's Load event).
You can read all about this (somewhat confusing) topic on MSDN: ASP.NET Page Life Cycle Overview

Help with dynamically added controls in .net

I'm stuck! I understand the page lifecycle and how i need to add the dynamic controls on page_init if I want to take advantage of viewstate. Also I know that I should try to avoid dynamic controls when possible. The dynamic controls are created depending on an object that is created from custom event arguments sent from a custom treeview. Problem is I need viewstate so I need to create them in page_init but I don't have the event args to create the object that tell me what controls to add until later in the lifecycle. Solution...
Private Function GetEventArgs() As npTreeViewEventArgs
Dim control As Control = Nothing
Dim e As npTreeViewEventArgs = Nothing
Dim ctrlname As String = Page.Request.Params("__EVENTTARGET")
Dim args As String = Request.Params("__EVENTARGUMENT")
If ctrlname IsNot Nothing AndAlso ctrlname <> String.Empty Then
control = Page.FindControl(ctrlname)
End If
If TypeOf control Is npTreeView AndAlso Not String.IsNullOrEmpty(args) Then
e = New npTreeViewEventArgs(args)
End If
Return e
End Function
I use this in page_init to create my object and controls. This feels very dirty to me. Is there another way to handle this?
This is actually the most straightforward solution to this type of problem. If you can't add all the controls to the page on every postback and use visibility to control their appearance, then what you are doing there is exactly what I would recommend. (And have recommended before.)
I cringe when I see people resort to redirects, or implementing their own viewstate tracking, or doing extreme dynamic control manipulation to solve this. It may feel dirty, but it's infinitely more understandable and maintainable than the alternatives.
Yes. The way I did it is to overload the viewstate of the dynamic controls to store it in their parents viewstate. Also overload the reading of the dynamic controls view state. Then you can create them late in the page cycle.
Of course it is a little trickier than that... but you get the idea. (I would post code examples, but it was a prior job and don't have access to them right now.)

Why is the DataBind() method necessary?

Simple question, I guess.
For a long time I've blindly followed a (supposedly) common pattern when programmatically databinding my ASP.NET controls. Namely:
gridView1.DataSource = someList;
gridView1.DataBind();
However, if I were setting my GridView to bind to a DataSource control via the DataSourceID property, the call to DataBind() is unnecessary. Namely:
gridView1.DataSourceID = LinqDataSource1;
is sufficient.
Furthermore, if you try to set the DataSource property in ASPX markup, you are greeted with the following:
You cannot set the DataSource property declaratively.
I assume these are related, but I am still stumped as to why DataBind() is necessary. The difference between DataSource and DataSourceID is secondary - I can understand some magic taking place there. The real question is why doesn't the DataSource propery setter cause databinding automatically? Are there any scenarios in which we want to set the DataSource but not bind to it?
In ASP.Net, it's often important to have certain data available and ready at certain points in the page life cycle, and not before. For example, you may need to bind to a drop down list early to allow setting the selected index on that list later. Or you might want to wait a bit to bind that large grid to reduce the amount of time you hold that connection active/keep the data in memory.
Having you explicitly call the .DataBind() method makes it possible to support scenarios at both ends of the spectrum.
DataSource is a property of the BaseDataBoundControl class. DataSourceID is a property of the DataBoundControl class, which inherits from BaseDataBoundControl and did not exist before ASP.NET 2.0.
Since DataBoundControl is explicitly for displaying data in a list or tabular form, and BaseDataBoundControl cannot make that assumption, binding is not automatic when DataSource is set because the type of control may not match the structure of the data.
Of course, this is all just a guess based on the MSDN documentation, so I could be wrong.
I noticed that without using DataBind() that nothing will be displayed in my GridView so I always include it as shown in this section of code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' TableAdapter object.
' Provide communication between this application and the database.
'-----------------------------------------------------------------
Dim suppliersAdapter As New SuppliersTableAdapter
' Get the data from the TableAdapter into the GridView.
'------------------------------------------------------
GridView1.DataSource = suppliersAdapter.GetSuppliers()
' Display the result set from the TableAdapter in the GridView.
'--------------------------------------------------------------
GridView1.DataBind()
End Sub
Please forgive the extra commenting as I'm also still learning ASP.Net as well and the comments will help me learn better "what and why" to use certain statements.
Try this:
if (GridView1.EditIndex == e.Row.RowIndex)
{
TextBox t2 = (TextBox)e.Row.FindControl("TextBox2");
DateTime dt2;
if (DateTime.TryParse(t2.Text, out dt2))
{
t2.Text = dt2.ToString("yyyy-MM-dd");
}
}

asp:DropDownList Error: 'DropDownList1' has a SelectedValue which is invalid because it does not exist in the list of items

I have a asp.net 2.0 web site with numerous asp:DropDownList controls.
The DropDownList control contains the standard info city, state, county etc... info.
In addition to the standard codes the site also has custom codes that the users can configure themselves.
For example a animal dropdown may contain the values Dog, Cat, Fish, ect...
I am popluating the DropDownList from a SQL 2005 table that I created e.g. tblCodes
Everything works great and users are able to add orders using the numerous DropDownList controls to choose items from the list.
The problem occurrs if a user wants to change one of their custom dropdowns. For example a user would like to change the verbage
on a animal type control from Dog to K9. This is where the problem starts.
For all new orders the drop down works fine. When the user retrieved an old order
I get the following error in the C# codebehind
"'DropDownList1' has a SelectedValue which is invalid because it does not exist in the list of items."
What's happening is the old order has a database field value of Dog and the DropDownList no longer has Dog in its list since the user changed it to K9.
Any ideas on a workaround?
Is there a way to make the asp:DropDownList accept items not seeded in its list?
Is there another control I could use?
I solved this exact same problem just two days ago. First, I moved the code that set the SelectedValue to a PreRender handler for the DropDownList. Then, I add logic to first check to see if the value is in the drop down list. If not, I add it.
Here's my code. ddSpecialty is my drop-down list, populated with "specialties" from the database. registration.Specialty is the specialty that the user chose, which may or may not be in the drop down, since that particular specialty may have been deleted since they last chose it.
protected void ddSpecialty_PreRender(object sender, EventArgs e)
{
if (!ddSpecialty.Items.Contains(new ListItem(registration.Specialty)))
ddSpecialty.Items.Add(registration.Specialty);
ddSpecialty.SelectedValue = registration.Specialty;
}
I've become very fond of the following little snippet for setting DropDownList values:
For non-DataBound (eg Items added manually):
ddl.SelectedIndex = ddl.Items.IndexOf(ddl.Items.FindByValue(value));
For DataBound:
ddl.DataBound += (o,e) => ddl.SelectedIndex = ddl.Items.IndexOf(ddl.Items.FindByValue(value));
I sure do wish though that ListControls in general didn't throw errors when you try to set values to somthing that isn't there. At least in Release mode anyways it would have been nice for this to just quietly die.
Your SelectedValue should be a unique id of some sort, that doesn't change. The Text value that gets displayed to the user is something seperate, and can change if necessary without affecting your application, because you associate the id with your Order, not the displayed string value.
I'm not sure it's the same issue, but I had a similar sounding issue with trying to bind a DropDownList that I wanted to contain in a GridView. When I looked around I found a lot of people asking similar questions, but no robust solutions. I did read conflicting reports about whether you could intercept databinding, etc events. I tried most of them but I couldn'f find a way of intercepting or pre-empting the error.
I ended up creating a subclass of the ddl, intercepting the error from there hacking a fix.
Not tidy but it worked for my needs. I put the code up on my blog in case it's of help. link text
Check this:
http://www.codeproject.com/Tips/179184/ASP-dropdownlist-missing-value-error.aspx
Ran into this myself. Oddly, ddl.ClearSelection(); didn't work. Had to use ddl.SelectedValue = null
Also noticed, that this must come AFTER I clear the items from the list ddl.Items.Clear(); which also seems weird. Setting the SelectedValue to null, then clearing the items still threw the error.
Once this is done, re-bind the list and re-select with new value.
Try this:
if (ddl.Items.Contains(new ListItem(selectedFacility)))
ddl.SelectedValue = selectedFacility;
I have made a workaround after having this problem very often. Unfortunate that MS still did not recovered this issue.
Anyway, my workaround is as follows.
1) I bind the data to the ToolTip property of the DropDownList
<asp:DropDownList ID="edtDepartureIDKey" runat="server" CssClass="textbox"
ToolTip='<%# Eval("DepartureIDKey") %>' DataSource="<%# DLL1DataSource() %>" DataTextField="DisplayField" DataValueField="IDKey"
onprerender="edtDepartureIDKey_PreRender">
2) On the prerender event i check the availibilty of the data, and if it is not in the list I simply add it, then set the selectedindex to the data valuei which I saved in ToolTip property
protected void edtDepartureIDKey_PreRender(object sender, EventArgs e)
{
DropDownList ddl = (sender as DropDownList);
if (ddl.Items.FindByValue(ddl.ToolTip) == null)
{
//I am pulling Departure Data through the ID which is saved in ToolTip, and insert it into the 1st row of the DropDownList
TODepartureData v = new TODepartureData(DBSERVER.ConnStrName);
TODeparture d = v.Select(Convert.ToInt32(ddl.ToolTip));
ddl.Items.Insert(0, new ListItem(d.DeptCode, ddl.ToolTip));
}
ddl.Items.FindByValue(ddl.ToolTip).Selected = true;
}

Resources