I am building a custom ASP.Net gridview server control (not user control) with a custom pager. The custom pager contains a dynamically added dropdownlist which changes the gridview's pagesize (items per page). The dropdownlist is added in the RenderChildren method which I am overriding in the server control's code.
I am basically cloning the default pager row and adding the dropdownlist into the row, then adding the cloned pager row to the gridview and destroying the original.
The problem is that even though the dropdownlist's autopostback property is set to true and I am adding an event handler for it, I cannot get the event to fire when changing the selectedindex. I have tried several different approaches with no luck.
I believe the problem is that the dropdownlist has not yet been rendered by the time the selectedindex is changed in the calling page. But I don't know enough about server controls and the page lifecycle to figure out how to resolve it.
Here is an abbreviated version of the code:
Public Class CustomGridView
Inherits System.Web.UI.WebControls.GridView
Implements IPostBackEventHandler
Protected WithEvents mctlDropdownListPageSize As New DropDownList
Public Event PageSizeChanged(ByVal sender As Object, ByVal e As EventArgs)
Protected Sub dropdownListPageSize_Click(ByVal sender As Object, ByVal e As EventArgs)
RaiseEvent PageSizeChanged(Me.mctlDropdownListPageSize, e)
End Sub
'This sets up paging using the pageddatasource
Protected Overrides Sub InitializePager(row As GridViewRow, columnSpan As Integer, pagedDataSource As PagedDataSource)
pagedDataSource.AllowPaging = True
pagedDataSource.AllowCustomPaging = True
pagedDataSource.VirtualCount = TotalRows
pagedDataSource.CurrentPageIndex = CurrentPageIndex
Me.PageIndex = CurrentPageIndex
MyBase.InitializePager(row, columnSpan, pagedDataSource)
End Sub
Protected Overrides Sub RenderChildren(writer As HtmlTextWriter)
Try
If Me.Controls.Count = 0 Then
' nothing to render, use default
MyBase.RenderChildren(writer)
Return
End If
' select the header row in the grid
Dim gridViewTable As WebControl = DirectCast(Me.Controls(0), WebControl) ' the table
' the table->row(0) if no top paging, otherwise row(1)
Dim pagerRow As GridViewRow = gridViewTable.Controls(0)
'Get the pager cell -- we want only one cell containing all the elements
Dim pagerContainerCell As TableCell = pagerRow.Cells(0)
'Create the new table which will contain the title, results summary and pager
Dim newPagerTable As New Table
newPagerTable.CellPadding = 0
newPagerTable.CellSpacing = 0
newPagerTable.CssClass = "section_headers gridpagertable"
'Create a table row to contain the new cells
Dim newPagerRow As New TableRow
newPagerRow.CssClass = "pagerrow"
'Create 2 cells to hold the new controls + pager
Dim resultsCell As New TableCell
resultsCell.CssClass = "results"
Dim pagerCell As New TableCell
pagerCell.CssClass = "pager"
If Me.AllowPaging Then
'Add the pagesize dropdown and results summary text
'Create the results label
Dim mctlResultsLabelText As New LiteralControl
mctlResultsLabelText.Text = "<span>Results per page </span>"
'Create the pagesize dropdown container div
Dim mctlResultsDropdownListContainerDiv As New HtmlControls.HtmlGenericControl("div")
mctlResultsDropdownListContainerDiv.Attributes("class") = "dropdown_select"
Dim mctlResultsDropdownListLabel As New HtmlControls.HtmlGenericControl("label")
'Create the pagesize dropdownlist
mctlDropdownListPageSize.ID = "dropDownListPageSize"
mctlDropdownListPageSize.Items.Add(New ListItem("100", "100"))
mctlDropdownListPageSize.Items.Add(New ListItem("200", "200"))
mctlDropdownListPageSize.Items.Add(New ListItem("300", "300"))
mctlDropdownListPageSize.AutoPostBack = True
mctlDropdownListPageSize.EnableViewState = True
AddHandler mctlDropdownListPageSize.SelectedIndexChanged, AddressOf Me.dropdownListPageSize_Click
'Add the dropdown tot he dropdown container div
mctlResultsDropdownListLabel.Controls.Add(mctlDropdownListPageSize)
mctlResultsDropdownListContainerDiv.Controls.Add(mctlResultsDropdownListLabel)
'Add the results drpdown label
resultsCell.Controls.Add(mctlResultsLabelText)
'Add the pagesize dropdown
resultsCell.Controls.Add(mctlResultsDropdownListContainerDiv)
'Add the cell to the row
newPagerRow.Controls.Add(resultsCell)
End If
'Add the pager control and action icons
Dim mctlPagerContainerDiv As New HtmlControls.HtmlGenericControl("div")
mctlPagerContainerDiv.Attributes.Add("class", "pagination")
'Add the div to the pager cell
pagerCell.Controls.Add(mctlPagerContainerDiv)
If Me.AllowPaging Then
'Get the existing pager container table with the pager buttons
Dim tblPager As Table
tblPager = pagerContainerCell.Controls(0)
tblPager.CellPadding = 0
'Add the pager to the cell
pagerCell.Controls.Add(tblPager)
End If
'Add the cell to the row
newPagerRow.Controls.Add(pagerCell)
'Add the row to the table
newPagerTable.Controls.Add(newPagerRow)
'Render the new pager row (table+row+3 new cells with controls)
newPagerTable.RenderControl(writer)
If Me.AllowPaging Then
' remove the original (default) pager row, otherwise we have two
gridViewTable.Controls.RemoveAt(0)
End If
'Render the gridview
gridViewTable.RenderControl(writer)
Catch ex As Exception
ControlErrorHandler(ex, "RenderChildren")
End Try
End Sub
Private Sub ControlErrorHandler(ByVal ex As Exception, ByVal methodName As String)
Throw New ApplicationException(Me.GetType().ToString & "." & methodName & " failed due to the inner error - " & ex.Message, ex)
End Sub
<Description("The current page index of the gridview."), DefaultValue(""), Themeable(False), Category("Appearance")> _
Public Property CurrentPageIndex() As Integer
Get
If Not ViewState(VSKeyCurrPageIndex) Is Nothing Then
Return Integer.Parse(ViewState(VSKeyCurrPageIndex))
Else
Return 0
End If
End Get
Set(value As Integer)
ViewState(VSKeyCurrPageIndex) = value
End Set
End Property
End Class
The calling page has this:
Private Sub gridViewResults_PageSizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles gridViewResults.PageSizeChanged
'
End Sub
I have looked at almost every related issue here on Stackoverflow, as well as other possible solutions in posts and article online. Any insight or suggestions will be appreciated.
Related
I have an ASP.NET DataGrid that returns three clickable columns at the end. When running SelectedIndexChanged how can I return the value of the item that was clicked?
Thanks
Private Sub DG_SelectedIndexChanged(sender As Object, e As EventArgs)
Try
Dim DG As GridView = CType(sender, GridView)
Dim vID As Integer = DG.SelectedRow.Cells(0).Text
'Need to determine which item was clicked here
Catch ex As Exception
End Try
End Sub
In the end I found the only way was to add an onclick event to the LinkButton
AG_Link.Text = "View Agenda"
AG_Link.Attributes.Add("onclick", "returnDocumentType('Agenda');")
Add some JavaScript to update a hidden textbox, then pick up the result
Private Sub DG_SelectedIndexChanged(sender As Object, e As EventArgs)
Try
Dim DG As GridView = CType(sender, GridView)
Dim vID As Integer = DG.SelectedRow.Cells(0).Text
Dim DocumentType As String = DocumentTypeTB.Text
Catch ex As Exception
End Try
End Sub
Bit of a long way round, but it works :-)
I have a template field with a button. I want to press the button and get the row Id so I can return the users selection. I have a regular select button which works fine but the user want the people who are not employee’s to hide the select button. I have this working but since it’s a template field it fires the RoW_command procedure and I cannot seem to get the row index since it is a template field and not the regular Select button. Or I cannot seem to name the Regular select button since it Command field does not have a Name or ID property?
Like I said this works hiding the template field called btnSelect
Private Sub GridView3_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView3.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
If (DataBinder.Eval(e.Row.DataItem, "LegacyPersonType") <> "Employee") Then
e.Row.ForeColor = Drawing.Color.Black
e.Row.BackColor = Drawing.Color.Yellow ' This will make row back color yellow
e.Row.FindControl("btnSelect").Visible = False
Else
e.Row.ForeColor = Drawing.Color.Black
e.Row.BackColor = Drawing.Color.White ' the normal employees make white
End If
End If
End Sub
I need to find the Row index when I press btnSelect
Private Sub GridView3_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView3.RowCommand
Dim index As Integer = Convert.ToInt32(e.CommandArgument) ‘ Error invalid
‘ in other words when pressing the tem-plate field there is no e.CommandArgument
‘But clicking the regular select there is
Dim row As GridViewRow = GridView3.Rows(index)
Session("EnterpriseID") = HttpUtility.HtmlDecode(GridView3.Rows(index).Cells(2).Text)
Dim EmployeeType As String = HttpUtility.HtmlDecode(GridView3.Rows(index).Cells(7).Text)
Dim CommonName As String = HttpUtility.HtmlDecode(GridView3.Rows(index).Cells(1).Text)
Dim EnterpriseID = HttpUtility.HtmlDecode(GridView3.Rows(index).Cells(6).Text)
The GridViewRow object knows its row index via the RowIndex property in the RowCommand event, like this:
Private Sub GridView3_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView3.RowCommand
' Get the row by finding the grandparent of the control (button)
' that initiated the command
Dim theGridViewRow As GridViewRow
theGridViewRow = CType(CType(e.CommandSource, Button).Parent.Parent, GridViewRow)
' Get the index value from the grid view row
Dim index As Integer = theGridViewRow.RowIndex
End Sub
Ok I figured it out myself, I made the default Select button on the grid a Template field. since it was the default button it had the necessary code to make it fire. This was the problem with making a template field from scratch. And by making it a template field it then also gave it a name. So in the RowDataBound code the FindControl method above hides it when a person is not an employee. – R2 Builder 1 min ago edit
I Have a web form in a asp.net web form 3.5. The listview has a checkbox on the item template, I am trying to retain the state of the checkboxes through pagination . Once I can page through records and preserver this state I need to send this to a print page which takes those ids...I have this working, but it will print only the records on each pagination. Please see live website:
http://rgvpreferred.com/ProviderSearch.aspx
Can you please check my code and suggest how can this be done, the code below is not working.
Protected Sub ListView1_ItemCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewCommandEventArgs) Handles ListView1.ItemCommand
Dim myButtonPrint1 As Button = CType(ListView1.FindControl("printButton1"), Button)
If e.CommandSource Is myButtonPrint1 Then
Dim recordsId As New List(Of String)
For Each lvi1 As ListViewItem In ListView1.Items
Dim chb1 As CheckBox = IIf(lvi1.FindControl("CheckBoxPrintProvider1") Is Nothing, IIf(lvi1.FindControl("CheckBoxPrintProvider3") Is Nothing, lvi1.FindControl("CheckBoxPrintProvider4"), lvi1.FindControl("CheckBoxPrintProvider3")), lvi1.FindControl("CheckBoxPrintProvider1"))
If chb1.Checked = True Then
Dim param1 As String
param1 = DirectCast(lvi1.FindControl("lblId1"), Label).Text
recordsId.Add(param1)
End If
Next
' Store in session to be pulled out in the Printable Page
Session("Records") = recordsId
Response.Redirect("PrintableProviderList.aspx")
End If
End Sub
'Trying to Preserve states
Private ReadOnly Property IDs() As List(Of Integer)
' Create a list of ID's that are selected. ID's is the primary
' Key for this table
Get
If Me.ViewState("IDs") Is Nothing Then
Me.ViewState("IDs") = New List(Of Integer)()
End If
Return CType(Me.ViewState("IDs"), List(Of Integer))
End Get
End Property
Protected Sub AddRowstoIDList()
' Loop through all the current items in the Listview
For Each lvi As ListViewDataItem In ListView1.Items
' Find the checkbox in each row
Dim chkSelect As CheckBox = CType(lvi.FindControl("CheckBoxPrintProvider1"), CheckBox)
' If the checkbox is ticked then add the corresponding ID to our private
' list
If (Not (chkSelect) Is Nothing) Then
' Get the ID from the datakeynames property
Dim ID As Integer = Convert.ToInt32(ListView1.DataKeys(lvi.DisplayIndex).Value)
If (chkSelect.Checked AndAlso Not Me.IDs.Contains(ID)) Then
' Add the ID to our list
Me.IDs.Add(ID)
ElseIf (Not chkSelect.Checked AndAlso Me.IDs.Contains(ID)) Then
' Not checked - remove the ID from our list
Me.IDs.Remove(ID)
End If
End If
Next
End Sub
Protected Sub ListView1_PagePropertiesChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.PagePropertiesChangingEventArgs) Handles ListView1.PagePropertiesChanging
AddRowstoIDList()
End Sub
Protected Sub ListView1_Sorting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewSortEventArgs) Handles ListView1.Sorting
AddRowstoIDList()
End Sub
Protected Sub ListView1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) Handles ListView1.ItemDataBound
' Get each Listview Item on DataBound
Dim lvi As ListViewDataItem = e.Item
If (lvi.ItemType = ListViewItemType.DataItem) Then
' Find the checkbox in the current row
Dim chkSelect As CheckBox = CType(lvi.FindControl("CheckBoxPrintProvider1"), CheckBox)
' Make sure we're referencing the correct control
If (Not (chkSelect) Is Nothing) Then
' If the ID exists in our list then check the checkbox
Dim ID As Integer = Convert.ToInt32(ListView1.DataKeys(lvi.DisplayIndex).Value)
chkSelect.Checked = Me.IDs.Contains(ID)
End If
End If
End Sub
you have to save the selected checkboxes before pagination .
see this link. it should help you:
http://evonet.com.au/maintaining-checkbox-state-in-a-listview/
I have nested repeaters, each item in the nested repeater has a label and a button on it, i want to beable to access the label.text when the button is clicked, I think i'm nearly there as I can return the index of the repeater and nested repeater that is clicked, i'm just having some trouble finding the label itself.
You might be able to help me without me posting the repeater code. Here is my code behind for when the button is clicked.
Protected Sub btnEditUser_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim btnEditUser As Button = DirectCast(sender, Button)
Dim reClient As RepeaterItem = DirectCast(btnEditUser.NamingContainer.Parent.Parent, RepeaterItem)
Dim reUser As RepeaterItem = DirectCast(btnEditUser.NamingContainer, RepeaterItem)
Dim selectedClient As Integer = reClient.ItemIndex
Dim selectedUser As Integer = reUser.ItemIndex
Dim UserId As Label = DirectCast(reClients.Items(selectedClient).FindControl("lUserName"), Label)
Response.Write(selectedClient & " " & selectedUser & " " & UserId.Text)
End Sub
I'm currently getting this error 'Object reference not set to an instance of an object.' when trying to write the value of UserId.Text so i think i've got it slightly wrong in this line:
Dim UserId As Label = DirectCast(reClients.Items(selectedClient).FindControl("lUserName"), Label)
This is just a guess, but sometimes you get errors like this when not all rows contain the control you're looking for. Often the code loops through the rows in order, hits a header row first that doesn't contain the relevant control, and fails.
Here is a good MSDN article - Locating a Control Inside a Hierarchy of Naming containers.
Private Function FindControlRecursive(
ByVal rootControl As Control, ByVal controlID As String) As Control
If rootControl.ID = controlID Then
Return rootControl
End If
For Each controlToSearch As Control In rootControl.Controls
Dim controlToReturn As Control =
FindControlRecursive(controlToSearch, controlID)
If controlToReturn IsNot Nothing Then
Return controlToReturn
End If
Next
Return Nothing
End Function
Try it,
Dim UserId As Label =DirectCast(FindControlRecursive(repClient,"lUserName"),Label)
Is there a way to select the number of records/rows to display in the gridview by a drop down list ?
If you mean a dynamic change of the number of rows based on a DDL selection, sure it can be done.
I would suggest using an AJAX method on the select action that would query the DB for the exact amount of rows and returning. Far too often I've seen a query bring back thousands of rows and the paging etc is done in memory. Much more efficient to just get the rows/page directly from the DB and preserve bandwidth.
Not sure if that is exactly what you were asking, but hope it helps.
You can also use RowCreated to create your Dropdownlist in Codebehind. Have a look at following example(VB.Net):
Private Sub Yourgrid_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles Yourgrid.RowCreated
Select Case e.Row.RowType
Case DataControlRowType.Pager
Dim ddlPager As New DropDownList
ddlPager.ID = "DdlPager"
ddlPager.AutoPostBack = True
ddlPager.ToolTip = "Change Pagesize"
ddlPager.Items.Add("5")
ddlPager.Items.Add("10")
ddlPager.Items.Add("25")
ddlPager.Items.Add("50")
ddlPager.Items.Add("100")
ddlPager.SelectedValue = "10"
AddHandler ddlPager.SelectedIndexChanged, AddressOf Me.PageSizeChanged
e.Row.Cells(0).ColumnSpan -= 1
Dim td As New TableCell
Dim span1 As New Label
span1.Text = "Show"
span1.Style("margin-left") = "50px"
td.Controls.Add(span1)
td.Controls.Add(ddlPager)
Dim span2 As New Label
span2.Text = "rows per page"
td.Controls.Add(span2)
e.Row.Cells.Add(td)
End Select
End Sub
Private Sub PageSizeChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Dim ddlPager As DropDownList = DirectCast(sender, DropDownList)
Dim newPageSize As Int32 = Int32.Parse(ddlPager.SelectedValue)
YourGrid.PageSize = newPageSize 'change the PageSize of the Grid'
DataBindYourGrid() 'call the function that Binds your grid to the Datasource'
UpdYourgrid.Update() 'if you use Ajax, update the UpdatePanel of this GridView'
End Sub
On this way you autogenerate the Dropdonwlist on every postback and add it to the Gridview's pager. The code is reusable for any GridView.