• 0
محمد رضى

فك الخلط الحاصل بين الوضعين المتصل و المنفصل

سؤال

السلام عليكم و رحمة الله تعالى و بركاته...

تقديم :

في الآونة الأخيرة و أظن أنها تتزامن مع فترة بدء الدراسة للعام الحالي, كان النصيب الأوفر من أسئلة قسمي Visual Basic.NET و Ado.NET يتمحور حول مشاكل في الربط مع قواعد البيانات. القارئ المتمعن لأسئلة الأعضاء المبتدئين سيلاحظ الخلط الكبير الحاصل في الفهم لطريقتي الربط المنفصل و المتصل. أثناء المحاولة للإجابة على هكذا اسئلة تكون أول خطوة هي عمل بحث صغير في القسم عن موضوع مشابه طرح فيه نفس السؤال, الخطوة الثانية تكون في عمل بحث صغير عن درس فيه الإجابة على السؤال أما الخطوة الأخيرة فهي الإجابة المباشرة على السؤال المطروح. بالطبع, هناك العديد من الدروس في موضوع الربط بقواعد البيانات, لكن بعد الإجابة على سؤال ما يكون رد العضو السائل هو طلب مثال مبسط و جاهز به الإجابة عن السؤال. من هنا جاءت فكرة هذا الدرس حتى يكون مرجعا للمبتدئين يتم الرجوع إليه وقت الحاجة دون اللجوء إلى إنشاء مثال في كل مرة.

فكرة هذا الدرس تتمحور حول عدة مشاريع مبسطة كل منها مرتبط بقاعدة البيانات إما عن طريق الوضع المنفصل أو عن طريق الوضع المتصل. بعد دراسة الكود المرفق ستصبح, إن شاء الله, قادرا على التفريق بين الوضعين و ستتكون لذيك فكرة واضحة عن كل وضع ما يمكنك مستقبلا من إمكانية اتخاد قرار استخدام الوضع المناسب في أي مشروع تريد تطويره.

قبل الدخول في صلب الموضوع, دعونا نلقي نظرة خاطفة على بنية قاعدة البيانات التي سنستخدمها في كل المشاريع المرفقة مع هذا الدرس. حتى نبسط الدروس و الكود, سنستخدم قواعد البيانات من نوع Access حتى نتمكن من إرفاق قاعدة البيانات مع المشاريع بدون تعقيدات. قاعدة البيانات AccessDB.mdb مكونة من جدولين, الجدول الأول UserInfo مكون من أربع حقول و هي كالتالي :

- ID : من نوع LONG, مرجع التسجيل, مفتاح رئيسي بخاصية الترقيم التلقائي.

- Login : من نوع VARCHAR, حجمه 32 (حرف)

- Password : من نوع VARCHAR, حجمه 32 (حرف)

- GroupID : من نوع LONG, للربط مع الجدول GroupInfo

الجدول الثاني GroupInfo مكون من حقلين و هي كالتالي :

- ID : من نوع LONG, مرجع التسجيل, مفتاح رئيسي بخاصية الترقيم التلقائي.

- Name : من نوع VARCHAR, حجمه 32 (حرف)

كما هو ظاهر فالجدول الأول هو لحفظ معلومات عن مستخدم ما, تم تحجيمها لأصغر ما يمكن لتبسيط الموضوع. الجدول الثاني هو لحفظ اسماء المجموعات التي يمكن أن ينتمي لها المستخدم. لماذا جدول المجموعات؟.. لأن من بين الأسئلة الشائعة للأعضاء أيضا, كيفية ربط كومبوبوكس بجدول بقاعدة البيانات.

قبل التطرق للأمثلة و الأكواد لابد من التكلم قليلا عن الوضعين موضوع الدرس.

الوضع المنفصل :

نقصد بالوضع المنفصل عملية تحميل البيانات من القاعدة إلى الذاكرة الحية و العمل على هذه البيانات على الذاكرة الحية و بعد الإنتهاء من العمل حفظ البيانات في حالتها الجديدة رجوعا إلى قاعدة البيانات. سميت طريقة العمل هاته بالوضع المنفصل لأننا نقطع الإتصال بقاعدة البيانات مباشرة بعد تحميل البيانات و يبقى الإتصال منقطعا طوال مدة العمل على البيانات على الذاكرة الحية و لا نحتاج لربط الإتصال مرة أخرى بقاعدة البيانات إلا عند الحاجة لحفظ البيانات من الذاكرة الحية رجوعا إلى قاعدة البيانات و قد يكون هذا الوقت عند غلق التطبيق. هذه خطوات العمل بهذه الطريقة :

- إنشاء و تهيئة كائن ربط الإتصال بقاعدة البيانات من نوع OleDbConnection.

- إنشاء و تهيئة كائن تبادل البيانات بين التطبيق و قاعدة البيانات أي الكائن OleDbDataAdpater الذي يضع رهن إشارتنا أربع خصائص من نوع OleDbCommand كل واحدة لنوع من أنواع العمليات الممكنة على جدول على قاعدة البيانات, أقصد عمليات تحميل, تحديث, زيادة و حذف البيانات.

- إنشاء و تهيئة كائن من نوع DataSet الذي سيكون بمثابة نسخة من قاعدة البيانات على الذاكرة الحية. هذا الكائن سيجمع كل الجداول التي سيتم تحميلها من قاعدة البيانات عن طريق الكائن OleDbDataAdapter.

- الآن تأتي عملية قراءة البيانات عن طريق الكائن OleDbDataAdapter بالمناداة على الدالة Fill التي تنشئ لنا جدولا على الكائن من نوع DataSet و تحمل إليه البيانات المطابقة لجملة الاستعلام المحددة على الكائن OleDbDataAdapter.SelectCommand

...

- بعد العمل على البيانات الموجودة الآن على الذاكرة الحية, سنحتاج بطبيعة الحال إلى حفظ التغييرات رجوعا إلى قاعدة البيانات لأنه في حالة خطأ في التطبيق أو نظام التشغيل أو انقطاع في الكهرباء, التغييرات التي قام بها المستخدم لن يتم حفظها لأنها كلها تم القيام بها على الذاكرة الحية. لحفظ البيانات من الذاكرة الحية رجوعا إلى قاعدة البيانات نستخدم الدالة Update للكائن من نوع OleDbDataAdapter التي ستقوم باستعراض كل البيانات على الذاكرة الحية (مجموعات DataRow) حيث ستحذف الأسطر المحذوفة, ستقوم بتحديث الأسطر التي تم عليها تحديث, ستقوم بزيادة الأسطر الجديدة و ستتجاهل الأسطر التي لم يطرأ عليها تحديث. ملخص القول أن الدالة Update في حالة نجاحها تكون قد حولت البيانات على قاعدة البيانات إلى نسخة طبق الأصل من البيانات الموجودة على الذاكرة الحية.

لاحظوا معي طول الوصف للوضع المنفصل, هذا لأن Ado.NET تقوم مكان المطور بكل العمليات و ستتوضح الفكرة أكثر في جزء ربط جدول بـ DataGridView حيث سنحتاج فقط لتحميل البيانات بطريقة الوضع المنفصل ثم ستقوم DataGridView بتسيير كل العمليات من زيادة و تحديث و حذف من دون الحاجة لتدخل المطور و في النهاية سنقوم بحفظ التغييرات بأقل من خمسة أسطر من الكود.

الوضع المتصل :

سمي هذا الوضع بالمتصل لأن التطبيق يبقى على اتصال مع قاعدة البيانات في أي عملية يريد القيام بها. الوضع المتصل أصعب في تطويره من الوضع المنفصل لأنك في هذا الوضع تأخد زمام الأمور لتقوم بالعمليات بطريقة مشخصة حسب حاجيات التطبيق و تصميمه. الوضع المتصل لا يحتاج إلى حجز الكثير من الذاكرة الحية كما هو الحال في الوضع الآخر. الوضع المتصل كذلك يكون أكثر فعالية في حالة كبر حجم قاعدة البيانات حيث نستطيع تحميل فقط البيانات التي نحتاج في وقت من أوقات تشغيل البرنامج. يمكن أن ألخص الوضع المتصل في تنفيذ جمل SQL في كل مرة نريد القيام بعملية على البيانات على القاعدة و سنتطرق بالتفصيل لهذا الوضع مع أول مثال لدراسة الوضع المتصل.

أيضا, لاحظوا معي وصف الوضع المتصل. صغر وصفه إن دل على شيء فإنما يدل على أنه وضع مشخص أكثر منه وضع قياسي, أي أن له ارتباط وثيق بتصميم و حاجيات المشروع المختلفة اختلاف مشروع عن الآخر.

الأمثلة التي سنقوم بدراستها ستكون بصيغة نفس المشروع (واجهة المستخدم و قاعدة البيانات) و لكن سنرى كيفية ربط المشروع بقاعدة البيانات بالوضع المنفصل و ما يقابلها بالوضع المنفصل حتى تتكون للقارئ فكرة عامة عن نقط الاختلاف بين الوضعين و بالتالي فك الاختلاط الحاصل بين الوضعين المتصل و المنفصل.

يتبع...

4

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

29 إجابة على هذا السؤال .

  • 0

السلام عليكم اخي العزيز محمد رضى

وشكراً لك على هذه المبادرة الجميله منك

وارجو منك اخي العزيز ان يتوسع هذا الدرس ليكون دورة شاملة في برمجة قواعد البيانات

كما اود ان اطرح بعض الافكار لتكون عناوين لدروس مختلفة في هذه الدروة وهي

1- ربط ComboBox بحقل في قاعدة البيانات وربط TextBox بحقل ثاني من قاعدة البيانات معتمداً في قيمته على ComboBox

2- كيفية جعل ComboBox تحمل حقلين معاً .. مثلاً تكون (11010001 حساب صندوق فرع 1 ) بحيث يمثل (11010001) قيمة حقل ارقام الحسابات و (حساب صندوق فرع 1) يمثل قيمة اسم الحساب .

وفي الاخير شكراً لك اخي العزيز و رحم الله والديك واسكنهم في الحنة ووالدينا وجميع الاخوه في هذا المنتدى الحبيب

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم و رحمة الله تعالى و بركاته...

شكرا أخي m_b و أتمنى أن يرتقي الدرس إلى مستوى "الدورة" و أن يكون ذا فائدة للجميع إن شاء الله.

ربط DataGridView بجدول في الوضع المنفصل :

post-207152-1258847208_thumb.jpg

سنبدأ على بركة الله مع أول مثال من سلسلة الأمثلة المرفقة مع هذا الدرس. هذا المثال سيكون عبارة عن نموذج به مربع DataGridView نقوم بربطها مع الجدول UserInfo على قاعدة البيانات. تتم عملية تحميل البيانات وقت تشغيل التطبيق ثم نترك المستخدم يقوم بجميع العمليات المتاحة من زيادة و تحديث و حذف البيانات على الداتاجريدفيو و وقت غلق التطبيق ستتم عملية حفظ المعلومات رجوعا نحو الجدول على قاعدة البيانات. بهذه الطريقة سنكون قد قمنا بالتعامل مع قاعدة البيانات في الوضع المنفصل حيث أننا سنربط الإتصال بقاعدة البيانات وقت تشغيل التطبيق لتحميل البيانات, العمليات كلها ستتم على الذاكرة الحية (مجموعات DataRow) للجداول DataTable, ثم لنا عودة مع ربط الإتصال مرة أخرى مع قاعدة البيانات وقت غلق التطبيق لحفظ البيانات من الذاكرة الحية نحو قاعدة البيانات.

في الوضع المنفصل, نحتاج من البيانات المحملة من قاعدة البيانات أن تبقى على الذاكرة الحية. هذه البيانات تحتاج لحاوية لتجمعها و لتيسر علينا تسييرها, حاوية البيانات التي سنستعمل هي كائن من نوع DataSet الذي يمكننا أن نقول أنه سيكون نسخة على الذاكرة الحية طبق الأصل من قاعدة البيانات. و حتى تبقى البيانات متاحة طيلة مدة اشتغال التطبيق سنحتاج لتعريف كائن من نوع DataSet على مستوى النموذج Form :


Dim MyDataSet As DataSet
	 ' الداتاست التي ستكون الحاوية للجداول التي سيملأها الكائن مايأدابتر

لتحميل البيانات من قاعدة البيانات نحو DataSet, سنحتاج إلى كائن من نوع OleDbDataAdapter. هذا الكائن سنحتاج إليه وقت تحميل البيانات وقت تشغيل التطبيق ثم سنحتاج إليه أيضا وقت عملية حفظ التغييرات التي تمت على البيانات من الذاكرة الحية نحو قاعدة البيانات. لهذا, سنقوم أيضا بتعريف الكائن على مستوى النموذج Form :


Dim MyAdapter As New OleDbDataAdapter
	 ' كائن تحميل, تحرير, زيادة و حذف المعلومات

بعد تعريف الكائنين MyDataSet و MyAdapter, سنحتاج إلى تهيئتهما لنتمكن من تحميل البيانات و بما أننا سنقوم بتحميل البيانات وقت تشغيل التطبيق فكود التهيئة سنضعه داخل دالة الحدث Load للنموذج الرئيسي للتطبيق.

سنحتاج أولا لتحديد جملة الربط مع قاعدة البيانات :


Dim ConString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb"
		 ' جملة الربط

بعد ذلك, سنحتاج إلى إنشاء و تهيئة الكائن MyAdapter الذي يحتاج إلى جملة الربط مع قاعدة البيانات, أيضا جملة الاستعلام SQL SELECT التي سيقوم بتنفيذها وقت طلب تحميل البيانات. سنقوم أولا بإنشاء الكائن و ننادي على الباني الإفتراضي الذي يقبل جملة الاستعلام و جملة الربط :


MyAdapter = New OleDbDataAdapter("SELECT * FROM UserInfo", ConString)
		 ' إنشاء الكائن و تهيئته بكائن الربط و جملة الإستعلام

وقت تحديث البيانات من الذاكرة الحية نحو جدول على قاعدة البيانات, يقوم الكائن من فئة OleDbDataAdapter بتنفيذ جملة SQL UPDATE على كل تسجيل تم تحديث نسخته الموجودة على الذاكرة الحية DataRow. لتهيئة الكائن MyAdapter ليتمكن من تحديث البيانات, سنحتاج إلى تحديد الخاصية UpdateCommand بكائن من نوع OleDbCommand مهيئ بجملة التحديث SQL UPDATE :


MyAdapter.UpdateCommand = New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.UpdateCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.UpdateCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.UpdateCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
MyAdapter.UpdateCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
		 ' تهيئة كائن جملة تحرير التسجيلات مع البرامترز

جملة SQL UPDATE التي هيأنا بها الخاصية UpdateCommand يجب أن يتم فيها تحديد كل الحقول التي نريد من الكائن MyAdapter تحديثها و كل حقل نحدد له وسيط لتمرير القيمة الجديدة للحقل, نزيد الوسيط و هو كائن من نوع OleDbParameter إلى مجموعة الوسائط Parameters للكائن OleDbCommand عن طريق المناداة على الدالة Add. سنحتاج كذلك لشرط كفيل بتحديد التسجيل الذي سيتم تحديث قيمته دون المغامرة بتحديث تسجيلات أخرى عن طريق الخطأ. لهذا فلكل جدول على قاعدة البيانات AccessDB.mdb معرف وحيد غير قابل للتكرار بخاصية الترقيم التلقائي و معرف التسجيل هو الحقل ID. بهذه الطريقة, سنضمن أن المقطع WHERE ID = @ID سيقوم بتحديث التسجيل الوحيد المعرف بـ @ID لا غير.

عندما يجد الكائن MyAdapter سطر على الذاكرة الحية معلم عليه أنه قد تم حذفه, سيقوم بتنفيذ جملة SQL DELETE المحددة على الخاصية DeleteCommand بكائن من نوع OleDbCommand :


