موضوع جديد أكتبه لكم لعل البعض يستفيد منه و هو عن ال Master/Detail Form
الكثير من الأفكار خلال زيارتنا لعدد من المواقع و بعضها يكون ساكنا و بعضها يكون Dynamic و هو الذي يهمنا كما نرى في المنتديات و المجلات الإلكترونية و العديد من التطبيقات الشبكية المنتشرة هنا و هناك على شبكة الإنترنت.
لو نفرض أن هناك تصنيفات و تقع تحتها عدد من المشاركات إعتدنا رؤيتها كترتيب للتصنيفات و عند الضغط على إحدها يتوجه بنا المتصفح لصفحة تعرض المشاركات التي تقع تحت هذا التصنيف و هذا بالعادة نراه في المنتديات و لكن نرى بعد التطبيقات تعرض التصنيفات و تحتها المشاركات كلا على حدة و هذا رأيناه في مثال ذكرناه في درس ال DataView و أحيانا نرى التصنيفات مذكورة بشكل عادي و عند الضغط على أحدها تظهر مشاركات هذا التصنيف تحته مباشرة و عند الضغط على تصنيف آخر تختفي مشاركات التصنيف الأول و تظهر مشاركات التصنيف الجديد.
بالعادة هذه البيانات تحتاج لوجود جدولين بينهم علاقة من نوع 1-M أو ما يطلق عليها اسم one_to_many و تسمى النماذج Forms التي تعتمد على هذه الجداول بإسم Master/Detail Forms و سنأخذ مثالا يعرض التصنيفات داخل DataList على شكل وصلات و عند الضغط على إحداها ستظهر الدروس التابعة لهذا التصنيف بنفس الصفحة داخل Repeater.
لنرى بداية شكل الصفحة التي ستظهر لنا في هذا الشكل :

