Link to home
Start Free TrialLog in
Avatar of Dukster131
Dukster131Flag for United States of America

asked on

Checkbox in Gridview

I want to use a Gridview to get a list of students in a classroom.  This list also includes an attendance checkbox and a reason for absence textbox.  I want to be able to click on the checkbox in the row to indicate the student is absent and that causes the textbox to be visible.  Once the reason is put in the textbox, I want to fire an event that will insert or update the value in the database.

What I have so far is;
<asp:GridView ID="AttendanceGrid" runat="server" AutoGenerateColumns="false"
        DataKeyNames="akoEmail" onrowdatabound="AttendanceGrid_RowDataBound">
    <Columns>
         <asp:TemplateField HeaderText="Name" SortExpression="NameLast">
         <ItemTemplate>                    
                        <asp:Label ID="lblFN" runat="server" Text='<%# Bind("NameFirst") %>'></asp:Label>
                        &nbsp;<asp:Label ID="lblLN" runat="server" Text='<%# Bind("NameLast") %>'></asp:Label>
             </ItemTemplate>
         </asp:TemplateField>
         <asp:TemplateField HeaderText="Absent" SortExpression="Present" ItemStyle-HorizontalAlign="Center">
         <ItemTemplate>
                        <asp:CheckBox ID="chkPresent" runat="server" OnCheckedChanged="chkAbsent_CheckedChanged"/>                  
                       
             </ItemTemplate>
         </asp:TemplateField>
         <asp:TemplateField HeaderText="Reason of Absence" SortExpression="roa">
         <ItemTemplate>
                        <asp:TextBox ID="txtReason" runat="server" TextMode="MultiLine" Rows="3" Visible="false" Enabled="false" />                  
                       
             </ItemTemplate>
         </asp:TemplateField>
         <asp:TemplateField HeaderText="Class Room" SortExpression="Phase1" ItemStyle-HorizontalAlign="Center">
         <ItemTemplate>
                        <asp:Label ID="lblCR" runat="server" Text='<%# Bind("Phase1") %>' />                  
                       
             </ItemTemplate>
         </asp:TemplateField>
         
   
    </Columns>
    </asp:GridView>

My checkedchanged event is:

protected void chkAbsent_CheckedChanged(object sender, EventArgs e)
    {
        CheckBox chkAbsent = (CheckBox)AttendanceGrid.FindControl("chkAbsent");
        TextBox txtReason = (TextBox)AttendanceGrid.FindControl("txtReason");

        if (chkAbsent.Checked)
        {
            txtReason.Visible = true;
            txtReason.Enabled = true;

        }
        else if(!chkAbsent.Checked)
        {
            txtReason.Visible = false;
            txtReason.Enabled = false;
        }
    }
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

I don't see any event handler for the TextBox.TextChanged event, or something to force the page to post-back (AutoPostBack)...
Avatar of Dukster131

ASKER

Well, right now i'm trying to get the textboxes to appear when I click the checkbox.  Right now when I click a checkbox nothing happens.
In order for the ASP.NET events to fire, the page needs to post-back, so that the event can be detected.  Usually, what you use is the AutoPostBack attribute to have the control automatically post back the page.
Ok.  I figured I had forgotten to do an autopostback.  Now I am getting a null exception error.  Where am I going to need to check on the checkbox for each row in the gridview?  Do I need to do that in the DataBound event?  Do I need to do an null check first?
"Now I am getting a null exception error"
Where are you getting that exception?
I'm getting it when I do the chkAbsent_CheckedChanged

protected void chkAbsent_CheckedChanged(object sender, EventArgs e)
    {
        CheckBox chkAbsent = (CheckBox)AttendanceGrid.FindControl("chkAbsent");
        TextBox txtReason = (TextBox)AttendanceGrid.FindControl("txtReason");

        if (chkAbsent.Checked)
        {
            txtReason.Visible = true;
            txtReason.Enabled = true;

        }
        else if(!chkAbsent.Checked)
        {
            txtReason.Visible = false;
            txtReason.Enabled = false;
        }
    }

At the if(chkAbsent.Checked) I am getting the message: NullReferenceException was unhandled by user code.
It is probably because of this line:

        CheckBox chkAbsent = (CheckBox)AttendanceGrid.FindControl("chkAbsent");

Are these static controls (defined in HTML), or dynamic controls (added in code)?
These controls are in the GridView and generated from the database.  I'm hoping to be able to do this in GridView with a checkbox in the item template and a textbox in the item template.   This is generated from a view that uses an outer join, so there are definitely null values in the database.  In actuality one of the tables that is used to generate this view is the table that the results will go into based upon who is checked.  
Is this the CheckBox?

<ItemTemplate>
    <asp:CheckBox ID="chkPresent" runat="server" OnCheckedChanged="chkAbsent_CheckedChanged"/>                  