MyAdapter.DeleteCommand = New OleDbCommand("DELETE FROM UserInfo WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.DeleteCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
		 ' تهيئة كائن أمر الحدف مع البرمتر مرجع التسجيل المراد حذفه

لتحديد التسجيل الوحيد الذي سيتم حذفه دون المغامرة بحذف تسجيل آخر عن طريق الخطأ سنحدد الشرط WHERE لجملة الحذف بمعرف التسجيل و نحدد الوسيط @ID الذي سيستقبل المعرف.

عندما يجد الكائن MyAdapter سطرا على الذاكرة الحية غير موجود على قاعدة البيانات سيقوم بتنفيذ جملة SQL INSERT لزيادته على قاعدة البيانات. لتهيئة الكائن MyAdapter بإمكانية زيادة أسطر موجودة على الذاكرة الحية و غير موجودة على قاعدة البيانات, سنحتاج إلى تهيئة الخاصية InsertCommand بكائن من نوع OleDbCommand مهيئ بجملة SQL INSERT لتنفيذها وقت زيادة سطر جديد إلى قاعدة البيانات :


MyAdapter.InsertCommand = New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyAdapter.SelectCommand.Connection)
MyAdapter.InsertCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.InsertCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.InsertCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
		 ' تهيئة كائن أمر زيادة التسجيلات من الداتاست إلى الجدول على قاعدة البيانات

لتحديد البيانات التي ستتم تهيئة السطر الجديد بها, سنقوم بزيادة وسائط لكل حقل من حقول التسجيل إلا الحقل المعرف للتسجيل لأنه يتمتع بخاصية الترقيم التلقائي.

بعد أن أتممنا تهيئة الكائن MyAdapter, سنقوم بإنشاء و بتهيئة الكائن MyDataSet الذي سيستقبل البيانات التي سيقوم الكائن MyAdapter بتحميلها من قاعدة البيانات :


MyDataSet = New DataSet("AccessDB")
		 ' إنشاء و تهيئة الداتاست

بعد عملية الإنشاء يمكننا أن نقوم بعملية تحميل البيانات من القاعدة :


MyAdapter.Fill(MyDataSet, "UserInfo")
		 ' تحميل تسجيلا الجدول من قاعدة البيانات إلى الداتاست

بعد تحميل البيانات, يمكننا ربط مربع DataGridView مع الجدول UserInfo الذي تم تحميل محتواه للتو :


DataGridView1.DataSource = MyDataSet.Tables("UserInfo")
		 ' ربط الداتاجريدفيو بالداتاست أي الوضع المنفصل

السطر أعلاه كفيل وحده بملئ الداتاجريدفيو بمحتوى الجدول UserInfo. بما أن معرف التسجيل أي الحقل ID معرف على أن له خاصية الترقيم التلقائي فتغيير محتواه سيرفع استثناء و لتفادي ذلك سنقوم بحظر الكتابة على العمود الأول :


DataGridView1.Columns(0).ReadOnly = True
		 ' منع الكتابة على خلايا عمود المرجع

إذا أنهينا مرحلة التهيئة في السطر الأخير, سنلاحظ أن العمود الأخير المخصص لإضهار المجموعة التي ينتمي إليها المستخدم يضهر أرقاما مكان اسم المجموعة. إذا تتذكرون بنية قاعدة البيانات التي نعمل عليها, سوف تتذكرون أن هناك جدول آخر GroupInfo به أسماء المجموعات و الذي سنقوم بربطه مع الجدول UserInfo عبر الحقل GroupID الذي يشير إلى مرجع التسجيل الذي يحتوي على اسم المجموعة. لإضهار أسماء المجموعات و تمكين المستخدم من تحديد و اختيار المجموعة التي ينتمي إليها المستخدم سوف نقوم بتحميل أسماء المجموعات على جدول في الذاكرة الحية DataTable و إنشاء عمود من نوع DataGridViewComboBoxColumn و تهيئته حتى تظهر مربعات ComboBox للعمود لائحة المجموعات للمستخدم وقت الاختيار و اسم المجموعة التي ينتمي إليها في الحالة العادية.

كما قمنا بذلك سابقا, سنقوم بنفس الطريقة بتحميل محتوى الجدول GroupInfo من قاعدة البيانات إلى الذاكرة الحية. سنقوم بإنشاء كائن جديد من نوع OleDbDataAdapter و نهيئه هذه المرة فقط بجملة SQL SELECT لأننا فقط سنقرأ مجموعات المستخدمين و لن نقوم بعمليات زيادة, تحديث أو حذف عليها. بعد ذلك سننادي على الدالة Fill لتحميل البيانات ثم نحرر الكائن لأننا لن نحتاج إليه مرة أخرى :


Dim adptr As New OleDbDataAdapter("SELECT * FROM GroupInfo", MyAdapter.SelectCommand.Connection)
adptr.Fill(MyDataSet, "GroupInfo")
adptr.Dispose()
		 ' تحميل بيانات أسماء المجموعات

المرحلة القادمة من عملية التهيئة, سنقوم خلالها بحذف العمود الذي يظهر معرفات تسجيلات أسماء مجموعات المستخدمين بعمود آخر تكون كل خلية منه عبارة عن ComboBox تمكن المستخدم من اختيار مجموعة من بين المجموعات الموجودة على الجدول GroupInfo. سنقوم أولا بإنشاء عمود جديد من نوع DataGridViewComboBoxColumn :


Dim CmbColumn As New DataGridViewComboBoxColumn()
		 ' عمود الكمبوبوكس الجديد

بعد ذلك و قبل حذف العمود الأصلي, سنحتاج إلى نسخ بعض الخصائص كي يعمل العمود الجديد بنفس طريقة العمود الأصلي :


Dim OldColumn As DataGridViewTextBoxColumn = DataGridView1.Columns(DataGridView1.Columns.Count - 1)
' نسخ خصائص العمود الأصلي
CmbColumn.Name = OldColumn.Name
CmbColumn.DataPropertyName = OldColumn.DataPropertyName
CmbColumn.HeaderText = OldColumn.HeaderText
		 ' العمود العادي المراد تغييره

بعد ذلك سنقوم بحذف العمود الأصلي و زيادة العمود الجديد مكانه :


DataGridView1.Columns.Remove(OldColumn)
' زيادة عمود الكمبوبوكس
DataGridView1.Columns.Add(CmbColumn)
		 ' حذف العمود القديم

و أخيرا, نقوم بربط العمود أو مربعات الكمبوبوكس للعمود بالجدول GroupInfo الموجود على الذاكرة الحية الذي سيظهر الحقل Name و يخزن على الجدول UserInfo الحقل GroupID.ID :


CmbColumn.DataSource = MyDataSet.Tables("GroupInfo")
CmbColumn.DisplayMember = "Name"
CmbColumn.ValueMember = "ID"
		 ' ربط العمود بجدول أسماء المجموعات

الآن و قد قمنا بتحميل البيانات من القاعدة سيظهر النموذج الرئيسي و عليه مربع الداتاجريدفيو التي هي الآن غير مرتبطة بقاعدة البيانات و إنما بنسخة طبق الأصل عنها موجودة على الذاكرة الحية في شكل جداول DataTable تجمعها الحاوية DataSet.

نترك الآن المستخدم يقوم بعمله على البيانات التي تظهرها الداتاجريدفيو و تتيح للمستخدم إمكانية زيادة, تحديث و حذف أسطرها و كل هذه العمليات ستتم على الذاكرة الحية أما قاعدة البيانات فلنا معها عودة عندما يغلق المستخدم النموذج.

...

عندما يغلق المستخدم النموذج ستتم المناداة على دالة الحدث FormClosing التي سنقوم بزيادة كود حفظ التغييرات التي قام بها المستخدم على البيانات المتواجدة على الذاكرة الحية. الكود بسيط للغاية, نقوم بالمناداة على الدالة HasChanges للكائن MyDataSet الذي ترجع لنا true إذا ما قام المستخذم بعملية ما أدت إلى تغيير البيانات الموجودة على أحد جداول MyDataSet, في هذه الحالة نقوم بالمناداة على الدالة Update للكائن MyAdapter التي كما ذكرنا سابقا ستقوم بالتحقق من حالة البيانات الموجودة على الذاكرة الحية أي مجموعات DataRow المتواجدة داخل الجداول DataTable الذي مررناه إلى الدالة Update و تقوم بحذف الأسطر المحذوفة, زيادة الأسطر الجديدة و تحديث الأسطر التي تم بها تحديث للمحتوى :


If MyDataSet.HasChanges() Then
' حفظ التغييرات في جدول المستخدمين على قاعدة البيانات
MyAdapter.Update(MyDataSet.Tables("UserInfo"))
End If
		 ' إذا كانت هناك تغييرات

أتمنى أن يكون المثال الأول قد أدى عمله و قد وضح طريقة الربط المنفصل بالنسبة لمربع DataGridView و سنرى في مثال لاحق مشروعا آخر بنفس طريقة الربط و لكن هذه المرة ستكون مربعات النص و الكومبوبوكس.

المثال التالي سيكون نفس المشروع الحالي و لكن الفرق بينه و بين هذا المثال سيكون في طريقة الربط بقاعدة البيانات التي ستكون بالطبع بالوضع المتصل.

يتبع...

NotConnectedDGV.rar

تم تعديل بواسطه محمد رضى
4

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم اخي العزيز محمد رضى

تابع على بركت الله وحفظه

وارجو ان يكون لديك وقت كافي لتشرح بمثال الافكار التى ذكرتها

وإلى الامام دائماً

2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم ورحمة الله تعالى و بركاته...

ربط DataGridView بجدول في الوضع المتصل :

post-207152-1258907279_thumb.jpg

تقديم :

بعد ما رأينا معا في الجزء السابق كيفية ربط مربع داتاجريدفيو بجدول بقاعدة بيانات في الوضع المنفصل, سنقوم في هذا الجزء بأخد نفس المشروع المرفق مع الجزء السابق و إعادة كتابته حتى يعمل في الوضع المتصل حتى تتظح للقارئ المبتدئ الرؤية في الاختلاف بين الوضعين المتصل و المنفصل.

كما سبق أن ذكرته, الوضع المتصل وضع مشخص, أي يمكن تنفيذه بأكثر من طريقة. فكرت أن أقوم بزيادة أزرار للحذف و التحديث بجانب كل سطر كما جاء ذلك في أسئلة العديد من الأعضاء و لكنني اخترت في النهاية اتباع نفس طريقة برامج تسيير قواعد البيانات بحيث إذا تم حذف كود تسيير عمود الكومبوبوكس سيتمكن التطبيق من التعامل مع أي جدول كان.

طريقة الربط المتصل بالجدول يمكن تلخيصها في أنه وقت الخروج من سطر الداتاجريدفيو نتحقق من السطر هل هو جديد لنقوم بزيادته على الجدول أم هل تم تحديثه لنقوم بتحديث نسخته على الجدول على قاعدة البيانات. أما بالنسبة للحذف فسنلتقط الحدث المناسب من الداتاجريدفيو و نقوم بحذف التسجيل المناسب من الجدول على قاعدة البيانات. الكود كان سيكون أسهل بدون عمود الكومبوبوكس و لكن أسئلة دعم الكومبوبوكس كثيرة وسيكون من الجيد زيادة بعض أسطر الكود حتى تزيد الفائدة.

كما رأينا معا سابقا, استعملنا الجداول DataTable و الحاوية DataSet و OleDbDataAdapter لتحميل البيانات في الوضع المنفصل. في الوضع المتصل, لن نستعمل أيا من هذه الفئات, سنستعمل بدلا منها الفئة OleDbDataReader لتحميل البيانات من القاعدة لأن الفئة OleDbDataReader لها بنية خاصة للاستعمال في الوضع المتصل بتحميله التسجيلات واحدا واحدا من الجدول عند المناداة على الدالة Read. أما بالنسبة لعمليات الزيادة, التحديث و الحذف فستتم مباشرة على قاعدة البيانات بالتوازي مع حدوثها على الداتاجريدفيو.

تعريفات :

لمعرفة حالة السطر الحالي للداتاجريدفيو, سنحتاج إلى توابث لتحديد حالة السطر, الأول RowAdded وقت زيادة سطر جديد و الثاني RowEdited وقت تحديث محتوى خلية :


Private Const RowAdded As Integer = 1

' لتعليم سطور الداتاجريدفيو على أنها تمت تحرير محتوياتها
Private Const RowEdited As Integer = 2
	 ' لتعليم سطور الداتاجريدفيو على أنها تمت زيادتها

لتنفيذ جمل SQL, سنحتاج لكائن من نوع OleDbConnection لفتح وغلق قناة الإتصال بقاعدة البيانات و قت الحاجة :


Private MyConnection As OleDbConnection
	 ' كائن الربط

التعريفات السابقة كلها معرفة على مستوى النموذج Form لأننا سنحتاج إليها طول وقت تنفيذ التطبيق.

تحميل البيانات من القاعدة إلى الداتاجريدفيو :

كما هو الحال بالنسبة للوضع المنفصل, سنقوم أيضا في الوضع المتصل بتحميل البيانات إلى الداتاجريدفيو وقت تشغيل التطبيق أي في دالة الحدث Load للنموذج الرئيسي.

سنبدأ بإنشاء و تهيئة كائن الربط الذي سبق لنا تعريفه :


MyConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb")
		 ' إنشاء و تهيئة كائن الربط

بعد ذلك, سنقوم بتحميل أسماء المجموعات من الجدول GroupInfo إلى جدول على الذاكرة الحية لربطه بعد حين بعمود من نوع DataGridViewComboBoxColumn الذي سيمكن المستخدم من اختيار مجموعة من المجموعات على اللائحة على الكومبوبوكس التي تظهر مكان خلايا العمود :


Dim cmd As New OleDbCommand("SELECT * FROM GroupInfo", MyConnection)
Dim tbl As New DataTable("GroupInfo")
MyConnection.Open()
tbl.Load(cmd.ExecuteReader())
		 ' تحميل جدول المجموعات

تفاديا لتعقيد الكود أكثر, استعملت جدول DataTable مع أنني ذكرت أنه يستعمل في الوضع المنفصل لأن عملية قراءة اسماء المجموعات و حفظها في لائحة غير DataTable لربطها بالعمود ستعطي نفس النتيجة و لكن مع المزيد من أسطر الكود.

بعد ذلك سنقوم بإنشاء كائن من نوع OleDbCommand و تهيئته بجملة استعلام لتحميل البيانات من القاعدة :


cmd = New OleDbCommand("SELECT * FROM UserInfo", MyConnection)
		 ' تحميل جدول المستخدمين

بعد ذلك سنقوم بتنفيذ جملة الاستعلام بالمناداة على الدالة ExecuteReader التي سترجع لنا كائن من نوع OleDbDataReader الذي سيمكننا من التعرف على اسماء حقول الجدول و كذلك قراءة التسجيلات التي تم إيجادها على قاعدة البيانات :


Dim rdr As OleDbDataReader = cmd.ExecuteReader()
		 ' تحميل جدول المستخدمين تسجيل بعد تسجيل

بعد ذلك سوف نقوم بقراء اسماء حقول الجدول و زيادة عمود لكل حقل على الداتاجريدفيو :


If rdr.FieldCount <> 0 Then
' إنشاء عمود لكل جدول
For i As Integer = 0 To rdr.FieldCount - 1
Dim fld As String = rdr.GetName(i)
If fld <> "GroupID" Then
' زيادة العمود
DataGridView1.Columns.Add(fld, fld)
Else ' عمود خاص بجدول الكومبوبوكس به لائحة المجموعات
Dim cmb As New DataGridViewComboBoxColumn()
cmb.HeaderText = fld
' ربط جدول المجموعات بخلايا العمود من نوع كومبوبوكس
cmb.DataSource = tbl
cmb.DisplayMember = "Name"
cmb.ValueMember = "ID"
' زيادة العمود
DataGridView1.Columns.Add(cmb)
End If
Next
' العمود الأول لحقل تلقائي الترقيم فلا يجب تحريره من طرف المستخدم
DataGridView1.Columns(0).ReadOnly = True
End If
		 ' إذا كانت هناك حقول على الجدول

لاحظوا معي في الكود أعلاه أن عمود المجموعات له طريقة خاصة في التهيئة حيث سنربطه أو سنربط خلايا الكومبوبوكس فيه بالجدول GroupInfo حتى يتم إضهار اسم المجموعة و تمكين المستخدم من اختيار احدى المجموعات عن طريق مربع لائحة الكومبوبوكس التي تظهر مكان خلايا العمود. أيضا, العمود الأول سيقوم بإضهار مراجع التسجيلات التي لها خاصية الترقيم التلقائي و لهذا فقد حددنا خاصية "القراءة فقط" لها حتى نمنع المستخدم من تحريرها محتواها و الوقوع في الأخطاء.

