I am binding a data set to a GridView in VB .net. I have some custom sorting setup, and want to display an icon next to the header if one of my 3 options is selected.
I've read a lot of methods in doing this, and I see Gridviews even have a ASC and DESC header style I can associate with the view. I have 2 problems with this, though:
I am sorting a List with linq on Sort Trigger, then binding it to the datagrid.
The reason I do it this way, is I want to maintain multiple sort levels, ordering by 3 columns rather than 1.
Edit for clarity
Specifically what I want to do is loop through the value of a GridView's Header text, see if it matches what I have saved in viewstate, and if so add an image for that header in particular. Something essentially like below, however headerRow.Cells(y).Text is always returning "", even when the header has text:
Sub gvPatronData_RowCreated(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
Dim savedSortDirection(), savedSortColumn() As String
Dim headerRow As GridViewRow = gvPatronData.HeaderRow
'this sets the values of these variables
'as strings equal to the text displayed in the header of the gridview
_patronBl.SplitPatronSort(savedSortDirection, SortDirection, savedSortColumn, SortColumn)
If SortDirection <> "" Then
If e.Row.RowType = DataControlRowType.Header Then
For x = 0 To savedSortDirection.Length - 1
For y = 0 To headerRow.Cells.Count - 1
If headerRow.Cells(y).Text = savedSortColumn(x) Then
If savedSortDirection(x) = "Ascending" Then
Dim bGStyle As New System.Web.UI.WebControls.Style()
bGStyle.CssClass = "upSort"
headerRow.Cells(y).ApplyStyle(bGStyle)
Else
Dim bGStyle As New System.Web.UI.WebControls.Style()
bGStyle.CssClass = "downSort"
headerRow.Cells(y).ApplyStyle(bGStyle)
End If
End If
Next
Next
End If
End If
End Sub
Have you tried to loop the GridView's Columns instead of the the GridViewRow's Cell-Collection? A DataControlField has a HeaderText property.
For Each col As DataControlField In gvPatronData.Columns
If col.HeaderText = savedSortColumn(x) Then
If savedSortDirection(x) = "Ascending" Then
Dim bGStyle As New System.Web.UI.WebControls.Style()
bGStyle.CssClass = "upSort"
headerRow.Cells(gvPatronData.Columns.IndexOf(col)).ApplyStyle(bGStyle)
Else
Dim bGStyle As New System.Web.UI.WebControls.Style()
bGStyle.CssClass = "downSort"
headerRow.Cells(gvPatronData.Columns.IndexOf(col)).ApplyStyle(bGStyle)
End If
End If
Next
This is how I ended up doing it. Actually drilling into the table objects was where I was coming up short before.
Sub gvPatronData_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
Dim savedSortDirection(), savedSortColumn() As String
Dim columnCollection As DataControlFieldCollection = gvPatronData.Columns
_patronBl.SplitPatronSort(savedSortDirection, SortDirection, savedSortColumn, SortColumn)
If e.Row.RowType = DataControlRowType.Header Then
For x = 0 To savedSortDirection.Length - 1
For Each col As DataControlField In columnCollection
If col.HeaderText = _headerDictionary(savedSortColumn(x)) Then
If savedSortDirection(x) = "Ascending" Then
e.Row.Cells(gvPatronData.Columns.IndexOf(col)).Attributes.Add("Style", _
"background-image: url(images/arrow_up.png);background-repeat:no-repeat;background-position: 96% 50%;")
Else
e.Row.Cells(gvPatronData.Columns.IndexOf(col)).Attributes.Add("Style", _
"background-image: url(images/arrow_down.png);background-repeat:no-repeat;background-position: 96% 50%;")
End If
End If
Next
Next
End If
_headerDictionary : This was a dictionary of my DataField to HeaderText strings, since my savedSortColumn was the data field to sort on.
Def:
Public Function ColumnDataFieldToHeaderTextDictionary() As Dictionary(Of String, String)
Dim dict As New Dictionary(Of String, String)
dict.Add("FirstName", "First Name")
dict.Add("LastName", "Last Name")
dict.Add("Phone", "Phone Number")
dict.Add("Phone2", "Alternate Phone Number")
dict.Add("Email", "Email")
Return dict
End Function
Related
I have a GridView with a asp:dropdownlist
I am able to add dropdown values using rowdatabound.
However what I need to do, is add specific dropdown list values base on the contents of another cell in that row.
So for example if Row(1) and Cell(2) = MAR001, I want the Dropdown values Four, Five and Six
However if Row(1) and Cell(2) <> MAR001, I want the dropdown values One, Two and Three.
Here is my attempt using a loop, however it doesn't target the dropdown values correctly.
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
For i As Integer = 0 To GridView1.Rows.Count - 1
Dim Name As String = GridView1.Rows(i).Cells(2).Text
If Name = "MAR001" Then
Dim ddl As DropDownList = e.Row.FindControl("ddlQuantity")
Dim numbers As New List(Of String)()
numbers.Add("Four")
numbers.Add("Five ")
numbers.Add("Six")
ddl.DataSource = numbers
ddl.DataBind()
Else
Dim ddl As DropDownList = e.Row.FindControl("ddlQuantity")
Dim numbers As New List(Of String)()
numbers.Add("One")
numbers.Add("Two")
numbers.Add("Three")
ddl.DataSource = numbers
ddl.DataBind()
End If
Next
Else
End If
End Sub
Any help much appreciated.
Not sure if this is what you need, but try finding the textbox first and get the text value of that.
Edit: i just realized you're looping the rows. you don't have to do that - this is the RowDataBound event, so this code happens for every row.
If e.Row.RowType = DataControlRowType.DataRow Then
Dim ddl As DropDownList = e.Row.FindControl("ddlQuantity")
Dim numbers As New List(Of String)()
Dim customerid As String = (e.Row.Cells(2).Text)
If customerid = "MAR001" Then
numbers.Add("Four")
numbers.Add("Five ")
numbers.Add("Six")
Else
numbers.Add("One")
numbers.Add("Two")
numbers.Add("Three")
End If
ddl.DataSource = numbers
ddl.DataBind()
End If
VB
I've got a gridview in which I need to use template columns, therefore instead of being able to retrieve text straight from the cell, it's inside of a label. All the labels have different ids as well so I would like to programmatically retrieve the label inside a cell then look at the text inside the label.
This is what I have got at the moment, however it never manages to return the text and instead returns the empty string ("").
Public Function ReturnLabelText(ByVal c As TableCell) As String
For Each lb In c.Controls
If lb.[GetType]() = GetType(Label) Then
Return CType(lb, Label).Text()
End If
Next
Return ""
End Function
The function is called on this line:
doc.ReplaceText("#" + gridView.HeaderRow.Cells(i).Text.ToLower, ReturnLabelText(row.Cells(i)).Replace(" ", "").Replace(vbCr, ""))
You have to use GridViewRow.FindControl("ControlID") to get the reference of the Label.
So for example, if you want to loop loop all rows:
For Each row As GridViewRow In Me.GridView1.Rows
Dim label = DirectCast(row.FindControl("LabelID"), Label)
Dim text As String = label.Text
' ... '
Next
But if you want to set it's text on databinding i'd use the GridView.RowDataBound event instead:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
Dim label = DirectCast(e.Row.FindControl("LabelID"), Label)
Dim text As String = label.Text
' ... '
End Select
End Sub
You can even get the DataSource of each row via e.Row.DataItem to access all fields.
If you have a TableCell and you want to get all labels in it you could also use OfType:
Dim firstLabel = c.Controls.OfType(Of Label)().FirstOrDefault()
If firstLabel IsNot Nothing Then Return firstLabel.Text
Return Nothing
just been trying to display my total price from a column in my footer row.
here is my code to try that but it doesnt seem to be even going through it when i put a break point in
Sub gvPayments_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
' add the UnitPrice and QuantityTotal to the running total variables
invoiceTotal += Convert.ToDecimal(DataBinder.Eval(e.Row.DataItem, "Total"))
ElseIf e.Row.RowType = DataControlRowType.Footer Then
e.Row.Cells(0).Text = "Total:"
' for the Footer, display the running totals
e.Row.Cells(5).Text = invoiceTotal.ToString("c")
e.Row.Cells(5).HorizontalAlign = HorizontalAlign.Right
e.Row.Font.Bold = True
End If
End Sub
If you need any more of my code, just ask!
I like to use a method that doesn't require handling any events. It just uses a TemplateField and a couple different functions. In your GridView, make the column like this:
<asp:TemplateField HeaderText="Total">
<ItemTemplate><%#DisplayAndAddToTotal(Eval("Total").ToString(), "Total")%></ItemTemplate>
<FooterTemplate><%#GetTotal("Total")%></FooterTemplate>
</asp:TemplateField>
The second parameter to DisplayAndAddToTotal can be any string you want as long as you use the same string in GetTotal. I usually just use the field name again though. Here are the two functions used, DisplayAndAddToTotal and GetTotal. They use a Hashtable to store the totals so that it works with any number of columns you want to add up. And they also work with counting the number of "True"s for a Boolean field.
Protected total As Hashtable = New Hashtable()
Protected Function DisplayAndAddToTotal(itemStr As String, type As String) As Double
Dim item As Double
If itemStr = "True" Then
item = 1
ElseIf Not Double.TryParse(itemStr, item) Then
item = 0
End If
If total.ContainsKey(type) Then
total(type) = Double.Parse(total(type).ToString()) + item
Else
total(type) = item
End If
Return item
End Function
Protected Function GetTotal(type As String) As Double
Try
Dim result As Double = Double.Parse(total(type).ToString())
Return result
Catch
Return 0
End Try
End Function
I am trying to delete a row from a gridview (based on a condition) and then add than row to another gridview inside of the "master" gridview's RowDataBound event. Originally I did not know that in order to call .DeleteRow(i) you needed to have an "ondelete" event handler. However, since all the gridview's .DeleteRow method does is call this event handler, I am confused as to how to use it. Can someone please help point me in the right direction?
Protected Sub grdProduct_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles grdProduct.RowDataBound
' Grey out expired products
Dim row As GridViewRow
row = e.Row
Dim incomingDate As String
Dim incomingStatus As String = ""
incomingDate = row.Cells(3).Text.ToString()
incomingStatus = row.Cells(5).Text.ToString()
If (e.Row.RowType <> DataControlRowType.DataRow) Then
Exit Sub
End If
Try
Dim expDate As Date = incomingDate
If (expDate < DateTime.Today Or incomingStatus.Equals("D")) Then
'Create object for RowValues
Dim RowValues As Object() = {"", "", "", "", "", ""}
'Create counter to prevent out of bounds exception
Dim i As Integer = row.Cells.Count
'Fill row values appropriately
For index As Integer = 0 To i - 1
RowValues(index) = row.Cells(index).Text
Next
'create new data row
dProdRow = dProdtable.Rows.Add(RowValues)
dProdtable.AcceptChanges()
grdProduct.DeleteRow(e.Row.RowIndex)
End If
Catch ex As Exception
End Try
End Sub
Protected Sub grdProduct_Delete(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles grdProduct.RowDeleting
'Not sure what to do here
End Sub
The best way to do this is to manipulate this at the datasource level rather than manipulating the UI components themselves.
For example:
if(!IsPostback)
{
DataTable one = ...
DataTable two = ...
var removeRows = (from c in one.AsEnumerable()
where c.Field<DateTime>("incomingDate")< incomingDate ||
c.Field<string>("incomingStatus")=="D"
select c).ToList();
for(var item in removeRows)
{
two.ImportRow(item);
}
//now delete from initial table
foreach(var item in removeRows)
{
one.Rows.Remove(item);
}
//Now bind both grids
grid1.DataSource=one;
grid1.DataBind();
grid2.DataSource=two;
grid2.DataBind();
}
Update - Sample toy program in VB.NET Hopefully you can adapt it to your situation.
Sub Main
Dim one As New DataTable()
one.Columns.Add("one", GetType(Integer))
For i As Integer = 0 To 9
Dim r As DataRow = one.NewRow()
r.ItemArray = New Object() {i}
one.Rows.Add(r)
Next
Dim two As New DataTable()
two.Columns.Add("one", GetType(Integer))
For i As Integer = 0 To 9
Dim r As DataRow = two.NewRow()
r.ItemArray = New Object() {i}
two.Rows.Add(r)
Next
Dim removeRows = (From c In one.AsEnumerable() Where c.Field(Of Integer)("one") = 5).ToList()
For Each item As DataRow In removeRows
two.ImportRow(item)
Next
For Each item As DataRow In removeRows
one.Rows.Remove(item)
Next
End Sub
I just realized that you are using VB.NET. You should be able to translate the above from C# to VB.NET. The general idea is there, anyway.
I'm trying to add a row grouping row to my data-bound gridview. It works fine on the first response, but at the postback i got the "Failed to load viewstate" error.
There is the code for the GridView's RowDataBound event:
Private Sub AddGroupingRow(ByRef eRow As GridViewRow, ByVal Css As String, ByVal ColSpan As Integer, ByVal Txt As String)
Dim cell As New TableCell()
cell.ColumnSpan = ColSpan
cell.CssClass = "Spacing FieldCell"
cell.Text = Txt
Dim row As New GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal)
row.CssClass = Css
row.Cells.Add(cell)
Dim tbl As Table
tbl = eRow.Parent
tbl.Rows.AddAt(eRow.RowIndex + 1, row)
End Sub
Private Prev_Client_ID As Integer = -1
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
Dim dr As Data.DataRowView = TryCast(e.Row.DataItem, Data.DataRowView)
If dr IsNot Nothing AndAlso dr.Row.RowState <> Data.DataRowState.Added Then
Dim Client_ID As Integer = dr.Item("Client_ID")
If Client_ID <> Prev_Client_ID Then AddGroupingRow(e.Row, "Group", 8, dr.Item("Client_Name"))
Prev_Client_ID = Client_ID
End If
End If
End Sub
I know that it's easier to create the grouping rows at the datasource, but deriving from this code, i want to create some base to other grids at the site.
Try this link. it should help you. I too had the same problem. The only way to resolve is a proper understanding of viewstate.
After some thinking, the answer could not be simpler: disable the viewstate of the gridview.
I was re-binding the datasource at every paging or sorting, so the need for the viewstate was minimum, except for the PageSize and PageIndex, that i became forced to keep track manually.
This is a considerable trade-off: things like PageSize and PageIndex must be managed manually afterwards, since their state are keeped by the ViewState.
So, even doing exactly what i was meant to do, i've decided to adopt an simple alternative solution:
If Cliente_ID <> Cliente_ID_Anterior Then
For i As Integer = 0 To e.Row.Cells.Count - 1
e.Row.Cells(i).Style("border-top") = "solid 1px #777777"
Next
e.Row.Cells(0).Style("border-bottom") = "none"
Else
e.Row.Cells(0).Text = " "
e.Row.Cells(0).Style("border-top") = "none"
e.Row.Cells(0).Style("border-bottom") = "none"
End If
It still groups the column, but in another way, and is more sort friendly. I could use rowspans and turn cells invisible, but it would be harder to keep. Besides, I'll need to paint the line when the user moves the mouse over it, and a rowspan would hinder a lot.