31

I have a GridView displaying person records. I want to conditionally show a CommandField or ButtonField based on some property of the underlying record. The idea is to only allow a command to be performed on specific people.

What is the best way to do this? I'd prefer a declarative solution to a procedural one.

Bob Kaufman
  • 12,864
  • 16
  • 78
  • 107
Larsenal
  • 49,878
  • 43
  • 152
  • 220

10 Answers10

67

First, convert your ButtonField or CommandField to a TemplateField, then bind the Visible property of the button to a method that implements the business logic:

<asp:GridView runat="server" ID="GV1" AutoGenerateColumns="false">
    <Columns>
        <asp:BoundField DataField="Name" HeaderText="Name" />
        <asp:BoundField DataField="Age" HeaderText="Age" />
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Button runat="server" Text="Reject" 
                Visible='<%# IsOverAgeLimit((Decimal)Eval("Age")) %>' 
                CommandName="Select"/>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Then, in the code behind, add in the method:

protected Boolean IsOverAgeLimit(Decimal Age) {
    return Age > 35M;
}

The advantage here is you can test the IsOverAgeLimit method fairly easily.

Marcel
  • 15,039
  • 20
  • 92
  • 150
ViNull
  • 1,022
  • 8
  • 12
  • 2
    Exactly answer I was looking for. Nice, thanks. Don't you just love those not-so-well-documented GridView features? :-) – Maciej Aug 05 '10 at 21:57
  • @levi I edited the answer slightly to include the command name (for a select command) within the proposed button. – Marcel Jul 18 '16 at 12:20
  • @Marcel will it effect the performance as the button's visible is calling the server side event every time.if yes please suggest an alternative way – clarifier Jun 21 '17 at 10:35
  • @clarifier Well, it's calling the code behind method every time. This in itself is inexpensive. However you should pay attention to the implementation of the method. If it is expensive, this could easily sum up to a large cost. A simple comparison should be no problem. **To be sure, use a profiling tool.** – Marcel Jun 21 '17 at 11:33
13

it could be done when the RowDataBound event fires

  protected void GridView_RowDataBound(Object sender, GridViewRowEventArgs e)
  {
    if(e.Row.RowType == DataControlRowType.DataRow)
    {
      // Hide the edit button when some condition is true
      // for example, the row contains a certain property
      if (someCondition) 
      {
          Button btnEdit = (Button)e.Row.FindControl("btnEdit");

          btnEdit.Visible = false;
      }
    }   
  }

Here's a demo page

markup

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DropDownDemo._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>GridView OnRowDataBound Example</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
            <Columns>
                <asp:BoundField HeaderText="Name" DataField="name" />
                <asp:BoundField HeaderText="Age" DataField="age" />
                <asp:TemplateField>
                    <ItemTemplate>                
                        <asp:Button ID="BtnEdit" runat="server" Text="Edit" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </form>
</body>
</html>

Code Behind

using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;

namespace GridViewDemo
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            GridView1.DataSource = GetCustomers();
            GridView1.DataBind();
        }

        protected override void OnInit(EventArgs e)
        {
            GridView1.RowDataBound += new GridViewRowEventHandler(GridView1_RowDataBound);
            base.OnInit(e);
        }

        void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType != DataControlRowType.DataRow) return;

            int age;
            if (int.TryParse(e.Row.Cells[1].Text, out age))
                if (age == 30)
                {
                    Button btnEdit = (Button) e.Row.FindControl("btnEdit");
                    btnEdit.Visible = false;
                }
        }

        private static List<Customer> GetCustomers()
        {
            List<Customer> results = new List<Customer>();

            results.Add(new Customer("Steve", 30));
            results.Add(new Customer("Brian", 40));
            results.Add(new Customer("Dave", 50));
            results.Add(new Customer("Bill", 25));
            results.Add(new Customer("Rich", 22));
            results.Add(new Customer("Bert", 30));

            return results;
        }
    }

    public class Customer
    {
        public string Name {get;set;}
        public int Age { get; set; }

        public Customer(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }
}

In the demo, the Edit Button is not Visible (the HTML markup is not sent to the client) in those rows where the Customer's age is 30.

Russ Cam
  • 124,184
  • 33
  • 204
  • 266
11

Allow me to share my approach for what it's worth. For me converting the commandfield to a templatefield control is not an option, as the commandfield comes with built-in functionality that I would otherwise have to create myself, for example the fact that it changes to "Update Cancel" when Edit is clicked, and that when Edit is clicked, all the cells in the row which are labels become textboxes, etc.

