Search code examples
c#asp.netgridview

How to bind gridview on button click as empty grid is binding on postback


I've form where I'm using gridview and I'm removing columns at runtime based on some condition and on button click event I'm binding the gridview by populating the datatable but it is showing empty grid with all empty rows only.

Gridview code:

<asp:GridView ID="AccessControlGrid" GridLines="None" CssClass="wk-TableBorder" runat="server" 
                                        AutoGenerateColumns = "false" ShowHeaderWhenEmpty="True" EmptyDataText="There are no records.">
<Columns>
    <asp:TemplateField HeaderText="User No" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:Label ID="lblUserNo" runat="server" Text='<%# Eval("UserNo") %>'></asp:Label>
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="User Name" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:Label ID="lbl_UserName" runat="server" Text='<%# Eval("UserName") %>'></asp:Label>
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Original" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsOriginal" AutoPostback = "True" OnCheckedChanged="Chk_CheckedChanged" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsOriginal")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Continuation" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsContinuation" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsContinuation")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Debtor Amendment" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsDebtorAmendment" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsDebtorAmendment")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Secured Party Amendment" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsSecPartyAmendment" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsSecPartyAmendment")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Partial Assignment" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsPartialAssignment" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsPartialAssignment")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Full Assignment" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsFullAssignment" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsFullAssignment")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Termination" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsTermination" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsTermination")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField HeaderText="Submit Collateral Amendment" HeaderStyle-CssClass="SpacerColumn gridheader1 GridColumnSpacing" ItemStyle-CssClass="GridColumnSpacing" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
        <ItemTemplate>
            <asp:CheckBox ID="chk_FS_IsCollateralAmendment" runat="server" Checked='<%# Convert.ToBoolean(Eval("IsCollateralAmendment")) %>' />
        </ItemTemplate>
    </asp:TemplateField>
</Columns>
</asp:GridView>

On page load I just have to remove uwanted columns based on some condition and I've to hide the grid:

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                //This action will be performed once based on access type.
                AccessControlGridColumnAdjustment();
                AccessControlGrid.Visible = false;
            }
        }

and finally on button click I've to populate and show grid:

protected void ImgBtnGo_Click(object sender, EventArgs e)
        {
            if (returnTable == null)
                returnTable = new DataTable("AccessControl");

            CreateAccessControlDataTable(ref returnTable, hdnAccessType.Value);
            LoadData();
            PopulateDataTable(ref returnTable, accessControlResultSet, hdnAccessType.Value);
            AccessControlGrid.DataSource = returnTable;
            AccessControlGrid.DataBind();
            AccessControlGrid.Visible = true;
        }

Inside CreateAccessControlDataTable I'm just creating the structure of Datatable based on some condition:

public DataTable CreateAccessControlDataTable(ref DataTable returnTable, string accessType)
        {
            try
            {
                #region Creating DataTable Schema for different access types
                switch (accessType)
                {
                    case "FS":
                        returnTable.Columns.Add(new DataColumn("UserNo"));
                        returnTable.Columns.Add(new DataColumn("UserName"));
                        returnTable.Columns.Add(new DataColumn("IsOriginal"));
                        returnTable.Columns.Add(new DataColumn("IsContinuation"));
                        returnTable.Columns.Add(new DataColumn("IsDebtorAmendment"));
                        returnTable.Columns.Add(new DataColumn("IsSecPartyAmendment"));
                        returnTable.Columns.Add(new DataColumn("IsPartialAssignment"));
                        returnTable.Columns.Add(new DataColumn("IsFullAssignment"));
                        returnTable.Columns.Add(new DataColumn("IsTermination"));
                        returnTable.Columns.Add(new DataColumn("IsCollateralAmendment"));
                        break;
                    case "IPF":
                        returnTable.Columns.Add(new DataColumn("UserNo"));
                        returnTable.Columns.Add(new DataColumn("UserName"));
                        returnTable.Columns.Add(new DataColumn("IsInPrepFilingDeletion"));
                        break;
                    case "FLD":
                        returnTable.Columns.Add(new DataColumn("UserNo"));
                        returnTable.Columns.Add(new DataColumn("UserName"));
                        returnTable.Columns.Add(new DataColumn("FloodStopAccess"));
                        returnTable.Columns.Add(new DataColumn("FloodReIssueAccess"));
                        returnTable.Columns.Add(new DataColumn("FloodLifeOfLoan"));
                        returnTable.Columns.Add(new DataColumn("FloodBasicCertificate"));
                        break;
                }
                #endregion
            }
            catch (Exception ex)
            {
                Response.Write(ex.Message);
            }
            return returnTable;
        }

