John Adams
John Adams

Reputation: 4823

Show only the footer for inserting new row in Gridview in .Net 3.5

When my Gridview is bound with "few rows" of data, my current approach for adding a new row is an adequate design and works "well". However, if bound with "many rows" of data, my current approach for adding a new row is flawed: the EmptyDataTemplate I am using is exposed using the FooterTemplate. Thus, if I have 3 rows of data and click "Add New Record", the grid is redisplayed with a 4th row all "prepared" for data entry. Yet if I have 30 rows, the display of the row to be inserted is so far down it requires a scroll.

Protected Sub AddNewRecord(ByVal sender As Object, ByVal e As EventArgs)
    GridView1.ShowFooter = True
    'rebind data so GridView1_RowDataBound gets a chance to populate the footer
    iSubscriberID = Session("SubscriberID")
    LoadDataGrid(iSubscriberID)
End Sub

I'm hoping to be able to improve the operation of adding a new row BUT still use the FooterTemplate.

Is there any code that could be added to my GridView1_RowDataBound handler to hide existing data rows but still expose the EmptyDataTemplate for insertion via the FooterTemplate? I've tried hacking a few things there without success. Here is my existing code for that handler:

    Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) _
  Handles GridView1.RowDataBound
    '-------------------------------------------------------------------------------------------*
    '   Handle 'Insert' requirements:
    '   - Bind dropdownlist controls with the possible incumbents and backups for some new position
    '-------------------------------------------------------------------------------------------*
    If e.Row.RowType = DataControlRowType.Footer Then
        '   Finding the Dropdown control.
        Dim ctrl As Control = e.Row.FindControl("ddlUsers")
        If ctrl IsNot Nothing Then
            Dim dd As DropDownList = TryCast(ctrl, DropDownList)
            dd.DataSource = allUsers
            dd.DataBind()
        End If
        Dim ctrlB As Control = e.Row.FindControl("ddlUsersBackup")
        If ctrlB IsNot Nothing Then
            Dim ddB As DropDownList = TryCast(ctrlB, DropDownList)
            ddB.DataSource = allUsers
            ddB.DataBind()
        End If
    End If
End Sub

I'm using TemplateField definitions for all the columns; here is one example that shows a part of the FooterTemplate:

<asp:TemplateField HeaderText="Incumbent">
        <ItemTemplate>
            <asp:Label ID="lblUser" runat="server" Text='<%# Eval("Incumbent")%>'></asp:Label>
        </ItemTemplate>
        <EditItemTemplate>
            <asp:Label ID="lblUser" runat="server" Text='<%# Eval("Incumbent")%>' Visible = "false"></asp:Label>            
            <asp:DropDownList Width="100%" runat="server" 
               id="ddlUsers" AutoPostBack="true" 
               DataTextField="FullName" DataValueField="UserID"
               OnSelectedIndexChanged="ddlUsers_SelectedIndexChanged">
            </asp:DropDownList> 
        </EditItemTemplate> 
        <FooterTemplate>
            <asp:Label ID="lblUser" runat="server" Text='Set Incumbent'></asp:Label>
            <br />          
            <asp:DropDownList Width="100%" runat="server" 
               id="ddlUsers" AutoPostBack="true" 
               DataTextField="FullName" DataValueField="UserID"
               OnSelectedIndexChanged="ddlUsers_SelectedIndexChanged">
            </asp:DropDownList> 
        </FooterTemplate>               
    </asp:TemplateField>

Another idea I had that might work would be to scroll the page to the bottom so the "insertion" line is always visible when repopulating the gridview with ShowFooter=True. However, that seems to involve some Javascript which I'd prefer to avoid on this page.

EDIT: 18 Feb 2016 - Attempt to add Paging to Gridview - a new complication

@Lesmian - adding a pager as you suggested was easy but now it totally breaks the Gridview:does not support server-side data paging

I researched that error and I cannot see why my strongly-typed collection for Positions cannot support paging; here is the code that instantiates my data source for the Gridview:

Public Class Positions
Implements IEnumerable(Of Position)
Public List As New List(Of Position)
Public Function GetEnumerator() As IEnumerator(Of Position) _
                Implements IEnumerable(Of Position).GetEnumerator
    Return List.GetEnumerator()