In my approach, you can leave the commandfield as is, then you can hide it as needed via code behind. In this example, I am hiding it if the field "Scenario" of the grid shows the text "Actual" for the relevant row of the RowDataBound event.

protected void gridDetail_RowDataBound(object sender, GridViewRowEventArgs e)
    {   
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            if (((Label)e.Row.FindControl("lblScenario")).Text == "Actual")
            {
                LinkButton cmdField= (LinkButton)e.Row.Cells[0].Controls[0];
                cmdField.Visible = false;
            }
    }}
leoinlios
  • 831
  • 2
  • 9
  • 17
  • 2
    I almost gave up and just convert my commandfield to a templatefield but I know it can be done so I keep on searching for it. I think this should be the correct answer. (Although I'm still not sure what the asker meant about the declarative answer.) – Gellie Ann May 07 '15 at 02:09
  • yes, I prefer this approach. Without assuming which control is which, you can also loop through and check CommandName – Garr Godfrey Nov 07 '16 at 23:00
  • I also agree that this answer is the most appropriate to the formulated question. While changing the commandfield to a templatefield does work (and imo is better in most cases) - the question is how to specifically hide a commandfield. – Spacemonkey Nov 14 '18 at 16:49
6

Hide the Entire GridView Column

If you want to remove the column completely (i.e. not just the button) from the table then use a suitable event handler, e.g. for the OnDataBound event, and then hide the appropriate column on the target GridView. Pick an event that will only fire once for this control, i.e. not OnRowDataBound.

aspx:

<asp:GridView ID="grdUsers" runat="server" DataSourceID="dsProjectUsers" OnDataBound="grdUsers_DataBound">
    <Columns>
        <asp:TemplateField HeaderText="Admin Actions">
            <ItemTemplate><asp:Button ID="btnEdit" runat="server" text="Edit" /></ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="FirstName" HeaderText="First Name" />
        <asp:BoundField DataField="LastName" HeaderText="Last Name" />
        <asp:BoundField DataField="Telephone" HeaderText="Telephone" />
    </Columns>
</asp:GridView>

aspx.cs:

protected void grdUsers_DataBound(object sender, EventArgs e)
{
    try
    {
        // in this case hiding the first col if not admin
        if (!User.IsInRole(Constants.Role_Name_Admin))
            grdUsers.Columns[0].Visible = false;
    }
    catch (Exception ex)
    {
        // deal with ex
    }
}
Scotty.NET
  • 12,533
  • 4
  • 42
  • 51
1

To conditionally control view of Template/Command fields, use RowDataBound event of Gridview, like:

    <asp:GridView ID="gv1" OnRowDataBound="gv1_RowDataBound"
              runat="server" AutoGenerateColumns="False" DataKeyNames="Id" >
    <Columns>   
        ...        
           <asp:TemplateField HeaderText="Order Status" 
HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center"> 
                 <ItemTemplate> 
                       <asp:Label ID="lblOrderStatus" runat="server"
Text='<%# Bind("OrderStatus") %>'></asp:Label> 
                 </ItemTemplate>
                 <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                 <ItemStyle HorizontalAlign="Center"></ItemStyle>
           </asp:TemplateField>  
        ...

            <asp:CommandField ShowSelectButton="True" SelectText="Select" />

    </Columns>
                </asp:GridView>

and following:

protected void gv1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        Label lblOrderStatus=(Label) e.Row.Cells[4].FindControl("lblOrderStatus");

        if (lblOrderStatus.Text== "Ordered")
        {
            lblOrderStatus.ForeColor = System.Drawing.Color.DarkBlue;
            LinkButton bt = (LinkButton)e.Row.Cells[5].Controls[0];
            bt.Visible = false;
            e.Row.BackColor = System.Drawing.Color.LightGray;
        }
    }
Altaf Patel
  • 1,351
  • 1
  • 17
  • 28
