How to access a ListViewDataItem datakeys - asp.net

I am wondering how to access the datakeys of a ListViewDataItem that is a member of a ListView.
What I find strange is that when debugging it is possible to access the DataKeysContainer which can be seen here
However when attempting to access the DataKeyContainer during coding it is impossible, which can also be seen here
Any advice on how to access the datakeys and their values for a ListViewDataItem would be greatly appreciated.

You can use late binding, i.e. box your loop variable in an Object. This allows you to defer member validation until when the member is accessed at run-time. It is similar to dynamic in c#.
For Each itemObject As Object In lstViewModules.Items
' the DataKeysContainer you were looking for
Dim container = CType(itemObject.DataKeysContainer, Control)
' the same l in your loop
Dim l = CType(itemObject, ListViewDataItem)
' from here on, the rest of your loop code should work
Dim key As DataKey = lstViewModules.DataKeys(l.DataItemIndex)
Dim value = key("name")
Next
Since you know it's a ListView, you could also cast container to ListView for design-time ease.
Note: you must have Option Strict Off for this to work.

Related

strange conversion error when grabbing dropdownlist from form

I've got a .net web form, back end is in vb.net. A few dropdownlists are added to the form, with their controls following the naming convention "ddlCol" + integer, so "ddlCol0", "ddlCol1", etc.
When I try and grab one of these dropdownlists to code against the control, I'm getting a conversion error from string to integer: Conversion from string "ddlCol0" to type 'Integer' is not valid.
Here is the code I'm using:
sDDL = "ddlCol" + iDDLControlCounter.ToString()
Dim ddl As DropDownList = CType(Me.Controls(sDDL), DropDownList)
I admit my vb.net is a bit rusty. Is there something dumb I'm doing wrong?
You have to use & if you want to concat strings in VB.NET (as oposed to C#):
sDDL = "ddlCol" & iDDLControlCounter.ToString()
However, that should not cause your conversion error. Maybe sDDL is declared as Int32.
Anyway, you should set OPTION STRICT to On because i suspect that the error is caused by an implicit conversion. Late binding should be avoided if possible.
See: Option Strict Statement
Edit: Controls.Controls is a collection which has an indexer. So you can get the first control via Me.Controls(0). So that is the main reason for the exception because sDDL is a String. But thisknowledge does not help further to find your DropDownLists.
So read more ...
Could the fact that these dropdowns are in the header row of a
gridview make a difference? I was of the mindset that a control is a
control is a control, but maybe there are rules I don't know about
with this?
No, that doesn't make a difference. Then you would get a NullReferenceException when you try to use your DropDownList.
But you need to use Control.FindControl to get a reference of a control via it's ID.
If it is really in a header row of a GridView you have to use FindControl on the correct NamingContainer which is the HeaderRow:
If grid.HeaderRow IsNot Nothing Then
Dim ddl = DirectCast(grid.HeaderRow.FindControl("ddlCol" & iDDLControlCounter), DropDownList)
End If
The issue isn't with your string concatenation, but rather with your assumption about how the Controls collection works; it only takes a numeric value.
Instead, I believe the method you need to be using is FindControl:
sDDL = "ddlCol" & iDDLControlCounter.ToString()
Dim ddl As DropDownList = CType(Me.FindControl(sDDL), DropDownList)
Note that if you know the precise type, DirectCast is (fractionally) faster than CType.
The actual error in the OP is that Me.Controls() only takes an integer as an arguement, not a string.

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

Databinding problems with postback

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.

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");
}
}

Resources