Hi simflex,
Please refer below sample.
HTML
<asp:GridView ID="Gridview1" runat="server" ShowFooter="true" AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField DataField="RowNumber" HeaderText="Row Number" />
        <asp:TemplateField HeaderText="Name">
            <ItemTemplate>
                <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Website Name">
            <ItemTemplate>
                <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Designation">
            <ItemTemplate>
                <asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
            </ItemTemplate>
            <FooterStyle HorizontalAlign="Center" />
            <FooterTemplate>
                <asp:Button ID="ButtonAdd" runat="server" Text="Add New Row"
                    OnClick="ButtonAdd_Click" />
            </FooterTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
Namespace
C#
using System.Data;
VB.Net
Imports System.Data
Code
C#
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        this.SetInitialRow();
    }
}
protected void ButtonAdd_Click(object sender, EventArgs e)
{
    this.AddNewRowToGrid();
}
private void SetInitialRow()
{
    DataTable dt = new DataTable();
    DataRow dr = null;
    dt.Columns.Add(new DataColumn("RowNumber", typeof(string)));
    dt.Columns.Add(new DataColumn("Column1", typeof(string)));
    dt.Columns.Add(new DataColumn("Column2", typeof(string)));
    dt.Columns.Add(new DataColumn("Column3", typeof(string)));
    dr = dt.NewRow();
    dr["RowNumber"] = 1;
    dr["Column1"] = string.Empty;
    dr["Column2"] = string.Empty;
    dr["Column3"] = string.Empty;
    dt.Rows.Add(dr);
    //Store the DataTable in ViewState
    ViewState["CurrentTable"] = dt;
    this.Gridview1.DataSource = dt;
    this.Gridview1.DataBind();
}
private void AddNewRowToGrid()
{
    int rowIndex = 0;
    if (ViewState["CurrentTable"] != null)
    {
        DataTable dtCurrentTable = (DataTable)ViewState["CurrentTable"];
        DataRow drCurrentRow = null;
        if (dtCurrentTable.Rows.Count > 0)
        {
            TextBox box1 = null;
            TextBox box2 = null;
            TextBox box3 = null;
            for (int i = 1; i <= dtCurrentTable.Rows.Count; i++)
            {
                //extract the TextBox values
                box1 = (TextBox)Gridview1.Rows[rowIndex].Cells[1].FindControl("TextBox1");
                box2 = (TextBox)Gridview1.Rows[rowIndex].Cells[2].FindControl("TextBox2");
                box3 = (TextBox)Gridview1.Rows[rowIndex].Cells[3].FindControl("TextBox3");
                drCurrentRow = dtCurrentTable.NewRow();
                drCurrentRow["RowNumber"] = i + 1;
                dtCurrentTable.Rows[i - 1]["Column1"] = box1.Text;
                dtCurrentTable.Rows[i - 1]["Column2"] = box2.Text;
                dtCurrentTable.Rows[i - 1]["Column3"] = box3.Text;
                rowIndex++;
            }
            if (!string.IsNullOrEmpty(box1.Text) && !string.IsNullOrEmpty(box2.Text) && !string.IsNullOrEmpty(box3.Text))
            {
                dtCurrentTable.Rows.Add(drCurrentRow);
            }
            else {
                ScriptManager.RegisterStartupScript(this, this.GetType(), "alert", "alert('you can not insert empty record.');", true);
            }
            ViewState["CurrentTable"] = dtCurrentTable;
            this.Gridview1.DataSource = dtCurrentTable;
            this.Gridview1.DataBind();
        }
    }
    else
    {
        Response.Write("ViewState is null");
    }
    //Set Previous Data on Postbacks
    this.SetPreviousData();
}
private void SetPreviousData()
{
    int rowIndex = 0;
    if (ViewState["CurrentTable"] != null)
    {
        DataTable dt = (DataTable)ViewState["CurrentTable"];
        if (dt.Rows.Count > 0)
        {
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                TextBox box1 = (TextBox)Gridview1.Rows[rowIndex].Cells[1].FindControl("TextBox1");
                TextBox box2 = (TextBox)Gridview1.Rows[rowIndex].Cells[2].FindControl("TextBox2");
                TextBox box3 = (TextBox)Gridview1.Rows[rowIndex].Cells[3].FindControl("TextBox3");
                box1.Text = dt.Rows[i]["Column1"].ToString();
                box2.Text = dt.Rows[i]["Column2"].ToString();
                box3.Text = dt.Rows[i]["Column3"].ToString();
                rowIndex++;
            }
        }
    }
}
VB.Net
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    If Not Page.IsPostBack Then
        Me.SetInitialRow()
    End If