End Function
Private Function GetEnumerator1() As IEnumerator _
                Implements IEnumerable.GetEnumerator
    Return List.GetEnumerator()
End Function
Public Sub New(ByVal subscriberID As Integer, Optional ByVal filterOnUserID As Integer = 0)
    Dim sConnDatabase As String = ConfigurationManager.ConnectionStrings("DatabaseConnString").ConnectionString
    Dim connection As New SqlConnection(sConnDatabase)
    Dim cmd As SqlCommand
    Try
        cmd = New SqlCommand("dbo.GetPositionsBySubscriberID", connection)
        cmd.CommandType = CommandType.StoredProcedure
        cmd.Parameters.AddWithValue("@SubscriberID", subscriberID)
        cmd.Parameters.AddWithValue("@UserID", filterOnUserID)  'non-zero UserID returns only positions where User is Incumbent or Backup
        connection.Open()
        Dim objReader As SqlDataReader = cmd.ExecuteReader()
        Do While objReader.Read()
            Dim p As Position = New Position(objReader)
            List.Add(p)
        Loop
        objReader.Close()
        connection.Close()

The approach of adding a pager to solve the original problem has opened up a new issue. Any thoughts on that?

Upvotes: 2

Views: 851

Answers (3)

user1429080
user1429080

Reputation: 9166

The actual question in the OP seems to boil down to "How do I keep the row for inserting in view" after post back.

The Page object already has a method called Focus which can be used to cause a specific control to receive focus after the page rendering is complete.

When a control that is outside of the visible area of the browser receives focus, the browser will scroll that control into view so the user can see what is focused.

So to make the insert row visible, just make sure to set focus on a suitable control in the row when you make it visible in the code behind.

Upvotes: 2

Lesmian
Lesmian

Reputation: 3952

Scrolling gridview requires javascript, but if you don't want to use it maybe other approach will be suitable for your needs. If you add a pager to your gridview and set a small number of items per page then adding new item won't cause scrollbar to appear. With this approach we have however other flaw - new item could appear on a new page so you should force grid to show last page then new row will be always visible:

Protected Sub GridView1_RowCommand(sender As Object, e As GridViewCommandEventArgs) _
    Handles GridView1.RowCommand

    ' Insert data if the CommandName == "Insert" 
    ' and the validation controls indicate valid data...
    If e.CommandName = "Insert" AndAlso Page.IsValid Then
        ' Insert new record
        GridView1DataSource.Insert()
        ' Indicate that the user needs to be sent to the last page
        SendUserToLastPage = True
    End If
End Sub
Protected Sub GridView1_DataBound(sender As Object, e As EventArgs) _
    Handles GridView1.DataBound

    ' Send user to last page of data, if needed
    If SendUserToLastPage Then
        GridView1.PageIndex = GridView1.PageCount - 1
    End If
End Sub

Here you can find complete example of this approach: http://www.asp.net/web-forms/overview/data-access/enhancing-the-gridview/inserting-a-new-record-from-the-gridview-s-footer-vb

EDIT: 18 Feb 2016 - data source does not support server-side data paging error

Your custom collection must implement ICollection interface in order to work with GridView server side paging. IEnumerable doesn't implement ICollection that's why you get error. For an example of implementation in vb you can read this: https://support.microsoft.com/en-us/kb/306961 and this: http://www.codeproject.com/Articles/265692/Having-fun-with-custom-collections. After that GridView should do the rest for you.

Upvotes: 0

Alex Kudryashev
Alex Kudryashev

Reputation: 9470

General idea:
1. Place your GridView in a fixed-height scrollable div
2. Below the div place single line table with all controls you need to insert new record and "Add" button
3. If possible use SqlDataSource to populate GridView and include insert command to the SqlDataSource
4. In InsertParameters block use asp:controlParameter to bind to new values.
5. Add code to Add_Click handler

mySqlDataSource.Insert()

PS: Even better, rewrite your GridView to ListView (supported by .NET 3.5). This will give you much more flexibility. For example, you can place insert into first row.

Upvotes: 0

Related Questions