Mansfield
Mansfield

Reputation: 15170

Datagrid conditional row editing based on row data

I have an ASP.NET datagrid with a few rows in it. I'd like some of the rows to be editable, but not all of them (based on a particular data item in that row).

Originally, I was doing this with a ButtonColumn, but I wasn't able to turn that on or off for specific rows.

Here's what I have now:

<asp:DataGrid ID="grid1" runat="server" AutoGenerateColumns="false" EnableViewState="true" CssClass="GridviewControlStyle" CellSpacing="0" CellPadding="4" HeaderStyle-CssClass="HeaderStyle"
    OnEditCommand="grid1_EditCommand" OnUpdateCommand="grid1_UpdateCommand" OnCancelCommand="grid1_CancelCommand" OnItemDataBound="grid1d_ItemDataBound">
    <Columns>
        <asp:TemplateColumn>
            <HeaderTemplate>
                <strong><%# Resources.Status %></strong>
            </HeaderTemplate>
            <ItemTemplate>
                <%# DataBinder.Eval(Container, "DataItem.STATUS") %>
            </ItemTemplate>
        </asp:TemplateColumn>
        <asp:TemplateColumn>
            <HeaderTemplate>
                <strong><%# Resources.Amount %></strong>
            </HeaderTemplate>
            <ItemTemplate>
                <%# DataBinder.Eval(Container, "DataItem.AMT") %>
            </ItemTemplate>
            <EditItemTemplate>
                <asp:CustomValidator ID="cvAmountGrid" OnServerValidate="cvAmountGrid_ServerValidate" Display="None" runat="server" ControlToValidate="txtAmount" />
                <asp:TextBox ID="txtAmount" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.AMT") %>' CssClass="small" />
            </EditItemTemplate>
        </asp:TemplateColumn>
        <asp:TemplateColumn>
            <ItemTemplate>
                <asp:Button runat="server" Text="Edit" ID="btnEdit" Visible='<%# IsRowEditable(Eval("STATUS").ToString()) %>' CommandName="Edit" />
                <asp:Button runat="server" Text="Update" ID="btnUpdate" Visible="false" CommandName="Update" />
                <asp:Button runat="server" Text="Cancel" ID="btnCancel" Visible="false" CommandName="Cancel" />
            </ItemTemplate>
        </asp:TemplateColumn>
</asp:DataGrid>

Code Behind:

public bool IsRowEditable(string status)
{
    return !status.Equals("Locked", StringComparison.OrdinalIgnoreCase);
}

protected void grid1_EditCommand(object source, DataGridCommandEventArgs e)
{
    grid1.EditItemIndex = e.Item.ItemIndex;

    grid1.DataBind();

    TextBox t = e.Item.FindControl("txtAmount") as TextBox;

    t.Visible = true; //this can't be found

    Button b = e.Item.FindControl("btnEdit") as Button;

    b.Visible = false;

    Button u = e.Item.FindControl("btnUpdate") as Button;

    u.Visible = true;

    Button c = e.Item.FindControl("btnCancel") as Button;

    c.Visible = true;    
}

There are a few problems I've run into with this approach. Firstly, calling DataBind seems to reset the visibility status I've set on any buttons. If I don't databind, then my editable column doesn't show as editable. So, I try to set the textbox to be editable manually; but findcontrol returns null so I can't. What am I doing wrong here?

Upvotes: 0

Views: 445

Answers (2)

Buddhi Madarasinghe
Buddhi Madarasinghe

Reputation: 748

Another way to achieve it;

  • There I removed data validation and column name data binders to avoid complexities. You can try below with them.
  • Update and cancel buttons are under EditItemTemplate so they will appear at edit event.

.ASPX

