The database for this page is
Project_Group_Type (Master Table)
columns (Basic ones):
| Project_Group_Type_ID | Description |
| 1 | New |
Project_Group (Details Table)
columns(Basic ones) :
| Project_Group_ID | Project_Group_Type_ID | Project_Group_Description |
| 1 | 1 | Check |
The DataSource
The data source for the Master Datagrid consists of these columns: Project_Group_Type_ID, Description
The data source for the Details Datagrid consists of these columns: Project_Group_ID Project_Group_Type_ID Project_Group_Description

Figure 1
The details Datagrid is expanded as shown in Figure 2.

Figure 2
Now the user can add/edit/delete row to the master/details Datagrid. Now I have created this as user control and used in other aspx pages.
The code is as follows. Declare the Datagrid with following attributes.
<asp:DataGrid ID="dgProjectGroupType" ShowFooter="True" ShowHeader="false" AutoGenerateColumns="False"
CellSpacing="0" BorderStyle="None" BorderWidth="0" GridLines="None" CellPadding="0" Width="100%" runat="server" OnItemDataBound="dgProjectGroupType_ItemDataBound" OnItemCommand="dgProjectGroupType_ItemCommand">
I have enabled show footer = true in order to add new record for this grid. I have added a new row with textbox and add button. And since I donot require the header have set it to false. I have following events for
- ItemDataBound: To bind the Inner grid with outer grid.
- ItemCommand: Handling the events on the master like add,edit,cancel...etc
<FooterStyle Font-Size="Smaller" />
<ItemStyle Font-Size="Smaller" />
<EditItemStyle BackColor="#CADDEE" Font-Size="Smaller" />
<SelectedItemStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" Font-Size="Smaller" />
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" Font-Size="Smaller" />
<HeaderStyle Font-Size="Smaller" />
<AlternatingItemStyle Font-Size="Smaller" />
This is style format I have used for grid, which self explanatory.
<Columns>
The "Columns" is declared under "<Columns>" tags since AutoGenerateColumns is "False".
<asp:TemplateColumn HeaderText="" >
<FooterTemplate>
<asp:TextBox ID="addDescription" runat="Server" />
</FooterTemplate>
<ItemTemplate>
<b> <%#DataBinder.Eval(Container.DataItem,"Description")%>
</b>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="storDescription"
Text=
'<%# DataBinder.Eval(Container.DataItem,"Description")%>'
runat="server" />
</EditItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="">
<ItemTemplate>
<asp:Label ID="projectGroupTypeID" runat="server" Text=
'<%# DataBinder.Eval(Container.DataItem, "Project_Group_Type_Id")%>' Visible="False" />
</ItemTemplate>
</asp:TemplateColumn>
This column is used for the editing the master records. This column is the primary key for this master table. Hence made invisible and retrived by the ID of lable and used in internal operations.
<asp:TemplateColumn HeaderText="">
<ItemTemplate>
<asp:DataGrid ID="dgProjectGroup" ShowFooter="True" ShowHeader="true" AutoGenerateColumns="False"
BorderWidth="0" GridLines="None" CellPadding="0" Width="100%" runat="server" OnItemCommand="dgProjectGroup_ItemCommand"
OnItemDataBound="dgProjectGroup_OnItemDataBound">
<FooterStyle Font-Size="Smaller" />
<ItemStyle Font-Size="Smaller" />
<EditItemStyle BackColor="#CADDEE" Font-Size="Smaller" />
<SelectedItemStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" Font-Size="Smaller" />
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" Font-Size="Smaller" />
<HeaderStyle Font-Size="Smaller" />
<AlternatingItemStyle Font-Size="Smaller" />
Here in the "asp:TemplateColumn" where columns are decalred, another datagrid is declared. I have following events for
- ItemDataBound : refer See code in css.
- ItemCommand : Handling the events on the details grid like add, edit, cancel...etc
This is style format I have used for grid, which self explainatary.
<Columns>
The "Columns" is declared under "<Columns>" tags since AutoGenerateColumns is "False".
<asp:TemplateColumn HeaderText="">
<ItemTemplate>
<asp:Label ID="projectGroupDescription" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Project_Group_Description")%>'
Width="275px" />
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtGroup" Text='<%# DataBinder.Eval(Container.DataItem, "Project_Group_Description") %>'
runat="server" Width="275px" />
</EditItemTemplate>
<FooterTemplate>
<asp:TextBox ID="addProject" runat="Server" Text="" />
<asp:TextBox ID="addProjectTypeID" runat="Server" Text="" Visible="False" />
</FooterTemplate>
</asp:TemplateColumn>
Then in the detials datagrid ,Iam displaying the project group description in an itemTemplate,displaying in textbox in edit mode and displaying a empty text on footer of the datagrid.
<asp:TemplateColumn>
<ItemTemplate>
<asp:Button ID="cmdEditprojectGroup" runat="server" Text="Edit" CommandName="EditGroup" />
<asp:Button ID="cmdDeleteprojectGroup" runat="server" Text="Delete" CommandName="DeleteGroup" />
</ItemTemplate>
<EditItemTemplate>
<asp:Button ID="cmdUpdateMeasure" runat="server" Text="Save" CommandName="SaveGroup" />
<asp:Button ID="cmdCancelEditprojectGroup" runat="server" Text="Cancel" CommandName="CancelGroup" />
</EditItemTemplate>
<FooterStyle VerticalAlign="Middle"></FooterStyle>
<FooterTemplate>
<asp:Button ID="cmdAddMeasure" CommandName="InsertGroup" runat="server" Text="Add" />
</FooterTemplate>
</asp:TemplateColumn>
Again in the details datagrid ,Iam displaying the Edit & delete buttons in an itemTemplate,displaying in save & cancel buttons in edit mode and displaying a add button on footer of the datagrid.
<asp:TemplateColumn HeaderText="" Visible="False">
<ItemTemplate>
<asp:Label ID="projectGroupTypeID" runat="server" Text='<%#DataBinder.Eval(Container.DataItem, "Project_Group_Type_ID")%>' Visible="False" />
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="" Visible="False">
<ItemTemplate>
<asp:Label ID="projectGroupID" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Project_Group_ID")%>' Visible="False" />
</ItemTemplate>
</asp:TemplateColumn>
In the details Datagrid, projectGroupTypeID and projectGroupID are used displayed on the itemtemplate but not visible to users. As these are used for edit and deleting operations.
</Columns>
</asp:DataGrid>
End of Inner DataGrid
</ItemTemplate>
End of ItemTemplare of Master DataGrid.
Start of EditItemTemplare for Master DataGrid.
And this is EditItemTemplate for this column when editing. So by another grid in the edititemtemplate you can see still see inner datagrid while editing master Datagrid.
<EditItemTemplate>
<asp:DataGrid ID="dgProjectGroup_Edit" AutoGenerateColumns="False" CellSpacing="0"
SkinID="datagridSkinID" CssClass="dataTable" BorderStyle="None" BorderWidth="0"
GridLines="None" CellPadding="0" Width="100%" runat="server" OnItemCommand="dgProjectGroup_ItemCommand"
OnItemDataBound="dgProjectGroup_OnItemDataBound">
<Columns>
` <asp:TemplateColumn HeaderText="">
<FooterStyle Font-Size="Small" Font-Bold="false" Wrap="false" BackColor="White" />
<ItemTemplate>
<asp:Label ID="projectGroupDescription" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Project_Group_Description")%>'
Width="275px" />
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="addProject" runat="Server" Text="" />
<asp:TextBox ID="addProjectTypeID" runat="Server" Text="" Visible="False" />
</FooterTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
</EditItemTemplate>
//end of EditItemTemplate
</asp:TemplateColumn>
//end of TemplateColumn
<asp:TemplateColumn>
<ItemTemplate>
<asp:Button ID="cmdEditMeasure" runat="server" Text="Edit Category" CommandName="EditCategory" />
<asp:Button ID="cmdDeleteProjectGroupType" runat="server" Text="Delete Category" CommandName="DeleteCategory"
OnClientClick="return confirm('Are you sure you wish to Delete all?');" />
</ItemTemplate>
<EditItemTemplate>
<asp:Button ID="btnUpdateMeasure" runat="server" Text="Save Category" CommandName="SaveCategory" />
<asp:Button ID="cmdCancelEditProjectGroupType" runat="server" Text="Cancel Category" CommandName="CancelCategory" />
</EditItemTemplate>
<FooterStyle VerticalAlign="Middle"></FooterStyle>
<FooterTemplate>
<asp:Button ID="cmdAddCategory" CommandName="InsertCategory" runat="server" Text="Add Category" />
</FooterTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
The code File aspx.cs is follows .......
#region Variables
private DataSet dsProject;
private DataTable dsProjectGroupType;
private DataTable dsProjectGroup;
private ProjectGroupsAndTypes projectGroupsAndTypes;
private string strprojectGroupTypeID;
private int typeCount;
#endregion
/// <summary>
/// Handles the Load event of the Page control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
protected void Page_Load(object sender, EventArgs e)
{
try
{
projectGroupsAndTypes = new ProjectGroupsAndTypes();
//The ProjectGroupsAndTypes is class which get does all //the operations such as getting records from
//database,adding records to the database, deleting //records to the database etc,,,,
//since is the effective way of taking the off code from //code behind file to bussiness service layer.
//init the tables
dsProjectGroupType = new DataTable();
dsProjectGroup = new DataTable();
//declare the projecttype and projectGroup tables
typeCount = 0;
strprojectGroupTypeID = string.Empty;
if (!IsPostBack)
{
LoadData();
//Bind the data tables to the DataGrid if it is not //postback
}
}
catch (System.Exception exception)
{
throw exception;
}
}
#region "GridData Project Group"
/// <summary>
/// Loads the data.
/// </summary>
private void LoadData()
{
try
{
//Get the Records from projectsGroupType Table, and the
//code to retrive the data is not shown here.As it is
//general area and quite simple
dsProjectGroupType = projectGroupsAndTypes.projectsGroupType();
dsProjectGroupType.TableName = "Master";
//Adding to the dataSet
//Get Detail Table
dsProjectGroup = projectGroupsAndTypes.projectGroup();
dsProjectGroup.TableName = "Detail";
//Declare new DataSet
dsProject = new DataSet();
//Adding to the dataSet
dsProject.Tables.Add(dsProjectGroupType);
//Adding to the dataSet
//DataSet used for relations between Master and Details tables.
dsProject.Tables.Add(dsProjectGroup);
//create the relation between dataTables
DataRelation relProjectGroups = new DataRelation("relProjectGroups",
new DataColumn[] { dsProject.Tables["Master"].Columns["Project_Group_Type_ID"] },
new DataColumn[] { dsProject.Tables["Detail"].Columns["Project_Group_Type_ID"] });
// Add the Relation to the dataset
dsProject.Relations.Add(relProjectGroups);
//assiging the dataSource to the dataGrid.
//Binding to the dataGrid (Master)
dgProjectGroupType.DataSource = dsProject.Tables["Master"].DefaultView;
dgProjectGroupType.DataBind();
}
catch (System.Exception exception)
{
throw exception;
}
}
/// <summary>
/// Handles the ItemDataBound event of the dgProjectGroupType control.
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.Web.UI.WebControls.DataGridItemEventArgs"/> instance containing the event data.</param>
//Here is where the detail grid is binded with data Source.
//Here there are two cases when it is an item or edititem.
//dgProjectGroup gets binds, when grid is not in edit mode.
//dgProjectGroup_Edit gets bind, when the gird is in edit mode.
//Note dgProjectGroup is again datagrid as dgProjectGroup.
protected void dgProjectGroupType_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.EditItem)
{
DataRowView drvDesc = (DataRowView)e.Item.DataItem;
if (e.Item.ItemType == ListItemType.EditItem)
{
// Bind the editable grid
((DataGrid)e.Item.FindControl("dgProjectGroup_Edit")).DataSource = drvDesc.CreateChildView("relProjectGroups");
((DataGrid)e.Item.FindControl("dgProjectGroup_Edit")).DataBind();
}
else
{
//binds the innergrid.
((DataGrid)e.Item.FindControl("dgProjectGroup")).DataSource = drvDesc.CreateChildView("relProjectGroups");
((DataGrid)e.Item.FindControl("dgProjectGroup")).DataBind();
}
}
}
/// Handles the ItemCommand event of the dgProjectGroupType control.
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="T:System.Web.UI.WebControls.DataGridCommandEventArgs"/> instance containing the event data.</param>
public void dgProjectGroupType_ItemCommand(object sender, DataGridCommandEventArgs e)
{
//Inserts the Project group Type in the Project Group Type Table
if (e.CommandName == "InsertCategory")
{
string txtDescription = ((TextBox)e.Item.Cells[0].FindControl("addDescription")).Text;
bool valid = true;
if ((txtDescription.Equals(string.Empty)))
valid = false;
{
//Whatever needed to be Done
//Outer Grid actions.
}
LoadData();
//Rebind the Grid
}
//Delete the Project group Type from the Project Group Type
if (e.CommandName == "DeleteCategory")
{
int ProjectGroupTypeID = Convert.ToInt32(((Label)e.Item.FindControl("projectGroupTypeID")).Text);
//Whatever is needed to perform Delete Action.
//like projectGroupsAndTypes.delete(ProjectGroupTypeID)
LoadData();
//Rebind the Grid
}
//edit the Grid
if (e.CommandName == "EditCategory")
{
dgProjectGroupType.EditItemIndex = e.Item.ItemIndex;
LoadData();
}
//Saves the category
if (e.CommandName == "SaveCategory")
{
//get the ProjectGroupTypeID to edit the record in the table
int ProjectGroupTypeID = Convert.ToInt32(((Label)(e.Item.Cells[1].Controls[1])).Text);
//get the ProjectGroupType or description to edit the record in the table
string ProjectGroupType = ((TextBox)e.Item.Cells[0].Controls[1]).Text;
//Do what ever required to so update
//projectGroupsAndTypes.updateProjectGroupType(ProjectGroupTypeID, ProjectGroupType);
//make Editable to reset and Bind the grid
dgProjectGroupType.EditItemIndex = -1;
LoadData();
}
//Cancel the action
if (e.CommandName == "CancelCategory")
{
//make Editable to reset and Bind the grid
dgProjectGroupType.EditItemIndex = -1;
LoadData();
}
}
//This is the important Procedure.Because the master ProjectTypeID is always binded with details Datagrid on the footer.
//So, whenever the a new record is added to the details grid the, we have the foreign key(ProjectTypeID).
//But there is no way around, other than this which I found to be quite simple and effective.
//On the footer event of ItemDataBoung, I am adding the ProjectTypeID to textBox(ID = addProjectTypeID).
//Since this events fires for every record od master table.
//I am increamenting the count(typeCount) such that detail table are matched to the master table.
//And this happens for the first time.But this event is fired whenever other edit event is fired on detials datagrid.
//And value stored in the textbox(ProjectTypeID) might be corrupted.
//So solution to this is else part.assiging the value which is already available in the details dataGrid.
public void dgProjectGroup_OnItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRowView drvDesc = (DataRowView)e.Item.DataItem;
strprojectGroupTypeID = drvDesc.Row["Project_Group_Type_Id"].ToString();
}
else if (e.Item.ItemType == ListItemType.Footer)
{
if (strprojectGroupTypeID.Equals(string.Empty))
{
((TextBox)e.Item.FindControl("addProjectTypeID")).Text = Convert.ToString((((DataTable)
projectGroupsAndTypes.projectsGroupType(programmeID)).Rows[typeCount])[0]);
}
else
{
((TextBox)e.Item.FindControl("addProjectTypeID")).Text = strprojectGroupTypeID;
strprojectGroupTypeID = "";
}
typeCount++;
}
}
/// Handles the ItemCommand event of the dgProjectGroup control.
/// /// </summary>
/// /// <param name="sender">The source of the event.</param>
/// /// <param name="e">The <see cref="T:System.Web.UI.WebControls.DataGridCommandEventArgs"/>
instance containing the event data.</param>
public void dgProjectGroup_ItemCommand(object sender, DataGridCommandEventArgs e)
{
//insert new record
if (e.CommandName == "InsertGroup")
{
int projectGroupTypeID = Convert.ToInt32(((TextBox)e.Item.FindControl("addProjectTypeID")).Text);
string txtinnerDescription = ((TextBox)e.Item.FindControl("addProject")).Text;
bool valid = true;
if ((txtinnerDescription.Equals(string.Empty)))
valid = false;
//Do ever is req here.or something like this.
//projectGroupsAndTypes.AddProjectGroup
//(projectGroupTypeID, txtinnerDescription);
//Load and rebind the data
LoadData();
}
//Delete the Record
else if (e.CommandName == "DeleteGroup")
{
int projectGroupID = Convert.ToInt32(((Label)(e.Item.FindControl("projectGroupID"))).Text);
//SomeCode to delete the projectGroupID
projectGroupsAndTypes.DeleteProjectGroup(projectGroupID)
//Bind and rebind the data
LoadData();
}
//edits inner group
else if (e.CommandName == "EditGroup")
{
//Another UserCode Goes here
//Declare a temporary dataGrid and get referred to details datagrid.
//Assingn the dataGird editItemIdex with Itemindex.
DataGrid tmpDg = (DataGrid)sender;
tmpDg.EditItemIndex = (int)e.Item.ItemIndex;
//delcare A dataview and get all records of the details //table.
//And now filter the dataview with the //Project_Group_Type_ID (foreign key in the details //table and primary in the
master table)
DataView dv = new DataView();
dv = projectGroupsAndTypes.projectGroup(programmeID).DefaultView;
dv.Sort = "Project_Group_Type_ID";
//The Project_Group_Type_ID is obtained by the following code.
object objExpr = ((Label)e.Item.FindControl("projectGroupTypeID")).Text;
//Now assigning the filtered dataview back to the datagrid(ref as detail dataGrid)
tmpDg.DataSource = dv.FindRows(objExpr);
tmpDg.DataBind();
}
// saves edited data
else if (e.CommandName == "SaveGroup")
{
int emp_id = e.Item.ItemIndex;
DataGrid tmpDg = (DataGrid)sender;
//retriving the ProjectGroupID from hidden control
int ProjectGroupID = Convert.ToInt32(((Label)e.Item.FindControl("projectGroupID")).Text);
//retriving the ProjectGroup from hidden control
string ProjectGroup = ((TextBox)e.Item.FindControl("txtGroup")).Text;
//performing some action to save
projectGroupsAndTypes.updateProjectGroup(ProjectGroupID, ProjectGroup);
//cancelling the edit eventby setting edititemindex to -1
tmpDg.EditItemIndex = -1;
//delcare A dataview and get all records of the details //table.
DataView dv = projectGroupsAndTypes.projectGroup(programmeID).DefaultView;
dv.Sort = "Project_Group_Type_ID";
//The Project_Group_Type_ID is obtained by the following code.
object objExpr = ((Label)e.Item.FindControl("projectGroupTypeID")).Text;
//Now assigning the filtered dataview back to the datagrid(ref as detail dataGrid)
tmpDg.DataSource = dv.FindRows(objExpr);
tmpDg.DataBind();
}
//cancel the action
else if (e.CommandName == "CancelGroup")
{
DataGrid tmpDg = (DataGrid)sender;
tmpDg.EditItemIndex = -1;
//delcare A dataview and get all records of the details //table.
DataView dv = projectGroupsAndTypes.projectGroup(programmeID).DefaultView;
dv.Sort = "Project_Group_Type_ID";
//The Project_Group_Type_ID is obtained by the following code.
object objExpr = ((Label)e.Item.FindControl("projectGroupTypeID")).Text;
//Now assigning the filtered dataview back to the datagrid(ref as detail dataGrid)
tmpDg.DataSource = dv.FindRows(objExpr);
tmpDg.DataBind();
}
}
#endregion

Join the conversation! Your thoughts help the community grow.