Please visit my new Web Site https://coderstechzone.com
Most of our web application needs to implement one-to-many relationships. So when we want to design such type of relationships in UI, at first we want to show the list of parents then show its corresponding child records. For example a company wants to see a list of suppliers as well as in one time also wants to see the product list that the supplier responsible to supply. For simplicity here i use datatable(one can fill datatable from database easily) to keep my example as simple as possible. Here i want to develop the below UI To display Master Detail record:
Here the main task is to design the gridviews. To do that here i am considering one master gridview & then i want to put another gridview in a template column to represent child records. Initially i will hide all corresponding child records by using javascript method & show a collapse icon in the detail column. So user can show hide the child records based on a master record. The aspx page code is given below:
Here the main task is to design the gridviews. To do that here i am considering one master gridview & then i want to put another gridview in a template column to represent child records. Initially i will hide all corresponding child records by using javascript method & show a collapse icon in the detail column. So user can show hide the child records based on a master record. The aspx page code is given below:
<asp:GridView runat="server" ID="gvMasterDetail" DataKeyNames="ID" OnRowDataBound="gvMasterDetail_RowDataBound">
<Columns>
<asp:BoundField ItemStyle-Width="5%" DataField="Code" HeaderText="Code"></asp:BoundField>
<asp:BoundField ItemStyle-Width="15%" DataField="Name" HeaderText="Name"></asp:BoundField>
<asp:BoundField ItemStyle-Width="15%" DataField="Address" HeaderText="Address"></asp:BoundField>
<asp:BoundField ItemStyle-Width="12%" DataField="Contact" HeaderText="Contact no"> </asp:BoundField>
<asp:TemplateField HeaderText="Details">
<ItemTemplate>
<img src="Image/Expand.jpg" onclick="ChildBlock(this,document.getElementById('<%#Eval("ID") %>'));" />
<div id='<%#Eval("ID") %>' style="display:none">
<asp:GridView runat="server" ID="gvChild" DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="Code" HeaderText="Code"></asp:BoundField>
<asp:BoundField DataField="Product Name" HeaderText="Product Name"></asp:BoundField>
<asp:BoundField DataField="Unit" HeaderText="Unit">
</asp:BoundField>
</Columns>
</asp:GridView>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<Columns>
<asp:BoundField ItemStyle-Width="5%" DataField="Code" HeaderText="Code"></asp:BoundField>
<asp:BoundField ItemStyle-Width="15%" DataField="Name" HeaderText="Name"></asp:BoundField>
<asp:BoundField ItemStyle-Width="15%" DataField="Address" HeaderText="Address"></asp:BoundField>
<asp:BoundField ItemStyle-Width="12%" DataField="Contact" HeaderText="Contact no"> </asp:BoundField>
<asp:TemplateField HeaderText="Details">
<ItemTemplate>
<img src="Image/Expand.jpg" onclick="ChildBlock(this,document.getElementById('<%#Eval("ID") %>'));" />
<div id='<%#Eval("ID") %>' style="display:none">
<asp:GridView runat="server" ID="gvChild" DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="Code" HeaderText="Code"></asp:BoundField>
<asp:BoundField DataField="Product Name" HeaderText="Product Name"></asp:BoundField>
<asp:BoundField DataField="Unit" HeaderText="Unit">
</asp:BoundField>
</Columns>
</asp:GridView>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
To show & hide child div records i am using a javascript method which is given below. You can also use this javascript method To show & hide a div:
function ChildBlock(img,obj)
{
if(obj.style.display=='none')
{
obj.style.display='';
img.src="Image/Collapse.jpg";
}
else
{
obj.style.display='none';
img.src="Image/Expand.jpg";
}
}
{
if(obj.style.display=='none')
{
obj.style.display='';
img.src="Image/Collapse.jpg";
}
else
{
obj.style.display='none';
img.src="Image/Expand.jpg";
}
}
Now we need to populate relational data. For simplicity i am using datatable. One can change this code to fit database data. The code under page load is:
if (!IsPostBack)
{
DataTable dtSupplier = new DataTable("Supplier");
dtSupplier.Columns.Add(new DataColumn("ID", System.Type.GetType("System.UInt64")));
dtSupplier.Columns.Add(new DataColumn("Code"));
dtSupplier.Columns.Add(new DataColumn("Name"));
dtSupplier.Columns.Add(new DataColumn("Address"));
dtSupplier.Columns.Add(new DataColumn("Contact"));
dtSupplier.Rows.Add(1, "st0001", "S.R. Steel", "Uttara, Dhaka", "01711xxxxxx");
dtSupplier.Rows.Add(2, "ir0039", "Shadesh builders", "Rampura, Dhaka", "01711yyyyyy");
dtSupplier.Rows.Add(3, "cr0042", "Orchard confec.", "Shahabag, Dhaka", "01711zzzzzz");
dtSupplier.Rows.Add(4, "er0078", "Windblow", "Mirpur, Dhaka", "01711qqqqqq");
dtSupplier.Rows.Add(5, "bd0301", "Rahimkarim", "Badda, Dhaka", "01711oooooo");
//Read Child Data Here
DataTable dtProducts = new DataTable("Products");
dtProducts.Columns.Add(new DataColumn("ID", System.Type.GetType("System.UInt64")));
dtProducts.Columns.Add(new DataColumn("Code"));
dtProducts.Columns.Add(new DataColumn("Product Name"));
dtProducts.Columns.Add(new DataColumn("Unit"));
dtProducts.Columns.Add(new DataColumn("SupplierID", System.Type.GetType("System.UInt64")));
dtProducts.Rows.Add(1, "PR0001", "AXE", "Dozen", 2);
dtProducts.Rows.Add(2, "PR0039", "LUX", "Qty", 2);
dtProducts.Rows.Add(1, "PR0001", "Dove", "Cartoon", 2);
Session["Products"] = dtProducts;
gvMasterDetail.DataSource = dtSupplier;
gvMasterDetail.DataBind();
}
{
DataTable dtSupplier = new DataTable("Supplier");
dtSupplier.Columns.Add(new DataColumn("ID", System.Type.GetType("System.UInt64")));
dtSupplier.Columns.Add(new DataColumn("Code"));
dtSupplier.Columns.Add(new DataColumn("Name"));
dtSupplier.Columns.Add(new DataColumn("Address"));
dtSupplier.Columns.Add(new DataColumn("Contact"));
dtSupplier.Rows.Add(1, "st0001", "S.R. Steel", "Uttara, Dhaka", "01711xxxxxx");
dtSupplier.Rows.Add(2, "ir0039", "Shadesh builders", "Rampura, Dhaka", "01711yyyyyy");
dtSupplier.Rows.Add(3, "cr0042", "Orchard confec.", "Shahabag, Dhaka", "01711zzzzzz");
dtSupplier.Rows.Add(4, "er0078", "Windblow", "Mirpur, Dhaka", "01711qqqqqq");
dtSupplier.Rows.Add(5, "bd0301", "Rahimkarim", "Badda, Dhaka", "01711oooooo");
//Read Child Data Here
DataTable dtProducts = new DataTable("Products");
dtProducts.Columns.Add(new DataColumn("ID", System.Type.GetType("System.UInt64")));
dtProducts.Columns.Add(new DataColumn("Code"));
dtProducts.Columns.Add(new DataColumn("Product Name"));
dtProducts.Columns.Add(new DataColumn("Unit"));
dtProducts.Columns.Add(new DataColumn("SupplierID", System.Type.GetType("System.UInt64")));
dtProducts.Rows.Add(1, "PR0001", "AXE", "Dozen", 2);
dtProducts.Rows.Add(2, "PR0039", "LUX", "Qty", 2);
dtProducts.Rows.Add(1, "PR0001", "Dove", "Cartoon", 2);
Session["Products"] = dtProducts;
gvMasterDetail.DataSource = dtSupplier;
gvMasterDetail.DataBind();
}
Now to bind child gridview i am implementing RowDataBound method of the master gridview. The code like:
protected void gvMasterDetail_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataTable dtProducts = GetProducts(Convert.ToUInt64(((DataRowView)e.Row.DataItem)["ID"]));
((GridView)e.Row.FindControl("gvChild")).DataSource = dtProducts;
((GridView)e.Row.FindControl("gvChild")).DataBind();
}
}
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataTable dtProducts = GetProducts(Convert.ToUInt64(((DataRowView)e.Row.DataItem)["ID"]));
((GridView)e.Row.FindControl("gvChild")).DataSource = dtProducts;
((GridView)e.Row.FindControl("gvChild")).DataBind();
}
}
In the above code segment i used a fuction named GetProducts. When gridview bind one master data then RowDataBound event will fire & call this method by master data id to populate child record. Here you can return a datasource to the child gridview based on master gridview Row DataItem ID value. Here i am showing how i filtered my child data:
private DataTable GetProducts(UInt64 nSupplierID)
{
DataTable dtProducts = (DataTable)Session["Products"];
DataTable dtClone = dtProducts.Clone();
foreach (DataRow oItem in dtProducts.Rows)
{
if (Convert.ToUInt64(oItem["SupplierID"]) == nSupplierID)
dtClone.Rows.Add(oItem.ItemArray);
}
return dtClone;
}
{
DataTable dtProducts = (DataTable)Session["Products"];
DataTable dtClone = dtProducts.Clone();
foreach (DataRow oItem in dtProducts.Rows)
{
if (Convert.ToUInt64(oItem["SupplierID"]) == nSupplierID)
dtClone.Rows.Add(oItem.ItemArray);
}
return dtClone;
}
Hope this example will help you. If you need more complex structure to design then read:
http://www.codeproject.com/KB/webforms/MasterDetail.aspx
3 comments:
thank you you are savior of my life :)
but I wonder is if effect the application 's performance if you cast a large table to session??
I can't find control gvchild in current context in my behind code.How to fix this. Thanks :)
Performance: I agreed. If hit count goes high then better to use data caching.
Can't find control gvchild: Please check your template column GridView name.
I WOULD BE DELIGHTED TO HEAR FROM YOU