Obtaining the id of the row being edited from a GridView - asp.net

My end goal is for my program to work like this:
Press Edit button on GridView
RowEditing event triggers, creating a new window
New window with a DetailsView displays the full information.
I got all that to work, however, the DetailsView only displays the content from the first entry in the database table, instead of the entry which I pressed the edit button on.
So my question is, how can I tell the program which row is being edited on the GridView, and get the information(like the key) to make a new select statement on the DetailsView?
TablePage.aspx(with only the relevant code)
<asp:GridView ID="GridView1" runat="server" OnRowEditing="RowEditing"
AutoGenerateEditButton="True">
<Columns>
<asp:BoundField DataField="name" HeaderText="name" SortExpression="name" />
</Columns>
</asp:GridView>
TablePage.aspx.vb(again, only relevant code)
Protected Sub RowEditing(ByVal sender As Object, ByVal e As GridViewEditEventArgs)
Dim url As String = "/tables/edit.aspx"
Dim s As String = "window.open('" & url + "', 'popup_window', 'width=300,height=300s,left=100,top=100,resizable=yes');"
ClientScript.RegisterStartupScript(Me.GetType(), "script", s, True)
End Sub

You can use e.NewEditIndex from RowEditing function to get the current row edited in GridView, obtaining that you can access the row and get any cell text from the row.
Please try this:
Protected Sub RowEditing(ByVal sender As Object, ByVal e As GridViewEditEventArgs)
'First, get the current RowIndex
Dim selectedRowIndex As Integer = e.NewEditIndex
'Then get any information you need from the row, for example the name of the first cell
Dim selectedItemText As String = GridView1.Rows(selectedRowIndex).Cells(1).Text
'Now you can store the information in session to use it in other page
Session("EditParameter") = selectedItemText
Dim url As String = "/tables/edit.aspx"
Dim s As String = "window.open('" & url + "', 'popup_window', 'width=300,height=300s,left=100,top=100,resizable=yes');"
ClientScript.RegisterStartupScript(Me.GetType(), "script", s, True)
End Sub
Of course you can add another hidden bound field that contains the record number and then access it instead of the name.
Hope this helps.

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.

How to delete a record using LinkButton and refresh the table?

I have an HTML table to display a set of records from database after user entered a patient code or during Page Load when the patient code is passed through Querystring. Each data row has a Delete link button which is created dynamically.
I'd like to have the table refreshed after deletion. However, whenever the link button is clicked, it will postback and all the records will be wiped out unless I reload the data. If I reload the data again after deletion, there will be 2 times data fetching and the table rows will be doubled.
The first 2 rows of the tables are defined on the ASPX for easier styling purpose. If I cleared the table when fetching the data, the first rows will be wiped out as well. I have another 8 tables on different pages created in the same manner, hence I'd rather find other solution rather than defining and styling the rows from code behind.
Any help will be greatly appreciated.
The codes I use are as follow:
ASPX
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblPatientCode" runat="server" Text="Patient Code : "></asp:Label>
<asp:TextBox ID="txtPatientCode" runat="server"></asp:TextBox>
<asp:Button ID="btnFind" runat="server" name="Date" Text="Find" Width="90" CssClass="ButtonNormal" />
<br /><br />
<table id="tblDoctorInformation" class="PatientDetailsTable" runat="server">
<tr><td colspan="2">DOCTOR INFORMATION</td></tr>
<tr>
<td>Doctor In Charge</td>
<td> </td>
</tr>
</table>
</div>
</form>
</body>
Code Behind:
Protected PM As New PatientManagement.Common
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
GetPatient()
Else
If txtPatientCode.Text <> "" Then
GetPatient()
End If
End If
End Sub
Private Sub GetPatient()
Dim sPatientCode As String = ""
If IsPostBack Then
sPatientCode = Trim(txtPatientCode.Text)
Else
sPatientCode = Trim(Page.Request.QueryString("PatientCode"))
End If
If sPatientCode <> "" Then
Dim dr As SqlDataReader
dr = PM.ExecuteReader("SELECT LOGID,DOCTORNAME FROM PMV_DOCTORINCHARGE WHERE PATIENTCODE='" & sPatientCode & "'")
If dr.HasRows Then
Do While dr.Read
Dim tRow As New HtmlTableRow
Dim tCellDoctorName As New HtmlTableCell
Dim tCellModifyLink As New HtmlTableCell
Dim lb As New LinkButton
'Doctor Name
tCellDoctorName.InnerHtml = PM.GetString_TableCell(dr("DoctorName"))
tRow.Cells.Add(tCellDoctorName)
'Delete links
lb.Text = "Delete"
lb.Attributes.Add("AutoPostBack", False)
lb.CommandArgument = dr("LogID").ToString()
AddHandler lb.Click, AddressOf DeleteRecord
tCellModifyLink.Controls.Add(lb)
tCellModifyLink.Align = "Center"
tRow.Cells.Add(tCellModifyLink)
tblDoctorInformation.Rows.Add(tRow)
Loop
End If
End If
End Sub
Private Sub btnFind_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnFind.Click
GetPatient()
End Sub
Protected Sub DeleteRecord(ByVal sender As Object, ByVal e As System.EventArgs)
Dim lbNew As New LinkButton
Dim sResult As String = ""
Dim bResult As Boolean
lbNew = sender
bResult = PM.DeleteMultiRecord(lbNew.CommandArgument, lbNew.CommandName, Session.Item("UserID"), sResult)
If Not bResult Then
MsgBox(sResult, MsgBoxStyle.OkOnly, "Deletion was not successful")
Else
GetPatient()
End If
End Sub
Before Deleting a record, try to place a PatientID in Textbox and Delete the record, then Table will Display the records matching with the TextBox input.
The Problem is with PageLoad() method, if the TextBox is Empty And It is a Postback, then GetPatient() method will not be called. So try to modify the PageLoad() Method.
Better to remove IsPostBack condition in PageLoad().
I have searched around and it seems dynamic handler is best to be declared during PageInit. For my case, as the link button control needs to carry some values on the respective data row, it seems I can't avoid calling GetPatient to create controls.
To move on with the issue, I did a workaround by using Javascript. I created a hidLogID hidden field on ASPX page. I changed the link button to an <a> control and call a javascript function passing the dr("LogID"). The javascript function will assign the value to hidLogID and submit the page. PageLoad will call DeleteRecord()when hidLogID value is not blank. The value for hidLogID will be cleared on DeleteRecord().
Thank you for all the help!