</ItemTemplate>

In 'chkAbsent_CheckedChanged', the sender variable is 'chkPresent'.

You can get a reference like this:
    CheckBox chkAbsent = (CheckBox)AttendanceGrid.FindControl("chkPresent");

       --or--

    CheckBox chkPresent = (CheckBox)sender;
Yes, that is it.  You are correct in that it should have been FindControl("chkPresent"), however, after changing that I still get the same message about NullReferenceException was unhandled by user code.
Did you try the other one?

    CheckBox chkPresent = (CheckBox)sender;
Same result.
Actually, the error doesn't occur on the Checkbox, but on the Textbox now.  So this is progress.  
You should be use the GridView row, and not the top-level GridView with FindControl.  You should be able to get the current row from the SelectedIndex property:

GridViewRow row = AttendanceGrid.Rows[AttendanceGrid.SelectedIndex];
TextBox txtReason = (TextBox)row.FindControl("txtReason");
When I do the GridViewRow row = AttendanceGrid.Rows[AttendanceGrid.SelectedIndex];
it raises the ArgumentOutOfRangeException was unhandled by user code.
That would mean that SelectedIndex is -1.

I set up a small test page, and here is an example of what I would suggest for a solution:

    protected void chkSelect_CheckChecked(object sender, EventArgs e)
    {
        CheckBox chkSelect = (CheckBox)sender;
        TextBox firstName = (TextBox)chkSelect.Parent.Parent.FindControl("FirstName");
        TextBox lastName = (TextBox)chkSelect.Parent.Parent.FindControl("LastName");
    }

You can get the CheckBox.Parent.Parent to get the GridViewRow.
      <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:AdventureWorksConnectionString %>" SelectCommand="SELECT EmployeeID, Title, FirstName, LastName FROM HumanResources.vEmployee"></asp:SqlDataSource>
      <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="SqlDataSource1">
        <Columns>
          <asp:TemplateField HeaderText="LastName" SortExpression="LastName">
            <ItemTemplate>
              <asp:TextBox ID="LastName" runat="server" Text='<%# Eval("LastName") %>' />
              <asp:TextBox ID="FirstName" runat="server" Text='<%# Eval("FirstName") %>' />
              <asp:CheckBox ID="chkSelect" runat="server" OnCheckedChanged="chkSelect_CheckChecked" AutoPostBack="true" />
            </ItemTemplate>
          </asp:TemplateField>
        </Columns>
      </asp:GridView>

Open in new window

I do not have the AdventureWorks Database.
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Sorry.  I misunderstood what you were saying.  that worked great.  So tell me if you will, how does this TextBox txtReason = (TextBox)chkAbsent.Parent.Parent.FindControl("txtReason"); work?

I'm still learning .net and this one is interesting to me.   Any good references would be appreciated.
The Parent property is the containing element on the page.  You can chain these calls together.

In order to understand that concept, break it down into steps.

control.Parent = cell
cell.Parent = row

Simplified HTML representation:
<table>
   <tr>
        <td><asp:CheckBox /></td>
   </tr>
<table>

<td> = cell
<tr> = row
Ok.  Thanks.  That helps.  One more thing.  The one other thing I am going to need is the dataKey information(that is my userID) for the row.  That way when they enter a reason I will be able to get all the relevant information for the Attendance Table.  I know in the GridView itself the value is {0}.
If your GridView is bound to a data source, then you should be able to get the DataItem from the GridViewRow:

DataRowView drv = row.DataItem as DataRowView;
string primaryKey = row[AttendanceGrid.DataKeyNames];

When I type row I get an error message saying it doesn't exist in the current context.  I need to get the dataKeyName for the row that I am doing the checkbox and textbox in.  Is there a namespace or class that needs to be referenced to do what you are saying or does it need to happen in a different place?
Aaah, the never-ending question...

If row.DataItem is null, then when the page posts back, the data source is not reset.  Usually, I store the DataTable in a Session variable, and set the data source back in the Page_Load:

if (Page.IsPostBack)
{
    this.GridView1.DataSource = (DataTable)Session["MyTable"];
    this.GridView1.DataBind();
}
If you do that, won't you lose any of the information that has been filled in - won't it revert to what is in the database, or not in the database yet?

Just seems like their should be a way to get the datakey information for the row just like getting the checked value or text value.
If you make sure that the DataTable in the Session variable is current, then nothing will be lost.  You should create a small, test web site to see what I mean.  You can also get that information from a cell, but only if it is included in the rendered output.
I decided to put a label in the gridview with with the userid information and get it the same way that I am getting the textbox information.  Thus:

Label lblAKO = (Label)chkAbsent.Parent.Parent.FindControl("lblAKO");
        string strAKO = lblAKO.Text;

That way I have the information in a variable and can use it to update the database.

Thanks for all the help.