بعد زيادة الأعمدة, نستطيع زيادة اسطر البيانات التي ستتم قرائتها بالتوازي مع عملية الزيادة على الداتاجريدفيو. الوضع المتصل بالطبع. الكود :


If rdr.HasRows Then
' قراءة السطر التالي من الجدول من قاعدة البيانات
While rdr.Read()
' زيادة السطر على الداتاجريدفيو
Dim row As DataGridViewRow = DataGridView1.Rows(DataGridView1.Rows.Add())
' إحالة محتوى الحقل إلى محتوى الخلية المناسبة
For i As Integer = 0 To rdr.FieldCount - 1
row.Cells(i).Value = rdr.GetValue(i)
Next
End While
End If
		 ' إذا كانت هناك سطور بالجدول

عند هذه النقطة, سنقطع الاتصال بقاعدة البيانات و نترك المستخدم يقوم بعمله على أسطر الداتاجريدفيو. كل عملية تتم على مستوى سطر ما من أسطر الداتاجريدفيو, سيتم تنفيذ جملة سكل الخاصة بها عندما ينتقل المستخدم من سطر إلى آخر...

زيادة سطر جديد/تسجيل جديد :

عندما يقوم المستخدم بالكتابة على السطر الأخير من الداتاجريدفيو, تقوم هذه الأخيرة بالمناداة على دالة الحدث UserAddedRow حيث سنقوم بتعليم السطر الجديد بتابث RowAdded بإحالته إلى الخاصية Tag للكائن الذي يمثل السطر داخل مجموعى أسطر الداتاجريدفيو Rows :


Private Sub DataGridView1_UserAddedRow(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewRowEventArgs) Handles DataGridView1.UserAddedRow
' تعليم السطر على أنه تمت زيادته
DataGridView1.Rows(e.Row.Index - 1).Tag = RowAdded
End Sub
	 ' إلتقاط حدث زيادة سطر جديد على الداتاجريدفيو

عملية زيادة السطر الجديد إلى الجدول على قاعدة البيانات ستتم بعدما ينتهي المستخدم من تحرير محتوى خلايا السطر ثم يقرر الانتقال إلى سطر آخر, في هذا الوقت, ستقوم الداتاجريدفيو بالمناداة على دالة الحدث RowValidating حيث سنقوم بزيادة السطر الجديد بعد فتح قناة الاتصال بقاعدة البيانات :


Dim cmd As New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = row.Cells(1).Value
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = row.Cells(2).Value
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = row.Cells(3).Value
' تنفيذ جملة سكل
n = cmd.ExecuteNonQuery()
			 ' إنشاء و تهيئة كائن تنفيذ جملة سكل

بعد ذلك, سنحتاج إلى الحصول على مرجع التسجيل الجديد لإضهاره مكان الخلية الفارغة المقروءة فقط على العمود ID :


cmd.CommandText = "SELECT @@IDENTITY FROM UserInfo"
row.Cells(0).Value = cmd.ExecuteScalar()
			 ' الحصول على مرجع التسجيل

سنحتاج إلى الحصول على مرجع التسجيل الجديد حتى نتمكن من تمريره لكود الحذف الذي يعتمد عليه لتمييز التسجيل المراد حذفه.

حذف سطر/تسجيل :

عندما يقوم المستخدم بالنقر على رأس سطر Row header تقوم الداتاجريدفيو بتحديد السطر كاملا و يكفي من المستخدم الضغط على الزر Delete من لوحة المفاتيح حتي يتم حذف السطر كاملا. قبيل تنفيذ عملية حذف السطر, ستقوم الداتاجريدفيو بالمناداة على دالة الحدث UserDeletingRow حيث سنقوم بالحصول على مرجع السطر المراد حذفه ثم نقوم بحذف التسجيل المعرف بنفس المعرف من الجدول على قاعدة البيانات :


Dim cmd As New OleDbCommand("DELETE FROM UserInfo WHERE ID=@ID", MyConnection)
' مرجع التسجيل الذي سيتم حذفه
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = e.Row.Cells(0).Value
' فتح قناة الاتصال
MyConnection.Open()
' تنفيذ جملة سكل
Dim n As Integer = cmd.ExecuteNonQuery()
' غلق قناة الاتصال
MyConnection.Close()
' تحرير الكائن
cmd.Dispose()
		 ' إنشاء و تهيئة أمر سكل

تحديث سطر/تسجيل :

عندما يقوم المستخدم ببدء الكتابة على خلية ما, ستقوم الداتاجريدفيو بالمناداة على دالة الحدث CellEndEdit حيث سنقوم بتعليم السطر الحالي على أنه تم تحديث محتواه عن طريق إحالة التابث RowEdited إلى الخاصية Tag للكائن الممثل للسطر إذا لم يكن معلم مسبقا على أنه سطر جديد في هذه الحالة نترك السطر بعلامة سطر جديد حتى لا يرفع استثناء بسبب محاولة تحديث سطر غير موجود على الجدول على قاعدة البيانات :