End Sub
Protected Sub ButtonAdd_Click(ByVal sender As Object, ByVal e As EventArgs)
    Me.AddNewRowToGrid()
End Sub
Private Sub SetInitialRow()
    Dim dt As DataTable = New DataTable()
    Dim dr As DataRow = Nothing
    dt.Columns.Add(New DataColumn("RowNumber", GetType(String)))
    dt.Columns.Add(New DataColumn("Column1", GetType(String)))
    dt.Columns.Add(New DataColumn("Column2", GetType(String)))
    dt.Columns.Add(New DataColumn("Column3", GetType(String)))
    dr = dt.NewRow()
    dr("RowNumber") = 1
    dr("Column1") = String.Empty
    dr("Column2") = String.Empty
    dr("Column3") = String.Empty
    dt.Rows.Add(dr)
    ViewState("CurrentTable") = dt
    Me.Gridview1.DataSource = dt
    Me.Gridview1.DataBind()
End Sub
Private Sub AddNewRowToGrid()
    Dim rowIndex As Integer = 0
    If ViewState("CurrentTable") IsNot Nothing Then
        Dim dtCurrentTable As DataTable = CType(ViewState("CurrentTable"), DataTable)
        Dim drCurrentRow As DataRow = Nothing
        If dtCurrentTable.Rows.Count > 0 Then
            Dim box1 As TextBox = Nothing
            Dim box2 As TextBox = Nothing
            Dim box3 As TextBox = Nothing
            For i As Integer = 1 To dtCurrentTable.Rows.Count
                box1 = CType(Gridview1.Rows(rowIndex).Cells(1).FindControl("TextBox1"), TextBox)
                box2 = CType(Gridview1.Rows(rowIndex).Cells(2).FindControl("TextBox2"), TextBox)
                box3 = CType(Gridview1.Rows(rowIndex).Cells(3).FindControl("TextBox3"), TextBox)
                drCurrentRow = dtCurrentTable.NewRow()
                drCurrentRow("RowNumber") = i + 1
                dtCurrentTable.Rows(i - 1)("Column1") = box1.Text
                dtCurrentTable.Rows(i - 1)("Column2") = box2.Text
                dtCurrentTable.Rows(i - 1)("Column3") = box3.Text
                rowIndex += 1
            Next
            If Not String.IsNullOrEmpty(box1.Text) AndAlso Not String.IsNullOrEmpty(box2.Text) AndAlso Not String.IsNullOrEmpty(box3.Text) Then
                dtCurrentTable.Rows.Add(drCurrentRow)
            Else
                ScriptManager.RegisterStartupScript(Me, Me.GetType(), "alert", "alert('you can not insert empty record.');", True)
            End If
            ViewState("CurrentTable") = dtCurrentTable
            Me.Gridview1.DataSource = dtCurrentTable
            Me.Gridview1.DataBind()
        End If
    Else
        Response.Write("ViewState is null")
    End If
    Me.SetPreviousData()
End Sub
Private Sub SetPreviousData()
    Dim rowIndex As Integer = 0
    If ViewState("CurrentTable") IsNot Nothing Then
        Dim dt As DataTable = CType(ViewState("CurrentTable"), DataTable)
        If dt.Rows.Count > 0 Then
            For i As Integer = 0 To dt.Rows.Count - 1
                Dim box1 As TextBox = CType(Gridview1.Rows(rowIndex).Cells(1).FindControl("TextBox1"), TextBox)
                Dim box2 As TextBox = CType(Gridview1.Rows(rowIndex).Cells(2).FindControl("TextBox2"), TextBox)
                Dim box3 As TextBox = CType(Gridview1.Rows(rowIndex).Cells(3).FindControl("TextBox3"), TextBox)
                box1.Text = dt.Rows(i)("Column1").ToString()
                box2.Text = dt.Rows(i)("Column2").ToString()
                box3.Text = dt.Rows(i)("Column3").ToString()
                rowIndex += 1
            Next
        End If
    End If
End Sub
Screenshot
