Failed to get datakey value on ItemDataBound - asp.net

I searched the web and found the example, but I still get it right. I have a multiple datakey on datagrid and I get index out of range error. Would someone help me out. Thanks in advance.
There is my aspx
<asp:datagrid id="dgrTrans" runat="server" autogeneratecolumns="False" gridlines="Both" borderstyle="NotSet" cssclass="dgrd w97p" cellpadding="2"
horizontalalign="center" datakeyNames="ID, Description" >
There is my code to retrieve the value on ItemDataBound
Select Case e.Item.ItemType
Case ListItemType.Item, ListItemType.AlternatingItem, ListItemType.EditItem
Dim currentItem As DataGridItem = CType(e.Item, DataGridItem)
Dim dKey As DataKey = Me.dgrTranscripts.DataKeys(currentItem.ItemIndex)
Dim cn As String = dKey("Description").ToString()
end select
I also tried the following code, but I get the "Conversion from string "" to type 'Integer' is not valid." error
Dim cn As String = dgrTranscripts.DataKeys(e.Item.DataItem("Description)).ToString

The index in your case can only be 0 or 1 because there are only two of the keys. You are trying to retreive DataKey with index that equals to currentitem.ItemIndex, which can be as big as many items you have, therefore you are getting an out of range error. You need to use either 0 or 1 as a DataKey index. I cannot give you a definitive answer because you did not specify what exactly you are trying to do...
EDIT:
Instead of ItemDataBound, use DataBound event and loop through every row to get its value.
Here is an example with GridView
Protected Sub GridView1_RowDataBound(sender As Object, e As EventArgs) Handles GridView1.DataBound
For Each row As GridViewRow In GridView1.Rows
Dim cn = GridView1.DataKeys(row.RowIndex).Value
Next
End Sub
On ItemDataBound, DataGrid does not know that there are keys yet. They will be assigned on DataBound

Related

How to htmlencode body but not heading of gridview

I have a GridView. I want the content of the cells, the actual detail data, to be html-encoded. But I want to keep the raw HTML in the heading.
That is, the heading in this case is the name of a language and the ISO code, for example "English (EN)". I want the name and code on separate lines, so I'm specifying the heading as "English<br/>(EN)".
But for the content, I want to see any HTML. If it says "<p>foobar</p>" I want the p-tags to display.
If I say "htmlencode=false", then the heading doesn't get encoded, but neither does the data.
Is there any way to say, html-encode the data, but not the heading?
If it matters, I'm building the columns in code rather than with tags in the ASP file, depending on which languages I find in my data. So here's how I'm creating the column:
For Each row As DataRow In ds.Tables(0).Rows
Dim iso2 = row("language")
Dim name = row("name")
... other code ...
Dim head = String.Format("{0}<br/>({1})", name, iso2)
gvSnippets.Columns.Add(New BoundField With {.HeaderText = head, .DataField = iso2, .HtmlEncode = False})
... other code ...
End For
(My first draft did not set HtmlEncode.)
Curious observation: In my first couple of test runs, the data did not include any HTML or entities, and the HTML in the heading was NOT encoded, I got the line break and not "<br/>". Then I did a test where there were entities in the data and the entities got html-encoded ... and so did the header. So like, ASP is apparently saying that by default, if the data has no HTML but the heading does, then don't HTML-encode the heading. But if the data has HTML, then HTML-encode both the data and the heading. So like, it's deciding dynamically whether to html-encode the heading or not.
In reply to #fnostro, here's the markup for the GridView:
<asp:GridView ID="gvSnippets" runat="server" AutoGenerateColumns="False" SkinID="skin3" EmptyDataText="No records found" Visible="false">
</asp:GridView>
There is no <Columns> markup. I build the columns completely in code. I haven't tested if the same behavior occurs in what I assume is the more normal case where you specify columns with markup.
Add these lines of code to the beginning of your loop
row("language") = HttpUtility.HtmlEncode(dr("language"))
row("name") = HttpUtility.HtmlEncode(dr("name"))
Based on my reading of the information provided in your post I have the following suggestions:
You should isolate data formatting from raw data.
Your DataTable is the data source for the GridView. The DataTable should contain only raw data, completely unformatted and unadulterated.
The DataTable gets bound to the Gridview by setting the GridView DataSource and making a call to DataBind(). Calling gvSnippets.Databind() will trigger all the databinding events associated with Gridviews which will allow you to:
Manage simple formatting and binding in the GridView Columns in the aspx markup.
Handle more complicated formatting in the code behind for specific GridView events like the RowDataBound event.
Regarding the GridView Header: HeaderText appears once at the top of the Gridview and is stored internally in GridView.HeaderRow, not on a per row basis even though every bound field has a HeaderText property.
Example excerpt gvSnippets Markup per your update:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False" SkinID="skin3"
EmptyDataText="No records found" Visible="false">
</asp:GridView>
Then you can do things like this in the Code Behind:
Public Class __JunkWebForm1
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Fill Dataset first, then:
gvSnippets.DataSource = ds.Tables(0)
' Add Columns...
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
. . .
' This will trigger data binding events
gvSnippets.DataBind();
End Sub
Private Property HeaderTextLanguage As String
Private Property HeaderTextLanguageIso As String
Dim NeedHeaderText As Boolean = False
Private Sub gvSnippets_DataBinding(sender As Object, e As EventArgs) Handles gvSnippets.DataBinding
NeedHeaderText = True
End Sub
Private Sub gvSnippets_DataBound(sender As Object, e As EventArgs) Handles gvSnippets.DataBound
Dim this As GridView = sender
this.Columns(0).HeaderText = String.Format("{0}<br>({1})", HeaderTextLanguage, HeaderTextLanguageIso)
End Sub
Private Sub gvSnippets_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles gvSnippets.RowDataBound
Dim this As GridView = sender
Dim drv As DataRowView = e.Row.DataItem
If e.Row.RowType = DataControlRowType.DataRow Then
If NeedHeaderText Then
HeaderTextLanguage = drv("language")
HeaderTextLanguageIso = drv("iso")
NeedHeaderText = False
End If
e.Row.Cells(0).Text = Server.HtmlEncode(drv("Somefieldnameofyours"))
End If
End Sub
End Class
Addendum Dealing with Dynamic cells
For a GridView defined as:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False">
</asp:GridView>
I don't know how you're populating your ds but I'm using a SqlDataSource to fill a table in the Code Behind. Note there is no GridView DataSourceID above:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
SelectCommand="select top 10 user_id, user_name, user_logon, 'English' as language, 'en' as iso from tbl_users"
ConnectionString='<%$ ConnectionStrings:MyConnectionString %>'>
</asp:SqlDataSource>
Then in the Code Behind I can do this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
GetDataSetAndPopulate(gvSnippets)
End Sub
Private Sub GetDataSetAndPopulate(gv As GridView)
Dim view As DataView = SqlDataSource1.Select(DataSourceSelectArguments.Empty)
Dim table As DataTable = view.ToTable()
Dim ds As DataSet = New DataSet()
ds.Tables.Add(table)
For Each dc As DataColumn In ds.Tables(0).Columns
Dim field As New BoundField
field.DataField = dc.ColumnName
field.HeaderText = dc.ColumnName
gv.Columns.Add(field)
Next
gv.DataSource = ds
gv.DataBind()
End Sub
And now the Gridview is populated dynamically and you can still handle all the formatting during the DataBinding events as needed.
Here's a solution I came up with that works in my particular case.
I am building the data to populate the GridView in code. I create a DataTable, add columns to it, and then populate those columns. Like:
dim dslang=GetListOfLanguages()
dim dtgv=new DataTable()
for each row as DataRow in dslang.tables(0).rows
dim language_name=row("name")
dim language_code=row("code")
dtgv.columns.add(String.format("{0}<br/>({1})",language_name, language_code)
end for
... bunch of other stuff ...
... inside a loop that reads the data ...
dim outrow=dtgv.NewRow()
... get data for a specific language ...
outrow(language_code)=text
... later ...
my_gridview.datasource=dtgv
my_gridview.databind()
So my solution:
When creating the GridView, set HtmlEncode=false. This makes the header display correctly.
When populating the data, say
outrow(language_code)=HttpUtility.HtmlEncode(text)
So I add the data to the datatable that will ultimately be used as the datasource for the gridview already encoded, so I don't need to rely on the Gridview encoding it.
I consider this a mediocre solution, as it only works because I am populating GridView from a DataTable that I build with code. If I was populating the GridView directly from a DataSource specified in markup, or from a DataSet created by a database query, there'd be no convenient place to put the HttpUtility.HtmlEncode call.

Check Checkboxes in a gridview based on a non-boolean value from a stored procedure

I am trying to check checkboxes on certain rows of a gridview based off of a selection from a dropdown. I have a gridview where the first column is a checkbox (cb_pain), and the second column is an ID (DrugID). The user makes a selection from the dropdown list, this fires a stored procedure. The stored procedure returns all of the DrugID's that need to be checked. I'm able to pull the DrugID from the data like this: dt.Rows(0)("pain1").ToString() That tells me DrugID for a row that needs to be checked.
Basically I would like to check the checkbox on the row where DrugID = dt.Rows(0)("pain1").ToString()
I think this needs to be done on the selectedindexchange of the dropdown.
My gridview looks like this: (sorry for the dot's, I couldn't figure out how to tab)
cb_pain........DrugID...............Field1
x.................3...................other data
x.................23.................other data
x.................24.................other data
x.................37.................other data
How can I use this to check the checkbox on the row that has the right DrugID?
I've tried a couple of different DirectCast things, but no success. Can anyone point me in the right direction?
Thanks
Another option that I tend to prefer is to make use of the RowDataBound event of the GridView. As each row gets data bound to it, this event gets called. What you would do is find the DrugID control and check its value. If it is what you want, do something.
First, let your GridView know of the event.
<asp:GridView ID="gvDrugs" runat="server" OnRowDataBound="gvDrugs_RowDataBound">
</asp:GridView>
Then handle the event. If the DrugID is 23, find the CheckBox and check it.
Protected Sub gvDrugs_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Dim lblDrugID As Label = e.Row.FindControl("lblDrugID")
If lblDrugID.Text = "23" Then
Dim cbPain As CheckBox = e.Row.FindControl("cbPain")
cbPain.Checked = True
End If
End If
End Sub
**Note here that I am assuming the types of controls to be labels and checkboxes. You have not shown your markup, so this is the best I can do.
After your edit, I would probably agree with you. This is something that could be done in the SelectedIndexChanged event of the DropDownList. Get the list of DrugIDs that need to be checked and iterate each row of your GridView, checking those that match.
Protected Sub dropdown_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim uniqueDrugIDs As HashSet(Of String) = New HashSet(Of String)()
' Here we assume dt is some sort of global variable as your question above implies
For Each dr As DataRow In dt.Rows
uniqueDrugIDs.Add(dr("drugID").ToString())
Next
For Each gvRow As GridViewRow In gvDrugs.Rows
Dim lblDrugID As Label = gvRow.FindControl("lblDrugID")
If (uniqueDrugIDs.contains(lblDrugID.Text)) Then
Dim cbPain As CheckBox = gvRow.FindControl("cbPain")
cbPain.Checked = True
End If
Next
End Sub
You can go for that kind of loop that will go through all your rows :
for (int x = 0; x < NameOfYourGrid.Rows.Count; x++)
{
GridViewRow row = (GridViewRow)NameOfYourGrid.Rows[x];
if(row("pain1").ToString() == "23")
CheckBox chk = (CheckBox)row.Cells[0].FindControl("The_Name_Of_The_Control");
}
Or, if you wich to do it on the binding ;
In the .aspx file put an explicit property on your check box :
<asp:CheckBox ID="chkInside" runat="server" Checked='<%# CheckForChecked(Container.DataItem as Whatever_the_object_is) %>' ></asp:CheckBox>
And link that event in the code behind :
protected bool CheckForChecked(Whatever_the_object_is OBJ)
{
try
{
return (bool) OBJ.Boolean_value;
// here you are supposed to have a compete Object
// OBJ.ID is suposed to be the rigth id
}
catch
{
return false;
}
}
Then the checkBox will be checked if the value is true on the gridview binding. You won't need the ID if your binding is done correctly.
I took j-f's response and got it to work when place in the right spot. At the end of filling the dataset I added this code:
Dim row As GridViewRow
For Each row In gv_pain.Rows
If row.RowType = DataControlRowType.DataRow Then
Dim lblDrugID As Label = row.FindControl("lbl_DrugID")
If lblDrugID.Text = dt.Rows(0)("pain1").ToString() Then
Dim cbPain As CheckBox = row.FindControl("CheckBoxPain")
cbPain.Checked = True
End If
End If
Next
This goes through my rows and check the correct ones.

Listview - Capture text from dynamically created textbox on Update

I have a database and a bound Listview with dynamically created controls. I have been attempting to find a way to capture the new value using markup as
<asp:ListView ID="ListView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="ID" OnItemUpdating="ListView1_ItemUpdating">
and code behind as
Protected Sub ListView1_ItemUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewUpdateEventArgs) Handles ListView1.ItemUpdating
Dim capString As String = String.Empty
capString = e.NewValues
End Sub
This fails with the error Unable to cast object of type 'System.Collections.Specialized.OrderedDictionary' to type 'System.String'
I'm hoping someone could help me identify either a better way to accomplish capturing this data, or if it is possible to cast this data and how to do it in code.
Try
Dim capString As String = String.Empty
capString = e.NewValues(1) As String
Substitute 1 with an appropriate index for your capString.

visual basic asp.net dropDownList not populating with values

I have an asp.net Visual Basic web site, and I am using a stored procedure to get values to populate a drop-down list.
However, I have two issues. If I put the method to fill the drop-down list within my If Not IsPostBack statement in the page load event, then the whole list is filled with items saying 'System.Data.DataViewRow' instead of the actual values. If I put it outside this 'if' statement but still in my page load event, I get the correct values, but no matter what I select, when I leave the drop-down it reverts back to the top item instead of the selected item.
What am I doing wrong??
Edit: I have now added the DataTextField as suggested by Nic and put the method inside my 'If Not IsPostBack' event as suggested by Lee Bailey. However, my drop-down is still showing all values as 'System.Data.DataViewRow' so I don't know if Lee's solution has worked or not! Any other ideas on the issue of showing the values! I have updated my code below to reflect the changes.
The visual basic:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
bodyPanel.Visible = False
drpRegion_fill()
End If
End Sub
Protected Sub drpRegion_fill()
Dim sqlConn As SqlConnection
sqlConn = New SqlConnection
sqlConn.ConnectionString = ConfigurationManager.ConnectionStrings("ConnString").ConnectionString
Dim drpRegionCmd As SqlCommand
drpRegionCmd = New SqlCommand("getRegionName", sqlConn)
drpRegionCmd.CommandType = CommandType.StoredProcedure
Dim drpRegionAdp As SqlDataAdapter
drpRegionAdp = New SqlDataAdapter(drpRegionCmd)
Dim drpRegionDs As DataSet
drpRegionDs = New DataSet
sqlConn.Open()
drpRegionAdp.Fill(drpRegionDs)
With drpRegion
.DataSource = drpRegionDs
.DataBind()
.DataValueField = "regionName"
.DataTextField = "regionName"
.SelectedIndex = 0
End With
sqlConn.Close()
End Sub
The markup:
<asp:Panel ID="panelRegion" runat="server" Height="160px" Width="71%" CssClass="inlineBlock">
<h2>Region:</h2>
<asp:dropDownList runat="server" AutoPostBack="true" ID="drpRegion" />
</asp:Panel>
The SQL procedure returns a two-column dataset with 'regionID' as column 1, and 'regionName' as column2.
I've spent about two days on this now, trying various things and reading all the books I can! I have never had this issue in C# when I have performed the same operation and I cannot for the life of me think what I've missed in the VB...
It does not look as though you are setting the DataTextField property of your DropDownList:
With drpRegion
.DataSource = drpRegionDs
.DataValueField = "regionName"
.DataTextField = "fieldToDisplayAsText"
.SelectedIndex = 0
.DataBind()
End With
source:
MSDN DataTextField
It's reverting back to the original value because you are re-binding the values to the dropdown after a postback. Changing the selected item is triggering a postback because you have the AutoPostBack property set to true. I would call drpRegion_fill() only if it's not a postback. Setting the DataTextField as Ric mentioned should solve the problem regarding 'System.Data.DataViewRow'

Unable get Label.text in ASP.Net Repeater using FindControl

I can access the text within a textbox within my repeater, but I am unable to pull the text value from a label within my repeater.
The repeater is populated from a datatable with row(x) being filled by sqlreader(x), I don't know if that makes a difference.
I cannot use javascript for this. I need to access the label value from the codebehind.
<asp:label id="weiLabel" runat="server">
<%#DataBinder.Eval(Container, "DataItem.weiLabel")%>
</asp:label>
is the markup
I can access a textbox on the same row using:
featTable.Controls(1).Controls(1).FindControl("costText")
and retrieve the textbox.text, but using the same statement for the label gives me {text=""}.
I have verified that the clientID of control that is returned with findcontrol is correct (featTable__ctl1_weiLabel)
Thanks for any help
Can you try declaring your label like this:
<asp:label id="weiLabel" runat="server" Text='<%#DataBinder.Eval(Container, "DataItem.weiLabel")%>' / >
You can also try putting in the value into your label from the code behind using the databound method. I find it a bit easier to debug and cleaner then putting it in the html
Private Sub repPoliList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repPoliList.ItemDataBound
If (e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim dr As DataRowView = CType(e.Row.DataItem, DataRowView)
Dim weiLabel As System.Web.UI.WebControls.Label= CType(e.Item.FindControl("weiLabel"), System.Web.UI.WebControls.Label)
weiLabel.text= dr("ColumnFromDatabase").toString
End If
End Sub

Resources