Private Sub DataGridView1_CellEndEdit(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
' حتى لا يتم تغيير علامة الزيادة بعلامة التحديث
If DataGridView1.Rows(e.RowIndex).Tag Is Nothing Then
' تعليم السطر على أنه تم تحديته
DataGridView1.Rows(e.RowIndex).Tag = RowEdited
End If
End Sub
	 ' تعليم السطر الذي تم تغيير قيمة خليته على أنه تم تحديث محتواه

كما هو الحال بالنسبة لعملية زيادة سطر جديد, سنترك المستخدم يقوم بتحديث محتويات الخلايا التي يريد حتى يقرر الخروج من السطر الحالي و الانتقال لسطر آخر حيث ستقوم الداتاجريدفيو بالمناداة على دالة الحدث RowValidating حيث سنقوم بتحديث السطر عن طريق تنفيذ جملة سكل لتحديث التسجيل الذي له نفس المعرف على الجدول على قاعدة البيانات :


Dim cmd As New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = row.Cells(1).Value
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = row.Cells(2).Value
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = row.Cells(3).Value
' مرجع التسجيل الذي سيتم تحديته
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = row.Cells(0).Value
' تنفيذ جملة سكل
n = cmd.ExecuteNonQuery()
			 ' إنشاء و تهيئة كائن تنفيذ جملة سكل

لمن لم يلاحظ منكم مسألة هامة, هي أن عمليتي التحديث و الزيادة تتمان كلتاهما في دالة الحدث RowValidating و قيمة الخاصية Tag للسطر هي التي تحدد العملية التي يجب القيام بها, زيادة سطر جديد أم تحديث سطر موجود. بالطبع و لتبسيط فهم الدرس تم التغاضي عن إدراج بعض الأكواد المساعدة الموجودة في المشروع المرفق حيث ستجدون الكود كاملا معلق عليه بالتفصيل.

أتمنى لمن قرأ هذا الجزء و الجزء السابق أن تكون فكرة كل وضع قد اتضحت له و أصبح يفرق بين الوضعين و أن أكون قد نجحت بتوفيق من الله في فك الخلط الحاصل بين الوضعين المتصل و المنفصل. لا بالطبع, هذه ليست نهاية الدرس. لاحقا سوف أقوم بكتابة مثالين آخرين واحد مرتبط مع قاعدة بيانات بالوضع المتصل و آخر بالوضع المنفصل و لكن ليس عن طريق DataGridView و لكن مربعات نص و مربعات كومبوبوكس و غيرها حتى تتضح الفكرة أكثر و أكثر.

يتبع...

ConnectedDGV.rar

تم تعديل بواسطه محمد رضى
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم و رحمة الله تعالى و بركاته...

ربط نموذج بقاعدة بيانات في الوضع المنفصل :

post-207152-12590918365891_thumb.jpg

تقديم :

رأينا في الجزئين السابقين من هذا الدرس كيفية ربط جدول مع مربع DataGridView في الوضعين المتصل و المنفصل. كل وضع له خصائصه و أدواته التي نستعملها وقت الحاجة للقيام بالعمل المطلوب تنفيذه. في هذا الجزء, سنعود مع دراسة لمثال جديد يعمل في الوضع المنفصل و لكن هذه المرة سنقوم بربط مربعات نص و مربع كومبوبوكس لنموذج مع قاعدة البيانات. طريقة الربط هذه تتطلب منا التعامل مباشرة مع التسجيلات لأننا و في لحضة ما ستكون مربعات النموذج تظهر و تتيح التعامل مع تسجيل واحد فقطفي كل مرة.

طريقة الربط مع قاعدة البيانات هي نفسها التي سبق و تكلمنا عنها في الجزء السابق الذي يتلكم عن الداتاجريدفيو و الوضع المنفصل. سنقوم بربط الإتصال مع قاعدة البيانات وقت تشغيل التطبيق لتحميل البيانات ثم نقطع الاتصال, طول مدة تشغيل التطبيق سنعمل على البيانات الموجودة على الذاكرة الحية و في نهاية التطبيق, نعيد ربط الاتصال لنقوم بحفظ التغييرات رجوعا نحو قاعدة البيانات. و سنتيح للمستخدم إمكانية حفظ البيانات في أي لحضة عن طريق زر مخصص لهذا العمل.

المرجو قراءة الجزء المخصص لربط مربع داتاجريدفيو في الوضع المنفصل قبل المتابعة...

تعريفات :

لتحميل و تخزين البيانات, سنحتاج لحاوية الجداول DataSet و لكائن تبادل البيانات بين التطبيق و قاعدة البيانات OleDbDataAdapter. الكائنين المعرفين أسفله يتم تعريفهما على مستوى النموذج Form لإننا سنحتاج لخدماتهما طوال مدة تشغيل التطبيق :


' كائن تحميل, تحرير, زيادة و حذف المعلومات
Private MyAdapter As New OleDbDataAdapter
' الداتاست التي ستكون الحاوية للجداول التي سيملأها الكائن مايأدابتر
Private MyDataSet As DataSet

تحميل البيانات :

كما هو الحال في التعامل مع قواعد البيانات في الوضع المنفصل, عملية تحميل البيانات تتم وقت تشغيل التطبيق. في حالة ما إذا كانت قاعدة البيانات تحوي بيانات كثيرة يمكن أن تكون عملية تحميل و حفظ البيانات على مستوى نموذج من نماذج التطبيق حتى يتم تحرير الذاكرة الحية من البيانات بمجرد غلق النموذج.

في هذا الجزء لن نتطرق لشرح كود تحميل البيانات, شرح الكود بالتفصيل تجدونه في الجزء المخصص لربط مربع داتاجريدفيو في الوضع المنفصل.


' جملة الربط
Dim ConString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb"
' إنشاء الكائن و تهيئته بكائن الربط و جملة الإستعلام
MyAdapter = New OleDbDataAdapter("SELECT * FROM UserInfo", ConString)
' تهيئة كائن جملة تحرير التسجيلات مع البرامترز
MyAdapter.UpdateCommand = New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.UpdateCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.UpdateCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.UpdateCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
MyAdapter.UpdateCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر الحدف مع البرمتر مرجع التسجيل المراد حذفه
MyAdapter.DeleteCommand = New OleDbCommand("DELETE FROM UserInfo WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.DeleteCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر زيادة التسجيلات من الداتاست إلى الجدول على قاعدة البيانات
MyAdapter.InsertCommand = New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyAdapter.SelectCommand.Connection)
MyAdapter.InsertCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.InsertCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.InsertCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
' إنشاء و تهيئة الداتاست
MyDataSet = New DataSet("AccessDB")
' تحميل تسجيلات الجدول من قاعدة البيانات إلى الداتاست
MyAdapter.Fill(MyDataSet, "UserInfo")

' تحميل بيانات أسماء المجموعات
Dim adptr As New OleDbDataAdapter("SELECT * FROM GroupInfo", MyAdapter.SelectCommand.Connection)
adptr.Fill(MyDataSet, "GroupInfo")
adptr.Dispose()

بعد ذلك, سنقوم بربط مربع ComboBox الذي سيقوم بمهمة إضهار المجموعة التي ينتمي إليها المستخدم المضهر في التسجيل الحالي و يمكن المستخدم كذلك من إمكانية اختيار مجموعة بين المجموعات الموجودة على الجدول GroupInfo. الخاصية DataSource نحدد فيها الجدول من فئة DataTable الموجود فيها أسماء المجموعات. الخاصية DisplayMember نحدد فيها اسم الحقل الذي يحوي اسم المجموعة. الخاصية ValueMember نحددها على اسم الحقل الذي يحوي مرجع التسجيل التلقائي الترقيم لاسم المجموعة الذي يربطها بالجدول UserInfo و الحقل GroupID :


' ربط كومبوبوكس مجموعات المستخدمين مع الجدول الموجود على الذاكرة الحية
GroupComboBox.DataSource = MyDataSet.Tables("GroupInfo")
GroupComboBox.DisplayMember = "Name"
GroupComboBox.ValueMember = "ID"

بعد ذلك, سنقوم بربط كل مربع من مربعات النموذج بالحقل المناسب من حقول الجدول UserInfo, نستعمل الخاصية DataBindings لمربعات التحكم للمنادة على دالتها Add التي نحدد من خلالها خاصية المربع التي نريد ربطها مع حقل لجدول ما على الحاوية DataSet. مربعات النص نربط الخاصية Text لكل منها مع الحقل المناسب حتى تظهر معلومات التسجيل الحالي أوتوماتيكيا و يتم تحديث محتوى حقل التسجيل عندما يقوم المستخدم بتحديث محتوى مربع النص. بالنسبة لمربع الكومبوبوكس فسنقوم بربط الخاصية SelectedValue التي تمثل معرف تسجيل الجدول GroupInfo الذي يمثل اسم المجموعة التي ينتمي إليها المستخدم. المعامل الأخير للدالة Add نحدد من خلاله أن عملية تحديث محتوى مربع التحكم يتم وقت حدوث الحدث Validated.


' ربط مربعات النموذج مع الحقول الموجودة على الذاكرة الحية
IDTextBox.DataBindings.Add("Text", MyDataSet, "UserInfo.ID", True, DataSourceUpdateMode.OnValidation)
NameTextBox.DataBindings.Add("Text", MyDataSet, "UserInfo.Login", True, DataSourceUpdateMode.OnValidation)
PasswordTextBox.DataBindings.Add("Text", MyDataSet, "UserInfo.Password", True, DataSourceUpdateMode.OnValidation)
GroupComboBox.DataBindings.Add("SelectedValue", MyDataSet, "UserInfo.GroupID", True, DataSourceUpdateMode.OnValidation)

بعد ذلك, سنقوم بتسجيل دالة للحدث Positionchanged للكائن BindingManagerBase الذي سيقوم بالمناداة على الدالة UserInfo_Positionchanged في كل مرة يقوم فيها المستخدم بالانتقال من تسجيل إلى تسجيل آخر حتى نتمكن من إضهار بعض المعلومات عن التسجيل الحالي للمستخدم :


' تحديد دالة الحدث تغيير التسجيل الحالي لإضهار معلومات
' حول التسجيل الحالي للمستخدم
AddHandler Me.BindingContext(MyDataSet, "UserInfo").PositionChanged, AddressOf UserInfo_PositionChanged

الفئة BindingManagerBase تقوم بعمليات التحديث المتزامن بين الحقول و مربعات التحكم المرتبطة بينها على نفس النموذج Form. للحصول على الكائن من فئة BindingManagerBase لجدول ما :


Dim mngr as BindingManagerBase = BindingContext(MyDataSet, "TableName")

الكائن الذي نحصل عليه يمكننا من الوصول إلى التسجيلات DataRow عن طريق الخاصية Current و يمكننا من تحديد و إضهار أي تسجيل عن طريق تحديد رقم التسجيل في الخاصية Position كما يمكننا من معرفة عدد التسجيلات في الجدول المرتبط عن طريق الخاصية Count...

بعد ذلك, سنقوم بتهيئة الحقل ID للجدول UserInfo الموجود على الذاكرة الحية على الحاوية MyDataSet ليصبح كمثيله على قاعدة البيانات تلقائي الترقيم بتحديد الخاصية AutoIncrement على true. لتحديد العدد الذي يبدأ منه الترقيم التلقائي, نقوم بتحديده في الخاصية AutoIncrementSeed. لتحديد خطوة الترقيم التلقائي عند زيادة تسجيل جديد, نقوم بتحديد العدد المناسب في الخاصية AutoIncrementStep.


' تهيئة الترقيم التلقائي للتسجيلات الموجودة على الداكرة الحية
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrement = True
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementStep = 1
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementSeed = 1

للوصول إلى الكائن الممثل لحقل ما DataColumn لجدول ما DataTable :


MyDataSet.Tables("TableName").Columns("FieldName")

بعد التهيئة الأولية للحقل التلقائي الترقيم, نحتاج لتحديث الخاصية AutoIncrementSeed كي تبدأ عند أكبر معرف تسجيل للجدول UserInfo موجود على قاعدة البيانات حتى يكون هناك تزامن بين معرف التسجيل على الذاكرة الحية و بين التسجيل على قاعدة البيانات. التزامن هنا تقريبي لعدة عوامل, أولها أنه للحصول على آخر معرف تلقائي الترقيم يجب قبله القيام بعملية زيادة تسجيل على الجدول على قاعدة الباينات حتى نحصل على نتيجة صحيحة عن طريق تنفيذ استعلام لـ @@IDENTITY التي ترجع 0 في الحالة المعاكسة. مرجع التسجيلات الموجودة على الذاكرة الحية سيكون أقرب إلى الحقيقة بعد زيادة أول تسجيل و حفظ على قاعدة البيانات بعد إعادة تشغيل التطبيق.


' الحصول على أكبر مرجع للتسجيلات لاستعماله في حساب مرحع التسجيلات
' الموجودة على الذاكرة الحية
If BindingContext(MyDataSet, "UserInfo").Count > 0 Then
' الحصول على أكبر معرف في الجدول
Dim rows() As DataRow = MyDataSet.Tables("UserInfo").Select("MAX(ID)=ID")
' حفظ المرجع للاستعمال
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementSeed = rows(0)("ID") + 1
End If

التحرك بين التسجيلات :

الآن و قد تم تحميل البيانات من قاعدة البيانات إلى الذاكرة الحية. سنلاحظ و كما هو ضاهر في الصورة أعلاه وجود معلومات عن مستخدم واحد فقط عكس مارأيناه في الجزئين السابقين على مربع الداتاجريدفيو التي تعرض جميع التسجيلات مرة واحدة. لتمكين المستخدم من استعراض جميع الجداول, قمنا بزيادة أزرار أعلى النموذج للتحرك إلى التسجيل الأول, الأخير, السابق و اللاحق. عملية التحرك إلى تسجيل معين تتم غالبا عن طريق تحديد رقم التسجيل في الخاصية Position للكائن من فئة BindingManagerBase :


' الوصول إلى أول تسجيل
Me.BindingContext(MyDataSet, "UserInfo").Position = 0

' الوصول إلى التسجيل السابق
Me.BindingContext(MyDataSet, "UserInfo").Position -= 1

' الوصول إلى التسجيل اللاحق
Me.BindingContext(MyDataSet, "UserInfo").Position += 1

' الوصول إلى آخر تسجيل
Dim mngr As BindingManagerBase = Me.BindingContext(MyDataSet, "UserInfo")
mngr.Position = mngr.Count - 1

زيادة تسجيل جديد :

في الوضع المنفصل, يتم التعامل مباشرة مع التسجيلات DataRow الموجودة على الذاكرة الحية لعمليات الزيادة, التحديث و الحذف و لكن عملية ربط مربعات التحكم بالحقول تعفينا من التعامل المباشر مع كائنات DataRow حيث تقوم الفئة BindingManagerBase بعمل التحديث المتزامن للبيانات الموجودة على مربعات التحكم للنموذج مع التسجيلات DataRow.

في الحالات العادية, المناداة على الدالة BindingManagerBase.AddNew كفيلة وحدها لزيادة تسجيل جديد DataRow على الجدول DataTable. في بعض الحالات, يحتاج التسجيل إلى تهيئة خاصة قبل زيادته إلى الجدول. لتشخيص عملية زيادة تسجيل جديد, نقوم أولا بالحصول على تسجيل جديد أي كائن من فئة DataRow للتسجيل الجديد عن طريق المناداة على الدالة NewRow للجدول DataTable المراد زيادة تسجيل جديد فيه. كائن DataRow الذي تم إرجاعه له بنية تسجيل من نوع تسجيلات الجدول DataTable من عدد الحقول و نوع قيمة كل حقل و غيره :


' الحصول على تسجيل جديد على الذاكرة الحية
Dim row As DataRow = MyDataSet.Tables("UserInfo").NewRow()

بعد ذلك, نقوم بتنفيذ أمر بدء تحرير حقول التسجيل row :


' طلب بدء عملية تحرير حقول التسجيل
row.BeginEdit()

بعد ذلك, نستطيع تحديد المحتوى الافتراضي لكل حقل من حقول التسجيل الجديد :


' تحديد المحتوى الأولي للحقول
row("Login") = "<Login_" + row("ID").ToString() + ">"
row("Password") = "<Password_" + row("ID").ToString() + ">"
row("GroupID") = 1

بعد عملية تحرير محتوى حقول تسجيل DataRow, نكمل العملية بالمناداة على الدالة EndEdit :


' أمر إنهاء عملية التحرير
row.EndEdit()

حتى الآن, التسجيل الذي قمنا بتهيئته لا ينتمي إلى أي جدول. لزيادته إلى الجدول DataTable نقوم بزيادته إلى مجموعة تسجيلات الجدول عن طريق المناداة Add للخاصية Rows للجدول المراد زيادة التسجيل إليه :


' زيادة التسجيل الجديد إلى الجدول على الذاكرة الحية
MyDataSet.Tables("UserInfo").Rows.Add(row)

يجب الانتباه إلى أن التسجيل تمت زيادته في آخر مجموعة تسجيلات الجدول, لذا سنحتاج إلى الانتقال إلى آخر تسجيل لتمكين المستخدم من بدء تحرير محتوى التسجيل :


' التسجيل الجديد تتم زيادته في آخر الجدول
' لذا نصل إلى آخر تسجيل
GoLastButton.PerformClick()

للحؤول دون كتابة كود زيادة مثل هذا, نستطيع تحديد قيمة افتراضية لكل حقل وقت زيادة الحقل على البرنامج مسير قواعد البيانات كـ Access أو SQL Server Management Studio أو غيره حتى نستطيع زيادة تسجيل جديد بأقل عدد ممكن من أسطر الكود.

تحديث تسجيل :

بالنسبة لعملية تحديث التسجيلات, الكائن الذي يقوم بالتحديث المتزامن للبيانات يقوم بالعمل كله. عندما تقوم بتغيير محتوى مربع مرتبط بحقل يقوم الكائن من فئة BindingManagerBase بتحديث محتوى الحقل المرتبط بالمربع على التسجيل الحالي DataRow أوتوماتيكيا قبيل الانتقال إلى مربع تحكم آخر أي بعد أو عند حدث التأكد من محتوى المربع Validated. و عند التحرك إلى تسجيل آخر, يقوم نفس الكائن بتحديث محتوى المربعات بمحتويات الحقول المرتبطة بها للتسجيل الذي تم الوصول إليه.

حذف تسجيل :

عملية حذف تسجيل من الجدول الموجود على الذاكرة الحية عملية بسيطة بساطة التطبيق الذي نحن بصدد دراسته الآن. يكفي المناداة على الدالة RemoveAt للكائن من فئة BindingManagerBase و تمرير رقم التسجيل DataRow المراد حذفه حتى تتم العملية :


Dim mngr As BindingManagerBase = BindingContext(MyDataSet, "UserInfo")
' خروج إذا لم تكن هناك تسجيلات على الجدول على الذاكرة الحية
If mngr.Count <= 0 Then Return
' حذف التسجيل الحالي من الجدول الموجود على الذاكرة الحية
mngr.RemoveAt(mngr.Position)

حفظ البيانات :

بعد القيام بعمليات الزيادة, الحذف و التحديث حسب متطلبات العمل نحتاج إلى حفظ البيانات من الذاكرة الحية رجوعا نحو قاعدة البيانات. الكود هو نفسه الذي سبق و درسناه في الجزء الأول. نقوم أولا بالتحقق من وجود تغييرات على البيانات الموجودة على الذاكرة الحية. في حالة وجودها, نقوم بالتأكد من قيمة مربع التحكم الحالي ثم نقوم بإنهاء عملية تحرير التسجيل الحالي و في الأخير, نقوم بعملية الحفظ :


' إذا كانت هناك تغييرات على البيانات الموجودة على الذاكرة الحية
If MyDataSet.HasChanges() Then
' نطلب التحقق من المعلومات المكتوبة على مربعات النموذح
Me.Validate()
' إنهاء تحرير التسجيل الحالي الموجود على الذاكرة الحية
Me.BindingContext(MyDataSet, "UserInfo").EndCurrentEdit()
' حفظ البيانات الموجودة على الجدول على الذاكرة الحية
' رجوعا نحو الجدول على قاعدة البيانات
MyAdapter.Update(MyDataSet.Tables("UserInfo"))
End If

الكود أعلاه يتم تنفيذه عند الضغط على الزر Save و ستجدون على المشروع المرفق أنه يتم تنفيذ نفس الكود عند غلق التطبيق بعد أخد موافقة المستخدم.

رأينا في هذا الجزء كيفية ربط قاعدة بيانات مع مربعات تحكم على نموذج في الوضع المنفصل حيث يتم التعامل مع نسخة طبق الأصل من قاعدة البيانات على الذاكرة الحية. و حيث أن البيانات موجودة على الذاكرة الحية فأي خطأ في التطبيق أو نظام التشغيل أو انقطاع كهرباني سيؤدي مباشرة إلى فقدان البيانات التي لم يتم حفظها من الذاكرة الحية إلى قاعدة البيانات.

في الجزء القادم من سلسلة الأمثلة المدروسة هذه, سوف نقوم بتحرير نفس مشروع هذا الجزء ليقوم بنفس العمل في الوضع المتصل حتى تتظح بإذن الله فكرة كل وضع عند القارئ المبتدئ و معا إن شاء الله حتى يتم فك الخلط الحاصل بين الوضعين المتصل و المنفصل.

يتبع...

NotConnectedForm.rar

تم تعديل بواسطه محمد رضى
2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

موضع دائما نحتاجه

جزيت خيرا

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم و رحمة الله تعالى و بركاته...

ربط مربعات نموذج في الوضع المتصل؟

post-207152-12593511636579_thumb.jpg

تقديم :

استعرضنا في الجزء السابق معا كيفية ربط مربعات نموذج بجداول قاعدة بيانات في الوضع المنفصل. في هذا الجزء, سنقوم بتحرير نفس المشروع المرفق مع الجزء السابق كي يعمل نفس العمل الذي يقوم به و ذلك في الوضع المتصل. لمن وصل مباشرة إلى هذا الجزء, أرجو منه مراجعة الأجزاء السابقة.

لندخل مباشرة في الموضوع...

تعاريف :

في الوضع المتصل سنحتاج إلى طريقة للانتقال بين تسجيلات الجدول UserInfo و لذلك سنخصص دالة خاصة لهذا الغرض و لتحديد التسجيل التالي المراد الوصول إليه ستحتاج الدالة إلى معرف وجهة الانتقال الذي سنحدده كالتالي :


' تحديد وجهة قراءة التسجيل نحو...
Private Enum RecordPosition
First ' التسجيل الأول
Previous ' التسجيل السابق
This ' التسجيل الحالي
[Next] ' التسجيل اللاحق
Last ' التسجيل الأخير
End Enum

كما هي العادة, سنحتاج إلى كائن جاهز لربط الاتصال بقاعدة البينات في أي لحظة نحتاج إلى ذلك :


' كائن الربط بقاعدة البيانات
Private MyConnection As OleDbConnection

الدالة GoToRecord :

حتى لا نقوم بتكرير أكواد الانتقال من تسجيل لآخر, سنقوم بكتابة دالة مخصصة لهذا الغرض تقوم بإدارة عملية الانتقال بين تسجيلات الجدول UserInfo. عملية الانتقال ترتكز في عملها على الحقل ID للجدول للتعرف على التسجيل التالي المراد الانتقال إليه و عندما تحدد مرجع التسجيل تقوم بتحميله من قاعدة البيانات ثم تظهر محتوى الحقول على مربعات النص للنموذج. كما تقوم الدالة قبيل الانتقال من التسجيل الحالي بالتحقق من إذا ما قام المستخدم بتغيير محتوى أي مربع و في هذه الحالة تقوم بتحديث التسجيل على قاعدة البيانات بالمحتوى الجديد.

للدالة GoToRecord معاملين اثنين. الأول CurID, من نوع Long نحدد من خلاله مرجع التسجيل الحالي الذي نريد الانتقال منه. الثاني RecPos, من نوع RecordPosition نحدد من خلاله وجهة الانتقال إلى التسجيل التالي. أما بالنسبة للقيمة المرجعة فهي من نوع Boolean تدل على نجاح عملية الانتقال في حالة True.

أولا, ستقوم الدالة GoToRecord بعملية التأكد من تغيير محتوى مربعات النموذج و عند أي تغيير ستقوم مباشرة بتحديث التسجيل على قاعدة البيانات بالمحتويات الجديدة.


' إذا تم تغيير محتوى إحدى مربعات النموذج
If NameTextBox.Modified Or PasswordTextBox.Modified Or (GroupComboBox.SelectedValue <> GroupComboBox.Tag) Then
' تحديث محتوى التسجيل على قاعدة البيانات
If IDTextBox.Text <> "-1" Then SaveButton_Click(Nothing, EventArgs.Empty)
End If

بعد ذلك, يتم التعرف على وجهة الانتقال و إنشاء جملة استعلام للحصول على مرجع التسجيل التالي المراد الوصول إليه ليتم تنفيذ الجملة.


' فتح قناة الاتصال
MyConnection.Open()
' الحصول على مرجع التسجيل المراد الوصول إليه
Dim query As String = ""
Select Case RecPos
Case RecordPosition.First ' استعلام مرجع أول تسجيل أي أصغر تسجيل
query = "SELECT MIN(ID) FROM UserInfo"
Case RecordPosition.Last ' استعلام مرجع آخر تسجيل أي أكبر معرف
query = "SELECT MAX(ID) FROM UserInfo"
Case RecordPosition.Next ' استعلام مرجع التسجيل اللاحق أي أصغر معرف أكبر من المعرف الحالي
query = "SELECT MIN(ID) FROM UserInfo WHERE ID > @CurID"
Case RecordPosition.Previous ' استعلام مرجع التسجيل السابق أي أكبر معرف أصغر من المعرف الحالي
query = "SELECT MAX(ID) FROM UserInfo WHERE ID < @CurID"
Case RecordPosition.This ' فقط لإعادة معرف التسجيل الحالي
query = "SELECT @CurID"
End Select
' إنشاء و تهيئة كائن أمر سكل
Dim cmd As New OleDbCommand(query, MyConnection)
' زيادة البرمتر الذي يحدد مرجع التسجيل الحالي
cmd.Parameters.Add("@CurID", OleDbType.BigInt).Value = CurID
' تنفيذ أمر إرجاع مرجع التسجيل التالي
Dim o As Object = cmd.ExecuteScalar()

إذا تم ارجاع مرجع تسجيل صحيح, نقوم بتنفيذ جملة استعلام لتحميل التسجيل المعرف عن طريق كائن من نوع OleDbDataReader و إضهار محتوى حقوله على مربعات النموذج. الحالات التي لا نحصل فيها على معرف التسجيل هي عندما نريد الانتقال من التسجيل الأخير إلى ما بعده أو العكس أي عندما نريد الانتقال من التسجيل الأول إلى ما قبله, في هاتين الحالتين الكود أعلاه سيرجع لنا قيمة فارغة DBNull حيث ستفشل العملية و نخرج من الدالة مباشرة بدون رفع أي استثناء.


' إذا تم إرجاع فراغ مكان المعرف فهذا يدل
'أننا موجودين عند أول أو آخر تسجيل في الجدول
If Not o Is DBNull.Value Then
NextID = o
' الوصول إلى التسجيل
cmd.CommandText = "SELECT * FROM UserInfo WHERE ID = @CurID"
cmd.Parameters("@CurID").Value = NextID
'' تحميل محتوى التسجيل
Dim rdr As OleDbDataReader = cmd.ExecuteReader()
'' قراءة محتوى التسجيل
rdr.Read()

بعد ذلك, نقوم بقراءة محتويات حقول التسجيل الذي تم تحميله من قاعدة البيانات و نظهرها على مربعات النموذج.


' عرض التسجيل على مربعات التحكم
IDTextBox.Text = rdr("ID")
NameTextBox.Text = rdr("Login")
PasswordTextBox.Text = rdr("Password")
GroupComboBox.SelectedValue = rdr("GroupID")
GroupComboBox.Tag = rdr("GroupID") ' محتوى الكومبوبوكس يكون تغير إذا كانت SelectedValue <> Tag
'' غلق القارئ و تحرير موارد النظام
rdr.Close()
' نجحت العملية
rslt = True

بعد ذلك, سنحتاج و كما هو الحال في نسخة الربط المنفصل في الجزء السابق إلى إضهار رقم التسجيل الحالي و عدد التسجيلات على الجدول.


' الوصول إلى رقم التسجيل
cmd.CommandText = "SELECT COUNT(*) FROM UserInfo WHERE ID < @CurID"
Dim number As Integer = cmd.ExecuteScalar()
' الوصول إلى مجموع عدد التسجيلات
cmd.Parameters.Clear() ' أمر سكل التالي لا يحتاج أي برمترات
cmd.CommandText = "SELECT COUNT(*) FROM UserInfo"
Dim count As Integer = cmd.ExecuteScalar()
' عرض المعلومات
Me.Text = String.Format("Connected form - [{0} / {1}]", number + 1, count)

ثم نقوم بغلق قناة الاتصال بقاعدة البيانات و نحرر موارد النظام و ننهي الدالة GoToRecord بإرجاع نتيجة العملية.


' تحرير موارد النظام
cmd.Dispose()
' غلق قناة الاتصال
MyConnection.Close()
' إرجاع نتيجة العملية
Return rslt

استعمالات الدالة GoToRecord متعددة في المشروع المرفق, أزرار الانتقال إلى التسجيل الأول, السابق, اللاحق و الأخير. مثال :


' الوصول إلى أول تسجيل
GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.First)