<asp:DataGrid ID="grid1" runat="server" AutoGenerateColumns="false" EnableViewState="true" CssClass="GridviewControlStyle" CellSpacing="0" CellPadding="4" HeaderStyle-CssClass="HeaderStyle"
OnEditCommand="grid1_EditCommand" OnUpdateCommand="grid1_UpdateCommand" OnCancelCommand="grid1_CancelCommand" OnItemDataBound="grid1d_ItemDataBound">
<Columns>
    <asp:TemplateColumn>
        <HeaderTemplate>
            <strong>Status</strong>
        </HeaderTemplate>
        <ItemTemplate>
            <%# DataBinder.Eval(Container, "DataItem.STATUS") %>
        </ItemTemplate>
    </asp:TemplateColumn>
    <asp:TemplateColumn>
        <HeaderTemplate>
            <strong>Amount</strong>
        </HeaderTemplate>
        <ItemTemplate>
            <%# DataBinder.Eval(Container, "DataItem.AMT") %>
        </ItemTemplate>
     <EditItemTemplate>
            <asp:TextBox ID="txtAmount" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.AMT") %>' CssClass="small" ReadOnly="false"/>
        </EditItemTemplate>
    </asp:TemplateColumn>


    <asp:TemplateColumn>
        <ItemTemplate>
            <asp:Button runat="server" Text="Edit" ID="btnEdit" Visible='<%# IsRowEditable(Eval("STATUS").ToString()) %>' CommandName="Edit" />
         </ItemTemplate>
        <EditItemTemplate>
            <asp:Button runat="server" Text="Update" ID="btnUpdate"  CommandName="Update" />
            <asp:Button runat="server" Text="Cancel" ID="btnCancel"  CommandName="Cancel" />
       </EditItemTemplate>
    </asp:TemplateColumn>
    </Columns>

Code Behind

  • There you don't have set the visibility manually. EditItemTemplate will handle it at the edit event and when the user clicks cancel or update the Edit template will be replaced with the view.
  • I used some hardcoded list of Data Items and you can replace it with actual data source and try.

        List<DataItem> t;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            LoadData();
        }
    }
    
    private void LoadData()
    {
        t = new List<DataItem>();
        DataItem t1 = new DataItem() { AMT = 5, STATUS = "LOCKED" };
        DataItem t2 = new DataItem() { AMT = 15, STATUS = "OPEN" };
        DataItem t3 = new DataItem() { AMT = 25, STATUS = "OPEN" };
        DataItem t4 = new DataItem() { AMT = 35, STATUS = "LOCKED" };
        t.Add(t1);
        t.Add(t2);
        t.Add(t3);
        t.Add(t4);
        grid1.DataSource = t;
        grid1.DataBind();
    }
    
    
    
    protected void grid1_UpdateCommand(object sender, DataGridCommandEventArgs e)
    {
        string newAmount = (e.Item.Cells[1].FindControl("txtAmount") as TextBox).Text;
    
        //Update the data source with edited data
    
        grid1.EditItemIndex = -1;
    
        //Load Data with updated data
        LoadData();
    
    }
    
    protected void grid1_CancelCommand(object sender, DataGridCommandEventArgs e)
    {
        grid1.EditItemIndex = -1; //Bring back the previous state
        LoadData();
    }
    
    
    public bool IsRowEditable(string status)
    {
        return !status.Equals("Locked", StringComparison.OrdinalIgnoreCase);
    }
    
    protected void grid1_EditCommand(object source, DataGridCommandEventArgs e)
    {
        grid1.EditItemIndex = e.Item.ItemIndex;
        LoadData();
    }
    
    protected void grid1d_ItemDataBound(object sender, DataGridItemEventArgs e)
    {
    
    }
    

DataItem.cs

public class DataItem
{
    public int AMT { get; set; }
    public string STATUS { get; set; }
}

Upvotes: 1

Mansfield
Mansfield

Reputation: 15170

Okay, I figured out a better way to do this. I added the code below to my markup for all three buttons:

Visible='<%# IsRowEditing(Container.ItemIndex) %>'

In my code behind, I checked to see if the row was currently being edited, and used that to control the visibility of the buttons:

protected bool IsRowEditing(int index)
{
    return index > 0 && index == grid1.EditItemIndex;
}

This fixed all my issues and was much simpler than what I was trying to do previously. Databinding also works with this method.

Upvotes: 0

Related Questions