Filling the dropdown from sql bug

Good afternoon people have been trying to fill a dropdown using a sql command, until so good, when I click on the dropdown it shows all the items, but when I try to click on an item in the dropdown it always returns the first item in the dropdown .... follows the codes, what i want to do is get the selected value and item from the dropdown and save it on a label for future use.
I appreciate all the support possible,
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
utilizador.Text = Me.Context.User.Identity.Name
If (Not Page.IsPostBack) Then
escolhePerfil()
End If
'DropDownPerfil.DataBind()
lbperfil2.Text = DropDownPerfil.SelectedItem.Text
lbnome.Text = DropDownPerfil.SelectedValue
End Sub
Function escolhePerfil() As Boolean
Dim connstring As String = "Data Source=10.2.24.17;Persist Security Info=True;User ID=sa;Password=Pr0dUn1C0$qL;database=ePrimavera"
Dim SQLData As New System.Data.SqlClient.SqlConnection(connstring)
Dim cmdSelect As New System.Data.SqlClient.SqlCommand("SELECT u.WindowsUser,u.Email ,g.Description, u.Login FROM [ePrimavera].[dbo].[PLT_Users] as u,[ePrimavera].[dbo].[PLT_UserGroups] as ug, [ePrimavera].[dbo].[PLT_Groups] as g where u.ID = ug.UserID And ug.GroupID = g.ID and u.WindowsUser like 'bancounico\" & utilizador.Text & "'", SQLData)
SQLData.Open()
Dim dtrReader As System.Data.SqlClient.SqlDataReader = cmdSelect.ExecuteReader()
If dtrReader.HasRows Then
DropDownPerfil.DataValueField = "Login"
DropDownPerfil.DataTextField = "Description"
DropDownPerfil.DataSource = dtrReader
DropDownPerfil.DataBind()
End If
SQLData.Close()
Return True
End Function
.aspx
<asp:DropDownList ID="DropDownPerfil" runat="server"
Height="16px" Width="202px" CssClass="DropBorderColor">
</asp:DropDownList>
try the following code:
Protected Sub DropDownPerfil_SelectedIndexChanged(sender As Object, e As EventArgs)
lbperfil2.Text = DropDownPerfil.SelectedItem.Text
lbnome.Text = DropDownPerfil.SelectedValue
End Sub
The problem is that you are trying to get the value "too early". The value is not valid in the Page_Load, since the control's OnLoad event fired after the Page's OnLoad (Page_Load) event.
The way, what the others wrote the correct, since the Event handlig section (inclueding control's onchanged event) will process after the Load section.
For more details check the offical ASP.NET life cycle site in the MSDN
The likely reason is that some (if not all) the values you are assigning to the DropDownList for Login are occur more than once.
When you then select an item in the DDL, if the value of that item occurs more than once, the selected index will highlight the first instance. To test this, comment out DropDownPerfil.DataValueField = "Login"
I am sure upon selection, it will highlight the correct item you intended on selecting.

Put multiple data in one cell ASP.NET, VB.NET

How can i put multiple data in one datagrid cell? For example, a user can be a member of different organisations.
But i dont want the table to create a new datagrid row for the same user that is a member of multiple organisations.
This is how the code looks like right now:
Dim dSourceMemberOf As New SqlDataAdapter("QUERY", dbConn)
This fills the datagrid, but for some users there are more then 5 rows. So i'd like to put for one particular column all data in one cell.
How can this be done?
Edit:
This is how it woud look like right now:
username memberof
user1 dft
user1 kbo
user2 test
And this is how i want it to looke like:
username memberof
user1 dft
kbo
user2 test
I think the best solution is to group the data using a Linq query, then bind the grouped data to a gridview:
So the GridView would look like this:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="UserName" />
<asp:TemplateField>
<ItemTemplate>
<asp:ListBox ID="lst" runat="server" DataSource="<%# Container.DataItem.MemberOfGrouped %>" DataTextField="MemberOf"></asp:ListBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and here is some example code to populate it:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim UngroupedData As List(Of YourData) = New List(Of YourData)()
Dim item1 As YourData = New YourData()
item1.MemberOf = "dft"
item1.UserName = "user1"
UngroupedData.Add(item1)
Dim item2 As YourData = New YourData()
item2.MemberOf = "kbo"
item2.UserName = "user1"
UngroupedData.Add(item2)
Dim grouped = From u In UngroupedData Group u By Key = u.UserName Into Group _
Select UserName = Key, MemberOfGrouped = Group.ToList()
GridView1.DataSource = grouped
GridView1.DataBind()
End Sub
Public Class YourData
Private _userName As String
Public Property UserName() As String
Get
Return _userName
End Get
Set(ByVal value As String)
_userName = value
End Set
End Property
Private _memberOf As String
Public Property MemberOf() As String
Get
Return _memberOf
End Get
Set(ByVal value As String)
_memberOf = value
End Set
End Property
End Class
Sorry, I think on paint was wrong, concept was right though - looks like you need to do it on the databind event event... more here (first three links will show you how to do what you require I think) - you could look at the row you are on, get the previous row, then decide what to do with a cell in your current row based on the prev row
You could use a table inside the GridView-Cell (or a Listbox like Ross Scott suggested).
<ItemTemplate>
<asp:table id="TblUserGroups" runat="server" Height="100%" Width="100%" CellSpacing="0" CellPadding="0">
</asp:table>
</ItemTemplate>
and in RowDataBound of gridview(ItemDataBound of Datagrid works similar):
Protected Sub Gridview1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles Gridview1.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
Dim drUser As DataRowView = DirectCast(e.Row.DataItem, DataRowView)
Dim TblUserGroups As Table = DirectCast(e.Row.FindControl("TblUserGroups"), Table)
Dim userID as int32 = Ctype(drUser(0), Int32)
Dim tblGroups as DataTable = getTblUserGroups(userID) 'write a function that returns the Groups of a given User f.e. as DataTable'
If tblGroups.Rows.Count = 0 Then
Dim tr As New TableRow
Dim td As New TableCell
Dim LblNoGroup As New Label
LblNoGroup .Text = "no user-group"
td.CssClass = "UserWithoutGroup"
td.Controls.Add(LblNoGroup)
tr.Cells.Add(td)
TblUserGroups.Rows.Add(tr)
Else
For Each groupRow As DataRow In tblGroups.Rows
Dim tr As New TableRow
Dim td As New TableCell
Dim LblGroup As New Label
LblGroup.Text = groupRow("GroupName").ToString 'The groups name column'
td.Controls.Add(LblGroup)
tr.Cells.Add(td)
TblUserGroups.Rows.Add(tr)
Next
End If
End Select
End Sub
I think this is the most flexible way.

Pass values from QueryString parameter in GridView hyperlink

I'm using GridView with a HyperLink column, and I want to do the following:
<asp:HyperLinkField DataNavigateUrlFields="DID"
DataNavigateUrlFormatString="~/student/group/document/Body.aspx?DID={0}&GN={QueryString("GN")}" HeaderText="View Document" Text="view" />
How I can retrieve the value of GN from the QueryString parameter and add it to the HyperLink column ?
How important is it to you to do this in the markup? I'm fairly sure you can't do this in the markup with the DataNavigateUrlFields and DataNavigateUrlFormatString, but you can do it in code in the RowDataBound event:
Private Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
Dim link As HyperLink
Dim row As DataRow
If e.Row.RowType = DataControlRowType.DataRow Then
'Get the row we are binding to
row = CType(e.Row.DataItem, DataRowView).Row
'Find the hyperlink control we want to set the link for
link = CType(e.Row.Controls(1).Controls(0), HyperLink)
'Check if the querystring we want to include is missing or empty
If Request.QueryString("GN") Is Nothing Or Request.QueryString("GN") Is String.Empty Then
'If there is no querystring then we can only bind to the DID
link.NavigateUrl = "~/student/group/document/Body.aspx?DID=" & row.Item("DID").ToString
Else
'The querystring element is present so include it in the link
link.NavigateUrl = "~/student/group/document/Body.aspx?DID=" & row.Item("DID").ToString & "&GN=" & Request.QueryString("GN").ToString
End If
End If
End Sub

Resources