Reputation: 11
I wanted to find the ID of a checkbox and only single select one of multiple checkboxes. I don't want to use radio buttons though.
Here's what I did so far to find the ID of the checkboxes :
protected void gvChild_RowDataBound(object sender, GridViewRowEventArgs e)
{
foreach (GridViewRow row in gvParent.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
GridView gvChild = (GridView)row.FindControl("gvChild");
// Then do the same method for check box column
if (gvChild != null)
{
foreach (GridViewRow rowChild in gvChild.Rows)
{
if (rowChild.RowType == DataControlRowType.DataRow)
{
CheckBox chk = (CheckBox)rowChild.FindControl("CbList");
if (chk.Checked)
{
string strScript = "uncheckOthers(" + ((CheckBox)e.Row.Cells[0].FindControl("CbList")).ClientID + ");";
((CheckBox)e.Row.Cells[0].FindControl("CbList")).Attributes.Add("onclick", strScript);
testLabel.Text = strScript;
}
}
}
}
}
}
}
I use "uncheckOthers()" as my javascript function to single select one and only checkbox.
NOTE : This function works properly in a parent gridview.
Here's the code to that function :
function uncheckOthers(id) {
var elm = document.getElementsByTagName('input');
for (var i = 0; i < elm.length; i++) {
if (elm.item(i).id.substring(id.id.lastIndexOf('_')) == id.id.substring(id.id.lastIndexOf('_'))) {
if (elm.item(i).type == "checkbox" && elm.item(i) != id)
elm.item(i).checked = false;
}
}
}
Last , but not least the Markup (removed boundfields for security purposes):
<asp:GridView ID="gvParent" runat="server" AutoGenerateColumns="false" CssClass="list" OnRowDataBound="OnRowDataBound" OnSelectedIndexChanged="gvParent_SelectedIndexChanged" DataKeyNames="MoodleTemplateID">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<img alt="" src="Images/view16x16.png"/>
<asp:Panel ID="pnlChild" runat="server" Style="display: none">
<asp:GridView ID="gvChild" runat="server" AutoGenerateColumns="false" CssClass = "list" DataKeyNames="ID" OnSelectedIndexChanged="gvChild_SelectedIndexChanged" OnRowDataBound="gvChild_RowDataBound">
<Columns>
<asp:TemplateField>
<ItemStyle HorizontalAlign="Center" />
<ItemTemplate>
<asp:CheckBox ID="CbList" runat="server"/>
</ItemTemplate>
<HeaderStyle Width="20px" />
</asp:TemplateField>
<asp:CommandField ButtonType="Image" SelectImageUrl="Images/edit16x16.png" ShowSelectButton="True" SelectText="Editar">
<ItemStyle Width="10px" />
</asp:CommandField>
</Columns>
</asp:GridView>
</asp:Panel>
</ItemTemplate>
</asp:TemplateField>
<asp:CommandField ButtonType="Button" ShowSelectButton="True" SelectText="Editar">
<ItemStyle Width="10px" />
</asp:CommandField>
</Columns>
</asp:GridView>
Upvotes: 0
Views: 137
Reputation: 49039
Ok, first up, I am going to suggest that the main grid is a list view.
The reason is listview supports "span" a lot better. When I try to nest gv's, they tend to look like this:
But, nesting the gridview inside of a listview, then it works much better like this:
And really amazing, is that without extra code the expanded parts persist - without really any extra code.
So here is our markup - and I added a check box to the child gv in above.
<asp:ListView ID="ListView1" runat="server" DataKeyNames="ID" >
<ItemTemplate>
<tr style="">
<td><asp:Button ID="cmdView" runat="server" Text="+" OnClick="cmdView_Click" /></td>
<td><asp:Label ID="HotelNameLabel" runat="server" Text='<%# Eval("HotelName") %>' /></td>
<td><asp:Label ID="CityLabel" runat="server" Text='<%# Eval("City") %>' /></td>
<td><asp:Label ID="ProvinceLabel" runat="server" Text='<%# Eval("Province") %>' /></td>
<td><asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>' /></td>
</tr>
<tr>
<td colspan="5">
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table table-hover" style="display:none" >
<Columns>
<asp:BoundField DataField="Firstname" HeaderText="Firstname" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox id="ckSel" runat="server"
OnCheckedChanged="ckSel_CheckedChanged"
AutoPostBack="true" >
</asp:CheckBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table id="itemPlaceholderContainer" runat="server" Class = "table table-hover" >
<tr runat="server" style="">
<th runat="server">View</th>
<th runat="server">HotelName</th>
<th runat="server">City</th>
<th runat="server">Province</th>
<th runat="server">Description</th>
</tr>
<tr id="itemPlaceholder" runat="server">
</tr>
</table>
</LayoutTemplate>
</asp:ListView>
Ok, so here is our load up the grid (well, I used listview).
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
public void LoadGrid()
{
string strSQL = @"SELECT * FROM tblHotels WHERE ID in (select hotel_Id from People)
ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL,
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
ListView1.DataSource = cmdSQL.ExecuteReader();
ListView1.DataBind();
}
}
Ok, so run the above, and we get this:
Ok, now next bus stop is the + key. This is just a plane jane regular asp.net button. And I DID NOT bother with gridview or the listview built in events (selected index and all that jazzz - its too confusing and better yet we just don't care nor need to bother with the events of those gv, or lv's.
Ok, so our code is this: not too much!!!
protected void cmdView_Click(object sender, EventArgs e)
{
Button cmd = (Button)sender;
ListViewDataItem gVR = (ListViewDataItem)cmd.Parent;
GridView gChild = (GridView)gVR.FindControl("GridView2"); // pluck out the grid for this row
if (gChild.Style["display"] == "normal")
{
// if grid is already display, then hide it, and exit
gChild.Style["display"] = "none";
return;
}
gChild.Style["display"] = "normal";
string HotelPK = (string)ListView1.DataKeys[gVR.DataItemIndex]["ID"];
// only re-load if never loaded (can't re-load else blow out check boxes
if (gChild.Rows.Count == 0)
{
string strSQL = @"SELECT * from People where hotel_id = " + HotelPK;
using (SqlCommand cmdSQL = new SqlCommand(strSQL,
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
gChild.DataSource = cmdSQL.ExecuteReader();
gChild.DataBind();
}
}
}
Ok, so that will expand, colipase each row. and REALLY amazing is that state management works (it remembers the expand or collapse - and it WILL even remember the setting of the un-bound check box.
So, we expand say the first row. We get this:
Ok, so now the only part left is the checkbox click event. and again, not too much code:
protected void ckSel_CheckedChanged(object sender, EventArgs e)
{
CheckBox ckBox = (CheckBox)sender;
GridView gvChild = (GridView)ckBox.Parent.Parent.Parent.Parent;
GridViewRow gvRowClick = (GridViewRow)ckBox.Parent.Parent;
if (ckBox.Checked)
{
// user just checked this box - un-check all
foreach (GridViewRow gvRow in gvChild.Rows)
{
if (gvRow.RowIndex != gvRowClick.RowIndex)
{
CheckBox ckRowBox = (CheckBox)gvRow.FindControl("ckSel");
ckRowBox.Checked = false;
}
}
}
}
A few things:
As noted, I dumped use of the gridv/listv event model. (we just pick up the grid view/listview row using sender.parent.
And we don't load the child grids until expanded - this is good for performance.
and if it been expanded, and we click "+" again, we hide, but if we re-expand, then we don't hit the database to re-load (and we don't want to, since if we did, then the checkbox would be lost).
And quite amazing? the lv/gv does retain its state for us. So expanded parts even survice the checkbox post back code.
I did not write out back to the database, but it would be quite simple code to then process each list-view row, then get the gv, and then get the checked row (PK ID) and then write that back to the database or do whatever if we wanted to on some button outside of this lv/gv.
Upvotes: 1