1
<asp:GridView ID="gv_Document" CssClass="gridstyle" runat="server" OnRowDataBound="gv_Document_RowDataBound" AutoGenerateColumns="false" DataKeyNames="SourceGUID,Source,FilePath" ShowHeaderWhenEmpty="false" OnRowDeleting="gv_Document_RowDeleting">
   <Columns>
       <asp:BoundField HeaderText="ItemID" DataField="ItemID" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
       <asp:BoundField HeaderText="SourceGUID" DataField="SourceGUID" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
       <asp:BoundField HeaderText="Source" DataField="Source" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
           <asp:TemplateField HeaderText="">
               <ItemTemplate>
                    <asp:HyperLink ID="hyperLink" runat="server" Target="_blank" NavigateUrl='<%# Bind("FilePath")%>'
                                                                Text='<%# Bind("FileName")%>'>  </asp:HyperLink>
               </ItemTemplate>
           </asp:TemplateField>
      <asp:BoundField HeaderText="Type" DataField="FileExtension" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
      <asp:BoundField HeaderText="Content type" DataField="FileMimeType" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
      <asp:BoundField HeaderText="File Path" DataField="FilePath" ItemStyle-CssClass="hidden-field" HeaderStyle-CssClass="hidden-field" />
      <asp:CommandField ShowDeleteButton="True" DeleteText="Delete" />
   </Columns>

Use this code to disable delete button in gridview from code behind.

protected void gv_Document_RowDataBound(object sender, GridViewRowEventArgs e)
{ 
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        ((LinkButton)e.Row.Cells[7].Controls[0]).Visible = false;            
    }
}
kboul
  • 13,836
  • 5
  • 42
  • 53
Bhavin
  • 81
  • 1
  • 3
1

Convert the CommandField to a TemplateField and set the visible property of the button based on the value of the field (true/false)

<asp:Button ID="btnSelect" 
runat="server" Text="Select" 
Visible='<%# DataBinder.Eval(Container.DataItem,"IsLeaf") %>'/>
Rory
  • 11
  • 1
0

If this was based on roles you could use the multiview panel but not sure if you could do the same against a property of the record.

However, you could do this via code. In your rowdatabound event you can hide or show the button in it.

Kevin LaBranche
  • 20,908
  • 5
  • 52
  • 76
0

You can hide a CommandField or ButtonField based on the position (index) in the GridView.

For example, if your CommandField is in the first position (index = 0), you can hide it adding the following code in the event RowDataBound of the GridView:

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        ((System.Web.UI.Control)e.Row.Cells[0].Controls[0]).Visible = false;
    }
}
Wilson
  • 500
  • 8
  • 8
0

I have done a very simple thing to enable or disable command button. Below is my grid

<asp:GridView ID="grdOrderProduct" runat="server" TabIndex="1" BackColor="White" BorderColor="#CEC9EF" CssClass="table table-striped dataTable table-bordered"
  OnRowEditing="grdOrderProduct_RowEditing" OnRowUpdating="grdOrderProduct_RowUpdating" OnRowDeleting="grdOrderProduct_RowDeleting" OnRowDataBound="grdOrderProduct_RowDataBound"
  Width="100%" CellPadding="3" CellSpacing="1" BorderWidth="0" AutoGenerateColumns="False">
        <HeaderStyle />
        <AlternatingRowStyle />
        <Columns>
        <asp:BoundField DataField="ProductSKU" ReadOnly="true" HeaderText="Product SKU" HeaderStyle-CssClass="headTb4" />
         <asp:BoundField DataField="ProductName" ReadOnly="true" HeaderText="ProductName" HeaderStyle-CssClass="headTb4" />
         <asp:BoundField DataField="QTY" HeaderText="QTY" HeaderStyle-CssClass="headTb4" />
         <asp:BoundField DataField="Discount" HeaderText="Discount %" HeaderStyle-CssClass="headTb4" />
         <asp:BoundField DataField="TPrice" HeaderText="MRP" ReadOnly="true" HeaderStyle-CssClass="headTb4" />
          <asp:CommandField ShowEditButton="true" ButtonType="Image" EditImageUrl="~/Images/edit.png"
                  UpdateImageUrl="~/Images/gear.png" CancelText=" " HeaderStyle-CssClass="headTb4"
                  ShowDeleteButton="true" DeleteImageUrl="~/Images/delete.png"
                  HeaderText="Action" ItemStyle-HorizontalAlign="Center">
     <HeaderStyle CssClass="headTb4" />
     <ItemStyle HorizontalAlign="Center" />
     </asp:CommandField>
   </Columns>
<AlternatingRowStyle CssClass="odd" />
<PagerStyle HorizontalAlign="Center" VerticalAlign="Top" Wrap="False" />

In the following method i have done the changes

 protected void grdOrderProduct_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {                

                foreach (ImageButton button in e.Row.Cells[5].Controls.OfType<ImageButton>())
                {
                    if (button.CommandName == "Delete")
                    {
                        button.Visible = false;
                    }
                }                    

        }
    }
Sapnandu
  • 620
  • 7
  • 9