' الوصول إلى التسجيل السابق
GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Previous)

' الوصول إلى التسجيل اللاحق
GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Next)

' الوصول إلى التسجيل الأخير
GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Last)

في الكود أعلاه, تأخد الدالة GoToRecord معرف التسجيل الحالي من المربع IDTextBox لتقوم بالانتقال إلى التسجيل التالي حسب الوجهة المحددة.

أيضا, سنرى الدالة في كود زيادة تسجيل جديد حيث تتم عملية الزيادة بعد آخر تسجيل على الجدول UserInfo و ننادي على الدالة GoToRecord للوصول إلى التسجيل الجديد. و كذلك الحال بالنسبة لعملية الحذف حيث تصبح محتويات مربعات النموذج بدون أهمية لتقوم الدالة بالانتقال إلى التسجيل اللاحق أو السابق عند الضرورة. المرة الوحيدة التي سنقوم بالمناداة على الدالة GoToRecord و تمرير RecordPosition.This هي بعد تحديث التسجيل الحالي حيث تقوم الدالة في هذه الحالة بإعادة تحميل محتوى التسجيل الحالي بعد عملية التحديث.

تهيئة التطبيق وقت التشغيل :

على غير العادة المعمول بها في الأجزاء السابقة, سنقوم فقط بتحميل التسجيل الأول من قاعدة البيانات و إضهار محتوياته على مربع النص وقت تشغيل التطبيق و لكن قبل ذلك سنقوم أولا بإنشاء و تهيئة كائن الربط.


' إنشاء و تهيئة كائن الربط
MyConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb")

بعد ذلك, سنقوم بتحميل لائحة مجموعات المستخدمين من الجدول GroupInfo على قاعدة البيانات و تحميلها على جدول على الذاكرة الحية لربطها بالكومبوبوكس التي تظهر اسم المجموعة الحالية لمستخدم ما و تمكن المستخدم من اختيار واحدة وقت تحرير التسجيل.


' تحميل بيانات أسماء المجموعات
'' إنشاء الجدول على الذاكرة الحية
Dim tbl As New DataTable("GroupInfo")
'' كائن أمر سكل لتحميل البيانات من القاعدة إلى الذاكرة الحية
Dim cmd As New OleDbCommand("SELECT * FROM GroupInfo", MyConnection)
'' فتح قناة الاتصال
MyConnection.Open()
'' تحميل البيانات من القاعدة إلى الذاكرة الحية
tbl.Load(cmd.ExecuteReader())
'' غلق قناة الاتصال
MyConnection.Close()
'' تحرير موارد النظام
cmd.Dispose()

بعد ذلك نربط الكومبوبوكس GroupComboBox بالجدول الذي تم تحميله.


' ربط لائحة اسماء المجموعات
'' ربط مربع اللائحة بجدول المجموعات
GroupComboBox.DataSource = tbl
'' مرجع التسجيل المرتبط
GroupComboBox.ValueMember = "ID"
'' نص اسم المجموعة
GroupComboBox.DisplayMember = "Name"
' محتوى الكومبو يتغير إذا كان محتوى الخاصيتان مختلفان
GroupComboBox.Tag = GroupComboBox.SelectedValue

السطر الأخير من الكود أعلاه يحدد قيمة الخاصيتين Tag و SelectValue في نفس القيمة حتى نتمكن من التعرف عما إذا ما قام المستخدم بتغيير محتوى الكومبوبوكس بمقارنة الخاصيتيه وقت الانتقال إلى تسجيل آخر.

و آخر خطوة في خطوات تهيئة التطبيق ستكون بالانتقال إلى أول تسجيل و إضهار محتواه قبيل ظهور النموذج.


' الوصول إلى أول تسجيل
GoToRecord(CType(IDTextBox.Text, Integer), RecordPosition.First)

زيادة تسجيل جديد :

عملية زيادة تسجيل جديد تتم بكل بساطة بتنفيذ جملة سكل INSERT و بعد ذلك مباشرة نقوم بالتوجه إلى آخر تسجيل عن طريق المنادة على الدالة GoToRecord لتحميل محتوى التسجيل الجديد إلى مربعات النص. نظرا لأنني قمت بتحديد شرط عدم تكرار قيمة الحقل Login فسنقوم بتحديد قيم افتراضية للحقول شيئا ما مقروءة نتيجة لزيادة عدد عشوائي للحؤول دون تكرار القيمة الافتراضية الذي سيتسبب في رفع استثناء.


' زيادة تسجيل جديد
' إنشاء و تهيئة كائن تنفيذ جملة سكل
Dim cmd As New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = "<Login" + (CType(Rnd() * 999.0F, Integer) Mod 100).ToString() + ">"
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = CType(Rnd() * 999999.0F, Integer).ToString()
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = 1
' تنفيذ جملة سكل
'' فتح قناة الاتصال
MyConnection.Open()
'' تنفيذ جملة زيادة التسجيل
Dim n As Integer = cmd.ExecuteNonQuery()
'' غلق قناة الاتصال
MyConnection.Close()
'' تحرير موارد النظام
cmd.Dispose()
' إذا تمت زيادة تسجيل
If n = 1 Then
' التسجيل الجديد تمت زيادته في آخر الجدول
GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Last)
Else
MsgBox("لم تنجح عملية زيادة السطر الجديد.", MsgBoxStyle.Critical, "خطأ")
End If

حذف تسجيل :

عملية حذف التسجيل الحالي تتم هي الأخرى ببساطة تنفيذ جملة سكل DELETE و من تم الانتقال إلى تسجيل آخر و في حالة حذف آخر تسجيل نقوم بإفراغ محتويات مربعات النموذج للدلالة على عدم وجود تسجيلات على الجدول و تحديد خاصية Text لمربع IDTextBox في 1- حتى تستطيع الدالة GoToRecord التوجه إلى التسجيل الأول بعد عملية زيادة.

أولا, نقوم بتنفيذ جملة سكل DELETE لحذف التسجيل الحالي من على قاعدة البيانات.


' حذف التسجيل الحالي
'' إنشاء و تهيئة أمر سكل
Dim cmd As New OleDbCommand("DELETE FROM UserInfo WHERE ID=@ID", MyConnection)
'' مرجع التسجيل الذي سيتم حذفه
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = CType(IDTextBox.Text, Integer)
'' فتح قناة الاتصال
MyConnection.Open()
'' تنفيذ جملة سكل
Dim n As Integer = cmd.ExecuteNonQuery()
'' غلق قناة الاتصال
MyConnection.Close()
'' تحرير الكائن
cmd.Dispose()

بعد ذلك, نقوم بمحاولة الانتقال إلى تسجيل آخر غير الذي تم حذفه فإما اللاحق إذا وجد أو السابق إذا وجد.


If n = 1 Then
' الوصول إلى التسجيل بعد التسجيل المحذوف
Dim b As Boolean = GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Next)
' ليس هناك تسجيل بعده, نرجع إلى التسجيل الذي قبله
If Not b Then b = GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.Previous)

إذا لم نتمكن من الانتقال إلى أية وجهة فهذا يعني أننا قمنا بحذف آخر تسجيل. في هذه الحالة نقوم بإفراغ النموذج من أي محتوى ثم نظهر الأصفار على شريط العنوان.


' لا تسجيل بعده أو قبله نفرغ محتوى المربعات لأنه لم يبق أي تسجيل
If Not b Then
IDTextBox.Text = "-1"
NameTextBox.Text = ""
PasswordTextBox.Text = ""
GroupComboBox.SelectedValue = GroupComboBox.Tag = 1
Me.Text = "Connected form - [0 / 0];"
End If

تحديث محتوى التسجيلات :

بالنسبة لعمليات التحديث فهي إما عملية تتم باختيار المستخدم عن طريق اختيار الزر Save أو تتم أوتوماتيكيا عند الانتقال من التسجيل الحالي الذي طرأ عليه تغيير إلى تسجيل آخر. العملية تتم بتنفيذ جملة سكل UPDATE و من تم إعادة تحميل التسجيل و إعادة إضهاره على مربعات النموذج.


Me.Validate()
' إنشاء و تهيئة كائن تنفيذ جملة سكل
Dim cmd As New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = NameTextBox.Text
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = PasswordTextBox.Text
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = GroupComboBox.SelectedValue
' مرجع التسجيل الذي سيتم تحديته
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = CType(IDTextBox.Text, Long)
' تنفيذ جملة سكل
'' فتح قناة الاتصال
MyConnection.Open()
'' تنفيذ جملة سكل لتحديث محتويات التسجيل الحالي على قاعدة البيانات
Dim n As Integer = cmd.ExecuteNonQuery()
'' غلق قناة الاتصال
MyConnection.Close()
'' تحرير موارد النظام
cmd.Dispose()
If n = 1 Then
' نقوم بتحديث محتوى المربعات فقط إذا كان المستخدم
' هو من نقر على زر الحفظ
If Not sender Is Nothing Then GoToRecord(CType(IDTextBox.Text, Long), RecordPosition.This)
Else
MsgBox("لم يتم تحديث أي تسجيل.", MsgBoxStyle.Critical, "خطأ")
End If

الكود أعلاه فقط يقوم بعملية التحديث للتسجيل على قاعدة البيانات و هو موجود داخل دالة الحدث Click للزر Save أما عمليات التحديث الأوتوماتيكي فالأولى كما رأينا ذلك تتم على مستوى الدالة GoToRecord أو على مستوى دالة الحدث FormClosing و كلتا الحالتين تتأكدا من تغيير محتوى المربعات قبل المناداة على الكود أعلاه. بالنسبة لعملية التعرف على المربعات التي تم تغيير محتواها فلمربعات النص الخاصية Modified التي تأخد القيمة True في حالة ما إذا قام المستخدم بالكتابة عليها و تأخد القيمة False إما بتحديدها أو تغيير محتوى الخاصية Text عن طريق الكود. بالنسبة لمربع الكومبوبوكس فكود إضهار محتوى التسجيل على المربعات يقوم بتحديد الخاصيتين SelectedValue و Tag على نفس القيمة و وقت الانتقال من التسجيل الحالي أو وقت إغلاق البرنامج يقوم كود التعرف على تغيير المحتوى بمقارنة محتوى الخاصيتين. إذا كانت بهما قيمتان مختلفتان فهذا يؤدي إلى تحديث محتوى التسجيل على الجدول على قاعدة البيانات.

خاتمة :

تكلمنا في هذا الجزء من هذا الدرس عن كيفية ربط مربعات نموذج بجدول على قاعدة البيانات في الوضع المتصل و تعرفنا سوية عن طريقة تنفيذ كل عملية من العمليات الأساسية ألا وهي تحميل و زيادة و حذف و تحديث البيانات و الانتقال بين التسجيلات.

حتى الآن, قمنا بدراسة أربع أمثلة اثنان بالوضع المتصل و اثنان بالوضع المنفصل. أتمنى أن أكون قد وفقت في اختيار الأمثلة و أن أكون قد وفقت للشرح الجيد.

هل تعتبر هذه نهاية الدرس؟.. سوف أقوم بزيادة مثال آخر يتطرق للعلاقات بين الجداول. هذا سؤال طرح بشكل ملحوظ على منتدي VB.NET و Ado.NET سنتعلم فيه كيفية ربط جدولين بعلاقة 1 -> N حيث لكل مجموعة عدد من المستخدمين و عند اختيار اسم مجموعة من على الكومبوبوكس تظهر فقط تسجيلات المستخدمين الذين ينتمون إلى هذه المجموعة لا غير. أيضا هذا المثال سينقسم إلى أربعة أجزاء. إثنان بواجهة مستخدم بالكومبوبوكس و الداتاجريدفيو و الاثنان الآخران بالكومبوبوكس و مربعات النص كالجزئين الأخيرين.

الملف المرفق به مشروع هذا الجزء مع الكود معلق عليه بالتفصيل.

عيد مبارك سعيد و كل عام و أنتم بخير, أعاده الله علينا و على الأمة الإسلامية بالخير و اليمن و البركات و الأفراح و المسرات...

يتبع...

ConnectedForm.rar

2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم

شكراُ لك اخي العزيز محمد رضى

ومنتظرين باقي الدروس

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

باسم الله الرحمن الرحيم...

ربط جدولين في الوضع المنفصل باستخدام كومبوبوكس و داتاجريدفيو.

post-207152-12598760142519_thumb.jpg

تقديم :

في هذا الجزء إن شاء الله, سنقوم بدراسة مثال نربط خلاله جدولين في الوضع المنفصل باستخدام مربع كومبوبوكس و داتاجريدفيو. كما توضح الصورة أعلاه, سيكون لدينا على النموذج مربع كومبوبوكس بلائحة المجموعات GroupInfo التي يمكن أن ينتمي إليها مستخدم مسجل على جدول المستخدمين UserInfo. بالإضافة إلى الكومبوبوكس, هناك داتاجريدفيو تظهر لنا لائحة المستخدمين المسجلين و لكن فقط الذين ينتمون إلى المجموعة المختارة و الظاهرة على كومبوبوكس المجموعات. في كل مرة نقوم باختيار مجموعة أخرى تظهر فقط أسماء المستخدمين الذين ينتمون إليها و تختفي أسطر باقي المستخدمين من المجموعات الأخرى.

بالنسبة للقراء الذين انتقلوا مباشرة إلى هذا الجزء, أنصحهم بقراءة الأجزاء الماضية من هذا الدرس لأننا لن نتطرق بالتفصيل لعمليات تحميل البيانات من القاعدة.

في هذا الجزء, سنستعمل فئة جديدة تمكننا من ربط الجداول بينها و الوصول فقط إلى الأسطر التي تدخل ضمن العلاقات المحددة بين الجداول DataTable. هذه الفئة هي BindingSource. الـ BindingSource هي حاوية لمصدر بيانات من أي نوع تسهل عمليات الربط بين مصدر البيانات و مربعات التحكم على نموذج ما و تسهل عمليات الانتقال بين أسطر أو عناصر مصدر البيانات. الـ BindingSource تمكن أيضا من ترتيب أسطر مصدر البيانات و تنفيذ فلتر عليها.