Inside LoadData() and PopulateDataTable() function I'm just populating the empty Datatable with values from backend that is working fine but at the end I'm getting empty grid with multiple empty rows as below:

enter image description here

I've checked multiple threads here but nothing worked in my case.

Note: If I do the same thing inside- if (!IsPostback) {} then gridview loads properly but I don't have to do that, I've to load the grid based on search criteria at postback.


Solution

  • Since we using templated columns (not auto generate). Then we need to deal with the columns we "are to hide".

    This ALSO suggests that for at least those columns we hide, we can't really use binding expressions in the markup, since the given data source will not have those columns, and they will fail upon binding.

    This suggests that we want to control the binding of those hide/show columns, and that of hiding/removing from the GV.

    I also find that the instant we start to do such things, and the instant we have a LOT of templated controls, then I suggest using a listview in place of gridview. (There are a lot of reasons for this, one grid view simple reason is that the markup becomes less "cluttered" up since each control (standard asp.net controls) do not require a templated markup section, and then on top of that, you need an "item template" (this gets too messy too fast!).

    However, let's put aside these reasons, and stick with the gv.

    With a gridview, this is a bit of a bear to ride and tame, but this seems to be about the most workable.

    Note: any optional column in the markup does not have a data binding expression (which would fail if data column is missing).

    This markup:

            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
                DataKeyNames="ID" Width="40%" GridLines="None"
                CssClass="table table-hover table-striped" OnRowDataBound="GridView1_RowDataBound">
                <Columns>
                    <asp:TemplateField HeaderText="First">
                        <ItemTemplate>
                            <asp:Label ID="lblFirst" runat="server"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                    <asp:TemplateField HeaderText="Last">
                        <ItemTemplate>
                            <asp:Label ID="lblLast" runat="server"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                    <asp:TemplateField HeaderText="City">
                        <ItemTemplate>
                            <asp:Label ID="lblCity" runat="server"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                    <asp:TemplateField HeaderText="Hotel Name">
                        <ItemTemplate>
                            <asp:Label ID="lblHotel" runat="server" 
                                Text='<%# Eval("HotelName") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                    <asp:TemplateField HeaderText="Description">
                        <ItemTemplate>
                            <asp:Label ID="lblDesc" runat="server" 
                                Text='<%# Eval("Description") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                </Columns>
            </asp:GridView>
    

    And this code to load:

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                LoadGrid();
        }
    
        void LoadGrid()
        {
            string strSQL = @"SELECT ID, FirstName, LastName, City, HotelName, Description
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
        }
    

    And our row data bind that looks for optional fields.

    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        // all columns that are "optional" go here
        List<string> sOptional = 
            new List<string> { "FirstName:lblFirst:0", "LastName:lblLast:1", "City:lblCity:2" };
    
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            DataRowView gBindData = (DataRowView)e.Row.DataItem;
    
            foreach (string sHide in sOptional)
            {
                string sField = sHide.Split(':')[0];
                string sCtrl = sHide.Split(':')[1];
                int gCol = Convert.ToInt32(sHide.Split(':')[2]);
    
                if (gBindData.Row.Table.Columns.Contains(sField))
                {
                    Label lbl = (Label)e.Row.FindControl(sCtrl);
                    lbl.Text = gBindData["FirstName"].ToString();
                }
                else
                {
                    GridView1.Columns[gCol].Visible = false;
                }
            }
        }
    }
    

    We define the optional columns. We do not use data bind expressions for optional columns (we bind them with code as per above. The above displays like this:

    enter image description here

    However, the above button click has this code:

        protected void cmdTest_Click(object sender, EventArgs e)
        {
    
            string strSQL = @"SELECT ID, HotelName, Description
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
    
        }
    

    We left out of the data source the 3 optional columns.

    The result is thus this:

    enter image description here

    With the above design, we just leave out the columns we don't want from the data source, and the rest of the code should just work.

    Edit: Not loading GV on first page load

    To be more complete here, we have this code:

    NB: a small edit was made to the row data bound (we have to re-enable (visible = true) for when we re-bind the gv.

    Say we now have this markup at start:

            <asp:Button ID="cmdTest" runat="server" Text="Load data" 
                CssClass="btn"
                OnClick="cmdTest_Click"
                />
            <asp:Button ID="cmdTest2" runat="server" Text="Load Different data" 
                style="margin-left:20px"
                CssClass="btn"
                OnClick="cmdTest2_Click"
                />
            <br />
    

    gv markup is un-changed.

    And now the code is really much the same, and is this:

        protected void Page_Load(object sender, EventArgs e)
        {
        }
    
        protected void cmdTest_Click(object sender, EventArgs e)
        {
            LoadGrid();
        }
    
        protected void cmdTest2_Click(object sender, EventArgs e)
        {
            string strSQL = @"SELECT ID, HotelName, Description
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
        }
        void LoadGrid()
        {
            string strSQL = @"SELECT ID, FirstName, LastName, City, HotelName, Description
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
        }
    
    
        protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            // all columns that are "optional" go here
            List<string> sOptional = 
                new List<string> { "FirstName:lblFirst:0", "LastName:lblLast:1", "City:lblCity:2" };
    
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                DataRowView gBindData = (DataRowView)e.Row.DataItem;
    
                foreach (string sHide in sOptional)
                {
                    string sField = sHide.Split(':')[0];
                    string sCtrl = sHide.Split(':')[1];
                    int gCol = Convert.ToInt32(sHide.Split(':')[2]);
    
                    if (gBindData.Row.Table.Columns.Contains(sField))
                    {
                        Label lbl = (Label)e.Row.FindControl(sCtrl);
                        lbl.Text = gBindData["FirstName"].ToString();
                        GridView1.Columns[gCol].Visible = true;
                    }
                    else
                    {
                        GridView1.Columns[gCol].Visible = false;
                    }
                }
            }
        }
    

    And result is now this:

    enter image description here

    Note closely: the hide or show of columns will persist even when you do a full-rebind of the gv. (since your columns are tempalted).

    This means if you hide a column (due to one data source bind), then those columns WILL retain their setting when you re-bind!

    Solution: Your code that hides columns must also un-hide the columns!

    This issue has nothing to do with the gv being loaded on page load or not. The issue of course is that if you bind the gv, hide some columns, and re-bind? Those columns will remain hidden, and they do not re-set their visible sitting on a re-bind.

    Edit 2: Get selected rows by user

    Assume a check box in the grid. It can be bound to data, or not, does not matter.

    Add this markup to the gv.

                    <asp:TemplateField HeaderText="Active" ItemStyle-HorizontalAlign="Center">
                        <ItemTemplate>
                            <asp:CheckBox ID="chkActive" runat="server"
                                checked='<%# Eval("Active") %>'
                                />
                        </ItemTemplate>
                    </asp:TemplateField>
    

    And our data sources now include column Active:

    eg this:

        protected void cmdTest_Click(object sender, EventArgs e)
        {
            LoadGrid();
        }
    
        protected void cmdTest2_Click(object sender, EventArgs e)
        {
            string strSQL = @"SELECT ID, HotelName, Description, Active
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
        }
        void LoadGrid()
        {
            string strSQL = @"SELECT ID, FirstName, LastName, City, HotelName, Description, Active
                            FROM tblHotelsA
                            ORDER BY HotelName";
    
            GridView1.DataSource = General.MyRst(strSQL);
            GridView1.DataBind();
        }
    

    And right after the gv, say we have this:

            <asp:Button ID="cmdShowChecked" runat="server" Text="Get Selected" 
                style="margin-left:20px"
                CssClass="btn"
                OnClick="cmdShowChecked_Click"
                />
            <asp:Label ID="lblSelected" runat="server" Text=""
                style="margin-left:30px">
            </asp:Label>
    

    Result is now this:

    enter image description here

    Code for selected button click after gv is this:

       protected void cmdShowChecked_Click(object sender, EventArgs e)
        {
            string sPKIDSelected = "";
            foreach (GridViewRow gRow in GridView1.Rows)
            {
                string sPK = GridView1.DataKeys[gRow.RowIndex]["ID"].ToString();
    
                CheckBox chkSel = (CheckBox)gRow.FindControl("chkActive");
                if (chkSel.Checked)
                {
                    if (sPKIDSelected != "")
                        sPKIDSelected += ",";
    
                    sPKIDSelected += sPK;
                }
            }
    
            string strSQL = $@"SELECT * FROM tblHotelsA
                            WHERE ID IN ({sPKIDSelected})
                            ORDER BY HotelName";
    
            DataTable MySelected = General.MyRst(strSQL);
    
            lblSelected.Text =
                $"PK database ID selected = {sPKIDSelected} Rows Selected = {MySelected.Rows.Count}";
            // do whatever with selected data table items
        }