الآن نأتي لتحليل الفكرة.
أولا نركز على ال DataList التي ستعرض التصنيفات و هذا الشيء اعتدنا عليه مرارا.
ثانيا نجعل أسماء التصنيفات عبارة عن وصلات تشعبية HyperLinks.
ثالثا نضيف حدث لل DataList عبارة عن ItemCommand.
رابعا نحدد ال DataKeyField و نجعله رقم التصنيف id و هو نفسه ال PK في الجدول.
خامسا عند الضغط على الوصلة أي التصنيف سوف يتم تنفيذ الحدث و بالتالي يتم تمرير رقم القسم له و الحدث يخص ربط ال Repeater بالبيانات حيث سيستغل رقم التصنيف ليجلب البيانات من جدول الدروس و يقوم بربطها بالأداة.
سادسا كل عنصر قمنا بإختياره و أقصد هنا التصنيف سوف يظهر بشكل عادي و ليس وصلة تشعبية من خلال الوسم SelectdItemTemplate.
نذكر الكود أولا ثم نقوم بتحليل أجزاءه :
<%@ Page Language="C#" Debug="True" %>
<%@ import Namespace="System.Data.OleDb" %>
<%@ import Namespace="System.Web.UI" %>
<%@ import Namespace="System.Web.UI.WebControls" %>
<script runat="server">
void BindDataList()
{
string prov="Provider=Microsoft.Jet.OleDb.4.0;Data Source=" + Server.MapPath("Library.mdb");
string SQL="SELECT * FROM Library_Categories";
OleDbConnection cn = new OleDbConnection(prov);
OleDbCommand cmd = new OleDbCommand(SQL,cn);
cn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
dl.DataSource= reader;
dl.DataBind();
reader.Close();
cn.Close();
}
void BindRepeater(string intCatID)
{
string prov="Provider=Microsoft.Jet.OleDb.4.0;Data Source=" + Server.MapPath("Library.mdb");
string SQL="SELECT * FROM Library_Lessons where catID=@catID";
OleDbConnection cn = new OleDbConnection(prov);
OleDbCommand cmd = new OleDbCommand(SQL,cn);
cmd.Parameters.Add("@catID",intCatID);
cn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
rp.DataSource= reader;
rp.DataBind();
reader.Close();
cn.Close();
}
void Page_Load()
{
if(!Page.IsPostBack)
{
BindDataList();
}
}
void dl_ItemCommand(Object source, DataListCommandEventArgs e) {
string intCatID;
dl.SelectedIndex=e.Item.ItemIndex;
BindDataList();
intCatID=dl.DataKeys[e.Item.ItemIndex>.ToString();
BindRepeater(intCatID);
}
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<asp : DataList id="dl" runat="server" OnItemCommand="dl_ItemCommand" DataKeyField="id">
<EditItemStyle borderstyle="Solid" bordercolor="Gray"></EditItemStyle>
<ItemTemplate>
<asp:LinkButton Runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"category")%>' />
</ItemTemplate>
<SelectedItemTemplate>
<i> <b><%# DataBinder.Eval(Container.DataItem,"category")%></b></i>
</SelectedItemTemplate>
</asp : DataList>
<asp:Repeater id="rp" runat="server">
<ItemTemplate>
<p align="right">
<a href='more.aspx?id=<%# DataBinder.Eval(Container.DataItem,"id")%>'> <%# DataBinder.Eval(Container.DataItem,"title")%> </a>
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</html>في منطقة الأكواد توجد أحداث و إجرائيات كالتالي :
BindDataList
BindRepeater
Page_Load
dl_ItemCommand
الأولى تقوم بربط أداة ال DataList مع البيانات المستخرجة من جدول التصنيفات و الثانية تقوم بربط ال Repeater بالدروس التابعة للتصنيف على حسب رقم التصنيف الممرر للإجرائية و الثالثة هي الحدث الخاص بتنفيذ تعليمات معينة عند تحميل الصفحة و الحدث الأخير يحدث في حال الضغط على أي وصلة من التصنيفات.
الآن نأتي لمنطقة ال HTML و نرى أننا أضفنا DataList كالتالي :
<asp : DataList id="dl" runat="server" OnItemCommand="dl_ItemCommand" DataKeyField="id"> <EditItemStyle borderstyle="Solid" bordercolor="Gray"></EditItemStyle> <ItemTemplate> <asp:LinkButton Runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"category")%>' /> </ItemTemplate> <SelectedItemTemplate> <i> <b><%# DataBinder.Eval(Container.DataItem,"category")%></b></i> </SelectedItemTemplate> </asp : DataList>
نرى أننا أضفنا خاصية OnItemCommand و التي تستدعي الحدث dl_ItemCommand عند الضغط على أي زر موجود داخل ال data list و أضفنا الخاصية DataKeyField لتحدد البيانات المميزة للسجل الذي اخترناه. ثم حددنا نمطا معينا للعنصر أو السجل الذي إخترناه من خلال EditItemStyle و بعد ذلك أضفنا الوسم ItemStyle و هو يعرض التصنيفات داخل LinkButton ثم أضفنا الوسم SelectedItem و الذي يعرض التصنيف الذي إخترناه و لكن بشكل عادي و خط مائل و عريض و ليس وصلة تشعبية كما كان في حالة ال ItemTemplate.
أيضا أضفنا Repeater و حددنا فيه أن ال ItemTemplate و الخاص بالعناصر التي ستظهر أن تكون عبارة عن وصلة تشعبية تذهب بنا لصفحة معينة و هذه الأداة لا تظهر إلا حال ربطها بالبيانات و التي سنعرف كيفيتها عند شرح الإجرائيات التي في منطقة الكودو طبعا هذا الجزء الخاص بال Repeater :
<asp:Repeater id="rp" runat="server"> <ItemTemplate> <p align="right"> <a href='more.aspx?id=<%# DataBinder.Eval(Container.DataItem,"id")%>'> <%# DataBinder.Eval(Container.DataItem,"title")%> </a> </ItemTemplate> </asp:Repeater>
الان نأتي لشرح الإجرائيات حسب تسلسل حدوثها.
void Page_Load()
{
if(!Page.IsPostBack)
{
BindDataList();
}طبعا كلنا نعرف هذا الحدث حيث أنه في حال أول زيارة للصفحة يتم ربطها بالبيانات و أقصد هنا ال DataList التي ستعرض التصنيفات و ذلك من خلال إستدعاء الإجرائية BindDataList.
void BindDataList()
{
string prov="Provider=Microsoft.Jet.OleDb.4.0;Data Source=" + Server.MapPath("Library.mdb");
string SQL="SELECT * FROM Library_Categories";
OleDbConnection cn = new OleDbConnection(prov);
OleDbCommand cmd = new OleDbCommand(SQL,cn);
cn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
dl.DataSource= reader;
dl.DataBind();
reader.Close();
cn.Close();
}هذه الإجرائية شرحناها كثيرا ولا نحتاج لإعادة شرحها.
يفترض الآن أن تظهر البيانات أي التصنيفات في ال DataList و لكن ماذا سيحدث عند الضغط على إحداها ؟؟
أولا سوف يتحول العنصر من حالة ال ItemTemplate إلى ال SelectedItemTemplate و طبعا نكون ضغطنا نحن بالأصل على ال LinkButton و بالتالي فعلنا الحدث dl_ItemCommand و الذي به الكود التالي :
void dl_ItemCommand(Object source, DataListCommandEventArgs e) {
string intCatID;
dl.SelectedIndex=e.Item.ItemIndex;
BindDataList();
intCatID=dl.DataKeys[e.Item.ItemIndex>.ToString();
BindRepeater(intCatID);
}نرى أننا عرفنا متغير نصي باسم intCatID و هو يحمل رقم التصنيف و جعلته من النوع string لسبب و هو أننا لو أردنا الحصول على رقم التصنيف من خلال الباراميتر e و الذي يحصل عليه من خلال خاصية ال DataList و هي ال DataKeyField فإن dl.DataKeys[e.Item.ItemIndex> سوف تعيد لنا الناتج من النوع Object لذا لجأنا للطريقة ToString لنحوله لنص. و قبلها جعلنا العنصر الذي إخترناه يدخل في حالة ال SelectedItemTemplate من خلال :
dl.SelectedIndex=e.Item.ItemIndex;
ثم قمنا بإستدعاء الإجرائية BindRepeater و مررنا لها رقم التصنيف و طبعا كلنا نعرف أن ال Repeater لا يظهر بيانات إلا عند ربطه بها و بالتالي حال إستدعاء الصفحة أول مرة لا تظهر لنا الدروس لأننا لم نختر أي تصنيف و أيضا لا تظهر البيانات في حال عدم وجود نتائج متطابقة مع جملة الإستعلام SQL أو مع ال DataView و هنا لم نستخدمها طبعا بل إستخدمنا جملة إستعلام فقط.
void BindRepeater(string intCatID)
{
string prov="Provider=Microsoft.Jet.OleDb.4.0;Data Source=" + Server.MapPath("Library.mdb");
string SQL="SELECT * FROM Library_Lessons where catID=@catID";
OleDbConnection cn = new OleDbConnection(prov);
OleDbCommand cmd = new OleDbCommand(SQL,cn);
cmd.Parameters.Add("@catID",intCatID);
cn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
rp.DataSource= reader;
rp.DataBind();
reader.Close();
cn.Close();
}كما نرى قمنا بعمل إتصال بقاعدة البيانات و استخدمنا إستعلاما مشروطا برقم التصنيف الذي مررناه ثم أنشأنا DataReader لجلب البيانات من جدول الدروس حسب رقم التصنيف الذي إخترناه و من ثم ربطناه بال Repeater و أغلقنا الإتصال.
كان هذا هو درسنا عن ال Master/Detail Forms و أتمنى أن تكونوا قد إستوعبتم هذا الدرس بكل سهولة فهو مهم للغاية.
محمد خليل أبو صلاح