طريقة ربط الجدولين التي سنتكلم عنها في هذا الجزء تنقسم إلى قسمين, القسم الأول نقوم فيه بزيادة علاقة بين الجدولين GroupInfo الأب و UserInfo الإبن عن طريق المناداة على الدالة Add لمجموعة علاقات الجدول DataTable.ChildRelations و تضمين كائن من فئة DataRelation الذي نحدد من خلاله اسم العلاقة و عمودي ربط الجدول الأب بالإبن. القسم الثاني ننشئ فيه الـ BindingSource الأول للجدول GroupInfo و نهيئه ليكون حاوية لـ DataTable الممثلة للجدول. بعد ذلك نقوم بإنشاء BindingSource أخرى للعلاقة التي قمنا بإنشائها و نهيئه ليكون حاوية لأسطر الجدول UserInfo المرتبطة بالمجموعة الحالية على حاوية الجدول GroupInfo أي DataTable الجدول. بهذه الطريقة, سنظمن أن حاوية الأسطر UserInfo المرتبطة بحاوية جدول المجموعات GroupInfo ستمكننا فقط من الوصول إلى الأسطر المرتبطة بين السطر الحالي للـ GroupInfo و أسطر UserInfo.

تعريفات :

نبدأ مع تعريف حاوية جدول المجموعات ثم تعريف حاوية الأسطر المرتبطة بين الجدولين المجموعات و المستخدمين.


' حاوية بيانات جدول المجموعات
Dim GroupInfoBindingSource As BindingSource
' حاوية بيانات جدول المستخدمين
Dim FKGroupsUsersBindingSource As BindingSource

بعد ذلك, نقوم بتعريف كائن مدير العمليات بين التطبيق و قاعدة البيانات ثم حاوية الجداول.


' كائن تحميل, تحرير, زيادة و حذف المعلومات
Dim MyAdapter As New OleDbDataAdapter
' الداتاست التي ستكون الحاوية للجداول التي سيملأها الكائن مايأدابتر
Dim MyDataSet As DataSet

تحميل البيانات :

تحميل البيانات يتم وقت تشغيل التطبيق أي داخل دالة الحدث Load. الشرح بالتفصيل لعملية تحميل البيانات في الوضع المنفصل تعرضنا لها بالتفصيل في الجزء الثاني من هذا الدرس.


' جملة الربط
Dim ConString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb"
' إنشاء الكائن و تهيئته بكائن الربط و جملة الإستعلام
MyAdapter = New OleDbDataAdapter("SELECT * FROM UserInfo", ConString)
' تهيئة كائن جملة تحرير التسجيلات مع البرامترز
MyAdapter.UpdateCommand = New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.UpdateCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.UpdateCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.UpdateCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
MyAdapter.UpdateCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر الحدف مع البرمتر مرجع التسجيل المراد حذفه
MyAdapter.DeleteCommand = New OleDbCommand("DELETE FROM UserInfo WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.DeleteCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر زيادة التسجيلات من الداتاست إلى الجدول على قاعدة البيانات
MyAdapter.InsertCommand = New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyAdapter.SelectCommand.Connection)
MyAdapter.InsertCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.InsertCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.InsertCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
' إنشاء و تهيئة الداتاست
MyDataSet = New DataSet("AccessDB")
' تحميل تسجيلا الجدول من قاعدة البيانات إلى الداتاست
MyAdapter.Fill(MyDataSet, "UserInfo")

' تحميل بيانات أسماء المجموعات
Dim adptr As New OleDbDataAdapter("SELECT * FROM GroupInfo", MyAdapter.SelectCommand.Connection)
adptr.Fill(MyDataSet, "GroupInfo")
adptr.Dispose()

تحديد العلاقات بين الجدولين :

أولا, نقوم بربط الجدول الأب GroupInfo عبر العمود ID بالجدول الإبن UserInfo عبر العمود GroupInfo.


' إنشاء علاقة بين الجدول الأب و الجدول الإبن
MyDataSet.Tables("GroupInfo").ChildRelations.Add(New DataRelation("FK_GroupInfo_UserInfo", _
MyDataSet.Tables("GroupInfo").Columns("ID"), _
MyDataSet.Tables("UserInfo").Columns("GroupID")))

بعد ذلك, نقوم بتهيئة كائن حاوية الجدول الأب GroupInfo


' تهيئة حاوية بيانات جدول المجموعات
GroupInfoBindingSource = New BindingSource(MyDataSet, "GroupInfo")

بعد ذلك نقوم بتهيئة كائن حاوية العلاقة بين الجدولين التي قمنا بتحديدها أعلاه. لاحظوا معي اسم الربط DataMember الذي سيأخده كائن BindingSource الجديد الذي يتمتل في اسم العلاقة التي أنشأناها أعلاه و لاحظوا معي اسم مصدر البيانات للحاوية الجديدة الذي يتمثل في حاوية الجدول الأب الذي أنشأناه للتو أي GroupInfoBindingSource.


' تهيئة حاوية البيانات المشتركة بين بيانات المجموعات و المستخدمين
FKGroupsUsersBindingSource = New BindingSource(GroupInfoBindingSource, "FK_GroupInfo_UserInfo")

الآن و قد أتممنا تحديد العلاقة بين الجدولين الأب و الإبن نستطيع ربط الكومبوبوكس التي ستحوي لائحة المجموعات للجدول الأب و الداتاجريدفيو التي ستظهر الأسطر المرتبطة بالسطر الحالي لحاوية الجدول الأب.


' ربط العمود بجدول أسماء المجموعات
ComboBox1.DataSource = GroupInfoBindingSource
ComboBox1.DisplayMember = "Name"
ComboBox1.ValueMember = "ID"

' ربط الداتاجريدفيو مع حاوية البيانات المرتبطة بين جدولي المستخدمين و المجموعات
' حتى يظهر فقط أسطر المستخدمين الذين ينتمون إلى المجموعة المظهرة على الكومبوبوكس
DataGridView1.DataSource = FKGroupsUsersBindingSource
' منع الكتابة على خلايا عمود المرجع
DataGridView1.Columns("ID").ReadOnly = True
DataGridView1.Columns("GroupID").Visible = False

لاحظوا معي أننا ربطنا المربعين بحاويتي البيانات BindingSource و ليس مباشرة مع الجدولين DataTable.

اشتغال التطبيق :

الآن و قد انتهت عملية تحميل البيانات و تحديد العلاقة بين الجدولين, سيظهر النموذج و عليه الكومبو محدد عليه اسم أول مجموعة و على الداتاجريدفيو أسطر المستخدمين الذين ينتمون إلى المجموعة. إذا قمنا بتحديد مجموعة أخرى على الكومبو تختفي أسطر الداتاجريدفيو الحالية و تظهر أسطر المستخدمين المنتمين للمجموعة الجديدة.

إذا تتذكرون بنية قاعدة البيانات و الجدولين. تعرفون أن الجدول UserInfo مرتبط مع الجدول GroupInfo عبر الحقل GroupID أي أن مجموعة المستخدم تعرف بـ GroupInfo.ID = UserInfo.GroupID. عند زيادة سطر جديد على الداتاجريدفيو يقوم إطار الدوت نت بربط السطر الجديد للـ UserInfo بسطر المجموعة الحالية GroupInfo المحدد على الكومبوبوكس عن طريق التحديد الأوتوماتيكي للحقل UserInfo.GroupID للسطر الجديد بمرجع سطر المجموعة الحالية. هذا يعني أنه زيادة للدعم الأوتوماتيكي لعمليات الزيادة, التحديث و الحذف من طرف الداتاجريدفيو نجد الدعم الأوتوماتيكي لربط الأسطر الجديدة للجداول الأبناء مع أسطر الجداول الآباء التي تربطهم علاقة تم تحديدها كما قمنا بذلك أعلاه.

حفظ المعلومات :

وقت إغلاق التطبيق, نقوم بحفظ المعلومات من الذاكرة الحية رجوعا نحو قاعدة البيانات.


' إذا كانت هناك تغييرات
If MyDataSet.HasChanges() Then
Me.Validate()
' حفظ التغييرات في جدول المستخدمين على قاعدة البيانات
MyAdapter.Update(MyDataSet.Tables("UserInfo"))
End If

خاتمة :

رأينا في هذا الجزء كيفية ربط جدولين في الوضع المنفصل باستخدام الفئة BindingSource و العلاقات DataRelation. في الجزء التالي سوف نقوم بتحرير المشروع المرفق مع هذا الدرس ليعمل نفس العمل و لكن في الوضع المتصل. و كل هذا حتى يتم فك الخلط الحاصل بين الوضعين المتصل و المنفصل.

في الملف المرفق مع هذا الدرس تجدون مشروع هذا الدرس به الكود كاملا معلق عليه بالتفصيل.

يتبع...

NotConRelatedDGV.rar

تم تعديل بواسطه محمد رضى
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

والله ابدعت جعلها سبحانه وتعالى فى ميزان حسناتك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم و رحمة الله تعالى و بركاته...

ربط جدولين في الوضع المتصل باستخدام كومبوبوكس و داتاجريدفيو :

post-207152-12609124869677_thumb.jpg

تقديم :

في الجزء السابق من سلسلة الأمثلة المدروسة هاته, رأينا معا كيفية ربط جدولين في الوضع المنفصل باستخدام الفئة BindingSource و العلاقات DataRelation. في هذا الجزء, سنقوم بتحرير نفس المشروع المرفق مع المثال السابق في الوضع المنفصل ليعمل في الوضع المتصل حتى يتمكن القارئ من ملاحظة الفارق بين الوضعين و يتمكن مستقبلا من اختيار العمل بالوضع الملائم لكل مشروع.

كود و طريقة عمل المثال المدروس لهذا الجزء يعتمد كثيرا على كود جزء ربط داتاجريدفيو في الوضع المتصل. الفرق بين كود و طرق عمل المثالين هو في طريقة الربط بين الجدولين. في هذا الجزء, سنركز أكثر على كود الربط بين الجدولين لأن كود تحميل, زيادة, حذف و تحديث البيانات مشروح بالتفصيل في جزء ربط الداتاجريدفيو في الوضع المتصل.

لمن يحتاج إلى معرفة بنية قاعدة البيانات التي نقوم بالربط بها في هذه الامثلة, المرجو قراءة الجزء الأول من هذه الدروس.

تعريفات :

كما هي العادة, نبدأ بتعريف الثوابت و الكائنات التي سنحتاج إليها طوال مدة عمل التطبيق. الثابت الأول RowAdded, لتعليم السطور التي تمت زيادتها على الداتاجريدفيو. الثابت الثاني RowEdited, لتعليم السطور الموجودة مسبقا على الداتاجريدفيو و تم تحديث محتواها. الكائن MyConnection, لربط الإتصال في أي لحظة بقاعدة البيانات.


' لتعليم سطور الداتاجريدفيو على أنها تمت زيادتها
Private Const RowAdded As Integer = 1

' لتعليم سطور الداتاجريدفيو على أنها تمت تحرير محتوياتها
Private Const RowEdited As Integer = 2

' كائن الربط
Private MyConnection As OleDbConnection

تهيئة الكائنات :

وقت تشغيل التطبيق, نحتاج إلى تهيئة كائن الربط الذي عرفناه على مستوى الفورم. نحتاج كذلك لتحميل أسماء المجموعات من جدول UserInfo إلى الكومبوبوكس التي ستمكننا من اختيار لائحة المستخدمين الذين ستظهر معلوماتهم على الداتاجريدفيو حسب انتمائهم للمجموعة المختارة على كومبوبوكس المجموعات. في الأخير, نقوم بالمناداة على الدالة UpdateDataGridView التي ستقوم بتحميل لائحة المستخدمين الذين ينتمون إلى المجموعة المختارة على كومبوبوكس المجموعات. سنتكلم بالتفصيل عن الدالة UpdateDataGeidView لاحقا في هذا الجزء.


' تحميل تسجيلات الجدول على الداتاجريدفيو وقت تشغيل البرنامج
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' إنشاء و تهيئة كائن الربط
MyConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb")

' تحميل جدول المجموعات
Dim cmd As New OleDbCommand("SELECT * FROM GroupInfo", MyConnection)
Dim tbl As New DataTable("GroupInfo")
MyConnection.Open()
tbl.Load(cmd.ExecuteReader())
MyConnection.Close()
' ربط جدول المجموعات بخلايا العمود من نوع كومبوبوكس
ComboBox1.DataSource = tbl
ComboBox1.DisplayMember = "Name"
ComboBox1.ValueMember = "ID"

' تحميل محتوى جدول المستخدمين الذين ينتمون إلى أول
' مجموعة محددة على كومبو المجموعات
UpdateDataGridView()
End Sub

زيادة سطر إلى الداتاجريدفيو :

عندما يقوم المستخدم بزيادة سطر جديد أو بمعنى آخر مستخدم جديد إلى الداتاجريدفيو, تقوم هذه الأخيرة بالمناداة على دالة الحدث UserAddedRow حيث سنقوم أولا بتعليم السطر على أنه سطر جديد حتى تتم زيادته إلى جدول المستخدمين UserInfo وقت الانتقال من السطر إلى سطر آخر. سنقوم بعد ذلك بربط تسجيل المستخدم الجديد بالمجموعة التي سينتمي إليها أي المجموعة الحالية الظاهر اسمها على كومبوبوكس المجموعات.

عملية ربط المستخدم الجديد بالمجموعة التي سينتمي إليها تتم عبر حفظ مرجع المجموعة GroupInfo.ID داخل حقل UserInfo.GroupID للمستخدم الجديد.


' إلتقاط حدث زيادة سطر جديد على الداتاجريدفيو
Private Sub DataGridView1_UserAddedRow(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewRowEventArgs) Handles DataGridView1.UserAddedRow
' تعليم السطر على أنه تمت زيادته
DataGridView1.Rows(e.Row.Index - 1).Tag = RowAdded
' تحديد مجموعة المستخدم الجديد
DataGridView1.Rows(e.Row.Index - 1).Cells("GroupID").Value = CType(ComboBox1.SelectedItem, DataRowView)("ID").ToString()
End Sub

حذف سطر :

وقت حذف سطر أو مستخدم من الداتاجريدفيو, تقوم هذه الأخيرة بالمناداة على دالة الحدث UserDeletingRow حيث سنقوم بحذف تسجيل المستخدم من قاعدة البيانات. عملية حذف سطر من الداتاجريدفيو تتم عن طريق النقر على رأس السطر عند أقصى اليسار أو اليمين حسب اللغة المختارة و بعد تحديد السطر كامله, ضغطة على الزر Del على لوحة المفاتيح, تقوم الداتاجريدفيو بحذف السطر و المناداة على دوال الحدثين UserDeletingRow قبيل الحذف و UserDeletedRow بعد الحذف. الكود مشروح بالتفصيل في جزء ربط داتاجريدفيو بجدول في الوضع المتصل.


' إلتقاط حدث حذف سطر من سطور داتاجريدفيو
Private Sub DataGridView1_UserDeletingRow(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewRowCancelEventArgs) Handles DataGridView1.UserDeletingRow
' إنشاء و تهيئة أمر سكل
Dim cmd As New OleDbCommand("DELETE FROM UserInfo WHERE ID=@ID", MyConnection)
' مرجع التسجيل الذي سيتم حذفه
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = e.Row.Cells(0).Value
' فتح قناة الاتصال
MyConnection.Open()
' تنفيذ جملة سكل
Dim n As Integer = cmd.ExecuteNonQuery()
' غلق قناة الاتصال
MyConnection.Close()
' تحرير الكائن
cmd.Dispose()
' التحقق من نتيجة العملية
If n = 0 Then MsgBox("لم يتم حذف أي سطر!", MsgBoxStyle.Critical, "خطأ")
End Sub

تحديث محتوى خلايا الداتاجريدفيو :

عندما يقوم المستخدم بتحديث محتوى خلية من خلايا سطر من أسطر الداتاجريدفيو, تقوم هذه الأخيرة بالمناداة على دالة الحدث CellEndEdit حيث سنقوم بتعليم السطر الذي توجد عليه الخلية أن محتوى السطر قد تغير و يحتاج إلى تحديث تسجيل المستخدم على الجدول على قاعدة البيانات.


' تعليم السطر الذي تم تغيير قيمة خليته على أنه تم تحديث محتواه
Private Sub DataGridView1_CellEndEdit(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
' حتى لا يتم تغيير علامة الزيادة بعلامة التحديث
If DataGridView1.Rows(e.RowIndex).Tag Is Nothing Then
' تعليم السطر على أنه تم تحديته
DataGridView1.Rows(e.RowIndex).Tag = RowEdited
End If
End Sub

زيادة و تحديث محتوى سطور الداتاجريدفيو :

عندما يقوم المستخدم بالانتقال من السطر الحالي, تقوم الداتاجريدفيو بالمناداة على دالة الحدث RowValidating حيث سنقوم بالتأكد من محتوى الخاصية Tag للسطر DataGridViewRow لمعرفة ما إذا تم تعليم السطر على أنه تم تحرير محتواه Tag = RowEdited أم أنه سطر جديد Tag = RowAdded أم أن محتوى السطر لم يتغير منذ تحميل محتواه آخر مرة Tag = Nothing. لكل حالة سنقوم بتنفيذ جملة سكل الملائمة حتى يتم تزامن محتوى الداتاجريد فيو بمحتوى جدول المستخدمين. الكود مشروح بالتفصيل في جزء ربط جدول بداتاجريدفيو في الوضع المتصل.


' التقاط حدث الخروج من السطر الحالي للداتاجريد فيو
Private Sub DataGridView1_RowValidating(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.RowValidating
' عدد السطور التي تمت زيادتها أو تحديثها
Dim n As Integer = 0
' الحصول على السطر الحالي
Dim row As DataGridViewRow = DataGridView1.Rows(e.RowIndex)
' تجاهل السطر إذا لم تتم عليه أية عملية
If row.Tag Is Nothing Then Return
' التأكد من اختيار مجموعة بسبب طريقة عمل العمود كومبوبوكس
If row.Cells(3).Value = Nothing Then
' إعلام المستخدم
MsgBox("المرجو تحديد المجموعة التي ينتمي إليها المستخدم")
' البقاء على نفس السطر حتى تصحيح الخطأ
e.Cancel = True
' خروج
Return
End If
' فتح قناة الاتصال بقاعدة البيانات
MyConnection.Open()
If row.Tag = RowAdded Then ' السطر جديد يجب زيادته على الجدول على قاعدة البيانات
' إنشاء و تهيئة كائن تنفيذ جملة سكل
Dim cmd As New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = row.Cells(1).Value
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = row.Cells(2).Value
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = row.Cells(3).Value
' تنفيذ جملة سكل
n = cmd.ExecuteNonQuery()
' الحصول على مرجع التسجيل
cmd.CommandText = "SELECT @@IDENTITY FROM UserInfo"
row.Cells(0).Value = cmd.ExecuteScalar()
ElseIf row.Tag = RowEdited Then ' السطر موجود و لكن تم تحديته
' إنشاء و تهيئة كائن تنفيذ جملة سكل
Dim cmd As New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyConnection)
' زيادة و تهيئة الوسائط بأسماء الحقول و قيمها من السطر الحالي على الداتاجريدفيو
cmd.Parameters.Add("@Login", OleDbType.WChar, 32, "Login").Value = row.Cells(1).Value
cmd.Parameters.Add("@Password", OleDbType.WChar, 32, "Password").Value = row.Cells(2).Value
cmd.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID").Value = row.Cells(3).Value
' مرجع التسجيل الذي سيتم تحديته
cmd.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID").Value = row.Cells(0).Value
' تنفيذ جملة سكل
n = cmd.ExecuteNonQuery()
End If
' غلق قناة الاتصال
MyConnection.Close()
' تعليم السطر أنه لم يتم تحديثه أو زيادته
row.Tag = Nothing
' التحقق من نجاح العملية
If n = 0 Then MsgBox("لم يتم تنفيذ أي عملية!", MsgBoxStyle.Critical, "خطأ")
End Sub

بعد تحديث أو زيادة السطر, نقوم بتحديد خاصيته Tag على Nothing حتى لا نكرر عمليات الزيادة أو التحرير.

الدالة UpdateDataGridView :

في كل مرة يقوم المستخدم باختيار مجموعة من مجموعات المستخدمين من على كومبوبوكس المجموعات, الكومبوبوكس تقوم بالمناداة على دالة الحدث SelectedIndexChanged حيث سنقوم فقط بالمنادة على الدالة UpdateDataGridView التي ستقوم بتحميل تسجيلات المستخدمين من الجدول UserInfoالذين ينتمون إلى مجموعة المستخدمين التي تم اختيارها من على كومبوبوكس المجموعات.

تبدأ الدالة UpdateDataGridView بالتأكد من وجود مجموعة مختارة على الكومبوبوكس :


If ComboBox1.SelectedValue Is Nothing Then Return

بعد ذلك, تقوم بإفراغ الداتاجريدفيو تماما من أي بيانات أو أعمدة :


' إفراغ الداتاجريدفيو
DataGridView1.Rows.Clear()
DataGridView1.Columns.Clear()

بعد ذلك, نقوم بفتح قناة الاتصال بقاعدة البيانات :


' فتح قناة ربط الاتصال
MyConnection.Open()

بعد ذلك, نحصل على معرف تسجيل المجموعة الحالية على الكومبوبوكس :


Dim id As Long = CType(ComboBox1.SelectedItem, DataRowView)("ID")

بعد ذلك, نقوم بتهيئة أمر جملة الاستعلام سكل و تحميل تسجيلات المستخدمين :


Dim cmd As OleDbCommand = New OleDbCommand("SELECT * FROM UserInfo WHERE GroupID = " + id.ToString(), MyConnection)
' تحميل جدول المستخدمين تسجيل بعد تسجيل
Dim rdr As OleDbDataReader = cmd.ExecuteReader()

بعد ذلك, نقوم بإنشاء أعمدة الجدول UserInfo على الداتاجريدفيو. معلومات الأعمدة نحصل عليها من الكائن rdr من فئة OleDbDataReader المرتبط حاليا بقاعدة البيانات. الكود :


' إذا كانت هناك حقول على الجدول
If rdr.FieldCount <> 0 Then
' إنشاء عمود لكل جدول
For i As Integer = 0 To rdr.FieldCount - 1
Dim fld As String = rdr.GetName(i)
' زيادة العمود
DataGridView1.Columns.Add(fld, fld)
Next
' العمود الأول لحقل تلقائي الترقيم فلا يجب تحريره من طرف المستخدم
DataGridView1.Columns("ID").ReadOnly = True
' إخفاء عمود مرجع المجموعة
DataGridView1.Columns("GroupID").Visible = False
End If

بعد تجهيز الأعمدة التي تمثل حقول الجدول, نقوم بتحميل البيانات واحدا واحدا من قاعدة البيانات عن طريق الكائن rdr الذي عرفناه سابقا.


' إذا كانت هناك سطور بالجدول
If rdr.HasRows Then
' قراءة السطر التالي من الجدول من قاعدة البيانات
While rdr.Read()
' زيادة السطر على الداتاجريدفيو
Dim row As DataGridViewRow = DataGridView1.Rows(DataGridView1.Rows.Add())
' إحالة محتوى الحقل إلى محتوى الخلية المناسبة
For i As Integer = 0 To rdr.FieldCount - 1
row.Cells(i).Value = rdr.GetValue(i)
Next
End While
End If

و أخيرا, نقوم بتحرير موارد النظام عن طريق إغلاق الكائن rdr, تحرير الكائن cmd و غلق قناة الاتصال بقاعدة البيانات.


' غلق قارئ البيانات
rdr.Close()
' غلق قناة الإتصال بقاعدة البيانات
MyConnection.Close()
' تحرير كائن أمر سكل
cmd.Dispose()

خاتمة :

تعاملنا في هذا الجزء مع البيانات في الوضع المتصل, أي أننا لا نقوم بتحميل البيانات إلا كلما دعت الضرورة, عكس الوضع المنفصل حيث نقوم بتحميل جميع البيانات إلى الذاكرة الحية ثم بعد ذلك نقوم بالعمل عليها. نقوم بتحميل فقط سطور الداتاجريدفيو التي نحتاج إضهارها التي تنتمي إلى مجموعة المستخدمين المحددة على كومبوبوكس المجموعات. عمليات التحديث و الزيادة تتم على مستوى سطر الداتاجريد فيو و ليس على مستوى كل تسجيلات الجدول DataTable كما هو الحال في الوضع المنفصل.

في الجزء القادم من سلسلة الأمثلة المدروسة سنقوم بربط جدولين و لكن هذه المرة عبر كومبوبوكس و فورم مكون مربعات النصوص مع أزرار الانتقال من التسجيلات و زيادتها و حذفها. كل هذا, حتى يتم فك الخلط الحاصل بين الوضعين المتصل و المنفصل.

يتبع...

ConRelatedDGV.rar

تم تعديل بواسطه محمد رضى
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

طيب شو اللي بيحدد اي اتصال نستخدم ؟؟ وانت شو بتفضل تستخدم؟؟؟ اي شو اللي بتنصحنا فيه نستخدمه في مشاريعنا ؟؟؟؟ انا شايفة من خلال قرائتي للموضوع ومن وجهة نظري انه الوضع المتصل افضل بكثير من الوضع المنفصل

تم تعديل بواسطه Riham Ahmed
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0
طيب شو اللي بيحدد اي اتصال نستخدم ؟؟ وانت شو بتفضل تستخدم؟؟؟ اي شو اللي بتنصحنا فيه نستخدمه في مشاريعنا ؟؟؟؟

المسألة لا علاقة لها بأي طريقة نفضل أو نحب أن نستخدم في الربط بقاعدة البيانات, و لكن متطلبات المشروع أو التطبيق الذي نحن بصدد تطويره هي التي تحتم علينا التفكير لاختيار أفضل طريقة للربط بالقاعدة. لا أستطيع أن أنصح باستعمال طريقة ما على الأخرى و لكنني طوال كتابة هذا الموضوع حاولت إعطاء القارئ ما استطعت من معلومات و مقارنات حتى يصبح قادرا بنفسه على تحديد أي طريقة تكون أفضل لأي مشروع من مشاريعه البرمجية المستقبلية. لا يمكن أن نحب طريقة و نعمل بها وحدها على كل المشاريع و لكن الموضوع كتب كي نتعلم و نفهم الطريقتين جيدا حتى نقرر أي الطريقتين ستكون الأفضل للمشروع الحالي مثلا.

بالطبع يمكن لنا الخلط بين الطريقتين أو نجد أنفسنا مضطرين للخلط بين الطريقتين في مشروع ما, و لكنني شخصيا لا أفضل هذا لأنني غالبا ما تواجهني مشاكل عند الرجوع إلى المشروع للقيام بأعمال الصيانة أو زيادة وظيفة جديدة للمشروع.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم و رحمة الله تعالى و بركاته...

ربط جدولين في الوضع المنفصل عن طريق كومبوبوكس و نموذج :

post-207152-12633963173909_thumb.jpg

تقديم :

و مازلنا معا على الطريق حتى فك الخلط الحاصل بين الوضعين المتصل و المنفصل...

في هذا الجزء, سنتكلم معا عن كيفية ربط جدولين عن طريق كومبوبوكس و نموذج. الكومبوبوكس ستظهر قائمة المجموعات و النموذج المكون من مربعات نص سيعرض معلومات المستخدمين المنتمين إلى المجموعة المحددة على الكومبوبوكس. للنموذج أزرار تمكن من التحرك بين التسجيلات و زيادتها و حذفها. بالطبع, هذه التسجيلات في وقت ما ستكون فقط التسجيلات الخاصة بالمستخدمين الذين ينتمون إلى المجموعة الظاهرة على كومبوبوكس المجموعات. عند زيادة تسجيل مستخدم جديد, سينتمي هذا الأخير إلى المجموعة الحالية على الكومبوبوكس و عند التحرك بين تسجيلات المستخدمين ستكون فقط تسجيلات المجموعة الحالية هي المتاحة و نستطيع في أي لحظة استعراض تسجيلات مجموعة أخرى عن طريق اختيارها من على الكومبوبوكس أعلى النموذج كما هو ظاهر على الصورة أعلاه.

الفرق بين مشروع الجزء الحالي و مشروع جزء ربط نموذج في الوضع المنفصل هو أننا في الأول قمنا بربط مربعات نص النموذج مباشرة إلى جدول معلومات المستخدمين UserInfo من فئة DataTable للوصول إلى جميع تسجيلات المستخدمين بدون استثناء. أما في المشروع المرفق مع هذا الجزء و الذي كما أسلفنا ذكره نريد ربط تسجيلات المستخدمين UserInfo مع تسجيلات المجموعات GoupInfo التي تنتمي إليها, سنقوم بربط كل جدول من فئة DataTable مع كائن من فئة BindingSource حتى نتمكن فقط من إظهار تسجيلات مستخدمي المجموعة الحالية على الكومبوبوكس.

طريقة ربط الجدولين التي سنتكلم عنها في هذا الجزء تنقسم إلى قسمين, القسم الأول نقوم فيه بزيادة علاقة بين الجدولين GroupInfo الأب و UserInfo الإبن عن طريق المناداة على الدالة Add لمجموعة علاقات الجدول DataTable.ChildRelations و تضمين كائن من فئة DataRelation الذي نحدد من خلاله اسم العلاقة و عمودي ربط الجدول الأب بالإبن. القسم الثاني ننشئ فيه الـ BindingSource الأول للجدول GroupInfo و نهيئه ليكون حاوية لـ DataTable الممثلة للجدول. بعد ذلك نقوم بإنشاء BindingSource أخرى للعلاقة التي قمنا بإنشائها و نهيئه ليكون حاوية لأسطر الجدول UserInfo المرتبطة بالمجموعة الحالية على حاوية الجدول GroupInfo أي DataTable الجدول. بهذه الطريقة, سنظمن أن حاوية الأسطر UserInfo المرتبطة بحاوية جدول المجموعات GroupInfo ستمكننا فقط من الوصول إلى الأسطر المرتبطة بين السطر الحالي للـ GroupInfo و أسطر UserInfo.

لاحظوا معي أنها نفس الطريقة التي استعملناها في جزء ربط جدولين في الوضع المنفصل عن طريق داتاجريدفيو و كومبوبوكس و لكن في هذا الجزء, بدل أن نظهر التسجيلات على داتاجريدفيو, نريد العمل على نموذج يمكننا من تشخيص العمل كما نريد و نسهل كذلك عمليات التحرير و الزيادة و الحذف على مستخدمي التطبيق عن طريق أزرار خاصة لهذا الغرض.

تعريفات :

كما في الأجزاء السابقة المخصصة للربط في الوضع المنفصل, سنحتاج لكائن من فئة OleDbDataAdapter الذي سيمكننا بعد تهيئته من تحميل, تحرير, زيادة و حذف التسجيلات من و إلى قاعدة البيانات.


' كائن تحميل, تحرير, زيادة و حذف المعلومات
Private MyAdapter As New OleDbDataAdapter

سنحتاج كذلك إلى حاوية البيانات أو ما يمكن اعتبارها نسخة طبق الأصل من قاعدة البيانات على الذاكرة الحية و هي كائن من فئة DataSet.


' الداتاست التي ستكون الحاوية للجداول التي سيملأها الكائن مايأدابتر
Private MyDataSet As DataSet

و بالنسبة لخصوصيات المشاريع المحتوية على جداول مرتبطة فيما بينها, سنحتاج لكائن من فئة BindingSource لربط مربعات التحكم عن طريقه إلى الجداول DataTable و ذلك لحاجتنا إلى زيادة العلاقات بين الجداول. الكائن الأول سيمثل مصدر تسجيلات المجموعات أي الجدول الأب GroupInfo.


' حاوية بيانات جدول المجموعات
Dim GroupInfoBindingSource As BindingSource

الكائن الآخر من نفس الفئة سيكون مصدر تسجيلات المستخدمين UserInfo أي الجدول الإبن الذي سنهيئه فيما بعد للعمل مع "أبيه" عن طريق تنفيذ فلترة أوتوماتيكية تمكننا من الوصول فقط إلى تسجيلات المستخدمين المنتمين إلى المجموعة المحددة على الكائن الأب.


' حاوية بيانات جدول المستخدمين
Dim FKGroupsUsersBindingSource As BindingSource

تحميل البيانات وقت تشغيل التطبيق :

كما واضبت على ذكره في الأجزاء المخصصة للوضع المنفصل, تحميل البيانات يتم وقت تشغيل التطبيق. في هذا الجزء لن نتعرض بالتفصيل لكود تحميل البيانات لأننا سبقنا و درسناه بالتفصيل في الأجزء السابقة المخصصة للوضع المنفصل.


' جملة الربط
Dim ConString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source =.\AccessDB.mdb"
' إنشاء الكائن و تهيئته بكائن الربط و جملة الإستعلام
MyAdapter = New OleDbDataAdapter("SELECT * FROM UserInfo", ConString)
' تهيئة كائن جملة تحرير التسجيلات مع البرامترز
MyAdapter.UpdateCommand = New OleDbCommand("UPDATE UserInfo SET [Login] = @Login, [Password] = @Password, GroupID = @GroupID WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.UpdateCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.UpdateCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.UpdateCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
MyAdapter.UpdateCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر الحدف مع البرمتر مرجع التسجيل المراد حذفه
MyAdapter.DeleteCommand = New OleDbCommand("DELETE FROM UserInfo WHERE ID = @ID", MyAdapter.SelectCommand.Connection)
MyAdapter.DeleteCommand.Parameters.Add("@ID", OleDbType.BigInt, 1, "ID")
' تهيئة كائن أمر زيادة التسجيلات من الداتاست إلى الجدول على قاعدة البيانات
MyAdapter.InsertCommand = New OleDbCommand("INSERT INTO UserInfo ([Login], [Password], GroupID) VALUES(@Login, @Password, @GroupID)", MyAdapter.SelectCommand.Connection)
MyAdapter.InsertCommand.Parameters.Add("@Login", OleDbType.WChar, 32, "Login")
MyAdapter.InsertCommand.Parameters.Add("@Password", OleDbType.WChar, 32, "Password")
MyAdapter.InsertCommand.Parameters.Add("@GroupID", OleDbType.BigInt, 1, "GroupID")
' إنشاء و تهيئة الداتاست
MyDataSet = New DataSet("AccessDB")
' تحميل تسجيلات الجدول من قاعدة البيانات إلى الداتاست
MyAdapter.Fill(MyDataSet, "UserInfo")

' تحميل بيانات أسماء المجموعات
Dim adptr As New OleDbDataAdapter("SELECT * FROM GroupInfo", MyAdapter.SelectCommand.Connection)
adptr.Fill(MyDataSet, "GroupInfo")
adptr.Dispose()

إنشاء علاقة الأب بالإبن بين الجدولين :

حتى نستطيع الوصول إلى الهدف من مشروع هذا الجزء و الذي حددناه في التقديم, سنحتاج إلى إنشاء علاقة الأب بالإبن بين الجدولين GroupInfo الأب و UserInfo الإبن.


' إنشاء علاقة بين الجدول الأب و الجدول الإبن
MyDataSet.Tables("GroupInfo").ChildRelations.Add(New DataRelation("FK_GroupInfo_UserInfo", _
MyDataSet.Tables("GroupInfo").Columns("ID"), _
MyDataSet.Tables("UserInfo").Columns("GroupID")))

قمنا في الكود أعلاه بزيادة كائن من نوع DataRelation إلى مجموعة علاقات الجدول الأب ChildRelations بأبنائه. الكائن الذي حددنا عن طريقه علاقة الأبوة له اسم العلاقة FK_GroupInfo_UserInfo ثم كائن العمود أو الحقل المعرف للتسجيل الأب ثم كائن العمود أو الحقل المعرف للتسجيلات الأبناء.

بعد ذلك سنقوم بتهيئة كائن المصدر الأب الغير مباشر لتسجيلات المجموعات عن طريق تحديد الداتاست الموجود عليها الجدول DataTable و اسم هذا الأخير.


' تهيئة حاوية بيانات جدول المجموعات
GroupInfoBindingSource = New BindingSource(MyDataSet, "GroupInfo")

المصدر الغير مباشر لتسجيلات المستخدمين يحتاج إلى تهيئته تهيئة خاصة حتى يبقى مرتبط بأبيه, لهذا سنربط الكائن FKGroupsUsersBindingSource بالكائن الأب من نفس الفئة و الذي قمنا بإنشاءه و تهيئته للتو GroupInfoBindingSrouce مع تحديد اسم الجدول في اسم العلاقة FK_GroupInfo_UserInfo التي قمنا بإنشائها سلفا بين الجدول الأب GroupInfo و الإبن UserInfo.


' تهيئة حاوية البيانات المشتركة بين بيانات المجموعات و المستخدمين
FKGroupsUsersBindingSource = New BindingSource(GroupInfoBindingSource, "FK_GroupInfo_UserInfo")

عند هذه النقطة, لو تحركنا من تسجيل أب إلى آخر على الكائن GroupInfoBindingSource مثلا عن طريق تحديد رقم التسجيل المراد التحرك إليه على الخاصية Position, يقوم الكائن الإبن FKGroupsUsersBindingSource بتحديث محتواه من التسجيلات إلى تلك المنتمية إلى المجموعة الحالية على الكائن الأب GroupInfoBindingSource. لاحظوا معي صورة واجهة المستخدم على الصورة أعلاه و الأهداف التي حددناها في التقديم, كومبوبوكس المجموعات هي التي سيقوم من خلالها المستخدم بتحديد المجموعة الحالية و بالتالي تسجيلات المستخدمين المنتمين إليها. إذن, سنقوم بربط كومبوبوكس المجموعات مع الكائن الأب مصدر تسجيلات المجموعات. بهذه الطريقة, كلما اخترنا مجموعة من على الكومبوبوكس, تقوم هذه الأخيرة بتحديد التسجيل المناسب على الكائن الأب المرتبط بها و بالتالي الإبن يقوم أوتوماتيكيا بتحديث محتواه من التسجيلات. إطار الدوت نت يقوم بكل هذا العمل أوتوماتيكيا و أكثر كما سنرى ذلك بعد قليل.


' ربط كومبوبوكس مجموعات المستخدمين مع الجدول الموجود على الذاكرة الحية
GroupComboBox.DataSource = GroupInfoBindingSource
GroupComboBox.DisplayMember = "Name"
GroupComboBox.ValueMember = "ID"

ملاحظة : الأكواد التالية تم التطرق لها بالتفصيل في الأجزاء السابقة المخصصة للوضع المنفصل و تم تظمينها هنا فقط للحفاظ على التسلسل المنطقي للكود. الكود معلق عليه بالتفصيل...

نقوم بعد ذلك بربط مربعات النص مع الكائن FKGroupsUsersBindingSource.


' ربط مربعات النموذج مع الحقول الموجودة على الذاكرة الحية
IDTextBox.DataBindings.Add("Text", FKGroupsUsersBindingSource, "ID", True, DataSourceUpdateMode.OnValidation)
NameTextBox.DataBindings.Add("Text", FKGroupsUsersBindingSource, "Login", True, DataSourceUpdateMode.OnValidation)
PasswordTextBox.DataBindings.Add("Text", FKGroupsUsersBindingSource, "Password", True, DataSourceUpdateMode.OnValidation)

في كل مرة تم التحرك إلى تسجيل آخر تتم المناداة على الدالة المخصصة لإظهار بعض المعلومات.


' تحديد دالة الحدث تغيير التسجيل الحالي لإضهار معلومات
' حول التسجيل الحالي للمستخدم
AddHandler FKGroupsUsersBindingSource.PositionChanged, AddressOf FKGroupsUsersBindingSource_PositionChanged

تهيئة نظام الترقيم التلقائي للتسجيلات الجديدة.


' تهيئة الترقيم التلقائي للتسجيلات الموجودة على الداكرة الحية
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrement = True
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementStep = 1
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementSeed = 1
' الحصول على أكبر مرجع للتسجيلات لاستعماله في حساب مرحع التسجيلات
' الموجودة على الذاكرة الحية
If BindingContext(MyDataSet, "UserInfo").Count > 0 Then
' الحصول على أكبر معرف في الجدول
Dim rows() As DataRow = MyDataSet.Tables("UserInfo").Select("MAX(ID)=ID")
' حفظ المرجع للاستعمال
MyDataSet.Tables("UserInfo").Columns("ID").AutoIncrementSeed = rows(0)("ID") + 1
End If

التحرك بين التسجيلات :

كل زر من أزرار التحرك بين تسجيلات المستخدمين تقوم بتعديل قيمة الخاصية Position للكائن FKGroupsUsersBindingSource. لاحظوا معي أن قيمة الخاصية Count تتغير في كل مرة تم تغيير المجموعة الحالية فهي تعيد عدد تسجيلات المستخدمين المنتمين إلى المجموعة الحالية و ليس عدد تسجيلات الجدول.


' الوصول إلى أول تسجيل
FKGroupsUsersBindingSource.Position = 0

' الوصول إلى التسجيل السابق
FKGroupsUsersBindingSource.Position -= 1

' الوصول إلى التسجيل اللاحق
FKGroupsUsersBindingSource.Position += 1

' الوصول إلى آخر تسجيل
FKGroupsUsersBindingSource.Position = FKGroupsUsersBindingSource.Count - 1

زيادة تسجيل جديد :

نحتاج عند زيادة أي تسجيل مستخدم جديد أن نقوم بربطه مع التسجيل الأب. لا, لا نحتاج لأن نقوم بأي شيء, إطار الدوت نت يقوم بهذا العمل عنا بشرط المناداة على دالة AddNew للكائن FKGroupsUsersBindingSource الذي يقوم بعملية تحديد مرجع التسجيل الأب على الحقل الذي حددناه عند زيادة العلاقة بين الجدولين.


' الحصول على تسجيل جديد على الذاكرة الحية
Dim row As DataRowView = FKGroupsUsersBindingSource.AddNew()
' طلب بدء عملية تحرير حقول التسجيل
row.BeginEdit()
' تحديد المحتوى الأولي للحقول
row("Login") = "<Login_" + row("ID").ToString() + ">"
row("Password") = "<Password_" + row("ID").ToString() + ">"
'row("GroupID") = 1 ' إطار الدوت نت يقوم بهذا
' أمر إنهاء عملية التحرير
row.EndEdit()

حذف تسجيل :

لا تعليقات على عمليات الحذف.


' خروج إذا لم تكن هناك تسجيلات على الجدول على الذاكرة الحية
If FKGroupsUsersBindingSource.Count <= 0 Then Return
' حذف التسجيل الحالي من الجدول الموجود على الذاكرة الحية
FKGroupsUsersBindingSource.RemoveCurrent()

حفظ المعلومات :

وقت غلق التطبيق أو عند النقر على الزر المخصص للحفظ, نقوم بعد التأكد من وجود تغييرات على البيانات الموجودة حاليا على الذاكرة الحية, نقوم بالاستعانة بخدمات الكائن MyAdapter لحفظ البيانات من الذاكرة الحية رجوعا نحو قاعدة البيانات.


' إذا كانت هناك تغييرات على البيانات الموجودة على الذاكرة الحية
If MyDataSet.HasChanges() Then
' نطلب التحقق من المعلومات المكتوبة على مربعات النموذح
Me.Validate()
' إنهاء تحرير التسجيل الحالي الموجود على الذاكرة الحية
CType(FKGroupsUsersBindingSource.Current, DataRowView).EndEdit()
' حفظ البيانات الموجودة على الجدول على الذاكرة الحية
' رجوعا نحو الجدول على قاعدة البيانات
MyAdapter.Update(MyDataSet.Tables("UserInfo"))
End If

خاتمة :

قبل ختم هذا الجزء من الأمثلة المدروسة, لا بد من التنويه إلى أنه رغبة مني في الحفاظ على هذه الدروس بسيطة و سهلة الفهم, قمت بتجاهل عدة نقاط و منها على سبيل الذكر لا الحصر, نقطة حذف التسجيل الأب. مثلا عند المناداة على الدالة GroupInfoBindingSource.RemoveXXXX(...) سيقوم إطار الدوت نت بحذف تسجيل المجموعة (الأب) مع و بطريقة أوتوماتيكية كل تسجيلات المستخدمين المنتمين إلى المجموعة المحذوفة...

لنا لقاء, إن شاء الله, مع الجزء القادم من سلسلة الأمثلة المدروسة هاته, حيث سنقوم بتحويل المشروع المرفق مع هذا الجزء للعمل و الربط مع قاعدة البيانات في الوضع المتصل.

يتبع...

NotConRelatedFrom.rar

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

سؤال كيف استطيع ان احصل علي id لعنصر في كمبو سواء وضع متصل او منفصل

تم تعديل بواسطه محمد رضى
حذف نص الاقتباس الكبير جدا جدا...
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بالنسبة لكومبو عادية أي System.Windows.Forms.ComboBox استعمل الخاصية SelectedValue بشرط أن الكومبو تكون مرتبطة بجدول DataTable عن طريق الخاصية DataSource و الخاصية ValueMember محددة على اسم الحقل مرجع ID تسجيلات الجدول.

بالنسبة لكومبو داخل DataGridView فالخاصية Value للخلية بشرط أن العمود DataGridViewComboBoxColumn الذي تنتمي إليه يكون مرتبط بجدول Datatable عن طريق الخاصية DataSource و الخاصية ValueMember محددة على اسم الحقل مرجع ID تسجيلات الجدول.

بالتوفيق :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

جزاك الله خيرا اخونا محمد رضى على هذا الموضوع المفيد جدا

لقد استفدت منه كثيرا

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم رحمة الله وبركاته

مشكور استاذي محمد رضى على هذا الدرس الشيق

واتمنى يكون في ميزان حسناتك الله يبارك فيك ويرحم والديك

اسمح لي اكون من المتابعين

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

لقد استفدت كثيراُ من الدورة احسنت وذادك المولى عز وجل علماً نافعا

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

شكراً يا أخ محمد..

الله يجزيك كل خير والله الموضوع رائع وأفادني كثير..

حاولت إستخدام الشرح الأول في ربط الداتا جريد بالوضع المنفصل مع وضع combobox بداخلها.. واشتغل معايا تمام التمام..

بس واجهت معضلة صغيرة, وهي أن أي تعديل أقوم به على الداتا جريد فإن التعديل يتم بعد إغلاق الصفحة على قاعدة البيانات في كل الحقول المطلوبة إلا حقل الـ combobox الذي قمت بتغييره لا ينطبق التغيير الذي أفعله على قاعدة البيانات..

أرجوا منك الإفادة إن أمكن.

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

الحمد لله أن وفقني لذلك أخي العزيز...

بالنسبة لتساؤلك عن مشكلة أن التعديلات لا تتم إلا بعد غلق التطبيق فهذا أمر عادي بالنسبة للعمل في الوضع المنفصل حيث, كما أسلفت ذكره في الموضوع, نقوم بالعمل على التسجيلات و الجداول في الذاكرة الحية ثم بعد ذلك و عموما وقت إغلاق التطبيق, نقوم بحفظ التعديلات على التسجيلات من الذاكرة الحية رجوعا نحو قاعدة البيانات. لحل المشكلة, يمكنك زيادة زر تسميه مثلا "احفظ" تقوم عند النقر عليه بتنفيذ كود الحفظ إلى قاعدة البيانات, نفس الكود الذي يتم تنفيذه وقت الحدث FormClosing :


' إذا كانت هناك تغييرات
If MyDataSet.HasChanges() Then
' حفظ التغييرات في جدول المستخدمين على قاعدة البيانات
MyAdapter.Update(MyDataSet.Tables("UserInfo"))
End If

بالنسبة للمشكلة الثانية فأعد التأكد من كود ربط الكومبوبوكس بالجدول و إن استمر المشكل فيمكنك ارفاق المشروع أو جزء منه حتي يسهل على بقية الأعضاء البحث عن السبب و تصحيحه.

بالتوفيق :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بارك الله فيكم وجزاكم الله الف خير على الفائدة واسأل الله ان يجعل ذلك في ميزان حسناتك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بارك الله فيكم ياباش مهندس محمد رضي

وجزاكم الله خير على هذا التميز

واسأل الله ان يجعل ذلك في ميزان حسناتك

وان يوفقك الله في الدنيا والآخرة

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

يا أخي ماشاء الله عليك

وتبارك الله الذي وفقك

شكرا لك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

الدرس جدا رائع كانت عندي مشكله في الربط والكومبو بوكس

الحمد لله اخيرن ضبطت :)

بس حبيت استفسر منك عن شغله عندي جدول للموظفين يحتوي على الحقول التاليه

1303942453381.png

واجهتني مشكله في عملية الحفظ عند هذه الجمله

MyAdapter.Update(MyDataSet.Tables("Employee"))

اتوقع لاني ما اعرفت اعرف الحقول صح ( خاصه انو عندي حقل تاريخ ) او ما ادري عالعموم هذا المشروع ارفقته لك

ياريت تشوف لي وين الخطا واكون شاكره وممتنه لك ^_^

WindowsApplication7.rar

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .