Page 1

ASP.NET ‫أسس البرمجة بلغة‬ C# ‫بمساعدة لغة‬

Basics Of ASP.NET Programming With C#

‫تأليف األستاذ خليل أبو شنب‬ 2015 ‫المكر‬-‫جديدة‬ abushanab_khalil@yahoo.com

1


‫الفهرس‬

5............................................................................ ‫مقدمة‬ 7.......................................................‫البرمجة بتسويق األحداث‬ 10 ............................................................................................................Controls ‫األدوات‬ 11 ............................................................................................................... TextBox ‫األداة‬ 17 .....................................................................................................................Label ‫األداة‬ 19 .................................................................................................................. Button ‫األداة‬ 27 .............................................................................................................................. ‫تمارين‬

42.................................................................... ‫االستثناءات‬ 47...............................................................‫صفحات الماستر‬ 61 ............................................................................................................. Menu Control 65 .............................................................................................................................. ‫تمارين‬ 66 ...................................................................................................... Calendar Control

72........................................................ ‫البرمجة عديمة الحالة‬ 72 ..................................................................................................... IsPostBack ‫الخاصية‬

76......................................................................... ‫تمارين‬ 78 .............................................................................................................. Control state 78 .................................................................................................................... ViewState 81 ....................................................................................................................... Session 83 ........................................................................................................................ Cookies 88 ................................................................................................................. Application 89 ................................................................................................................ Global.asax 91 .............................................................................................................................. ‫تمارين‬

95............................................. ‫أدوات التحقق من صحة اإلدخال‬ 96 ................................................................................... RequiredFieldValidator ‫األداة‬ 99 .......................................................................................... ValidationSummary ‫األداة‬ 102 ................................................................................................ RangeValidator ‫األداة‬

2


‫األداة ‪105 ....................................................................... RegularExpressionValidator‬‬ ‫األداة ‪113 ...........................................................................................CompareValidator‬‬ ‫األداة ‪118 ............................................................................................. CustomValidator‬‬ ‫‪122 ............................................................................................................. Page.IsValid‬‬ ‫تمارين ‪124 ............................................................................................................................‬‬

‫‪125........................................................................ SQL‬‬ ‫األمر ‪130 ............................................................................................................. SELECT‬‬ ‫تحديد وترتيب النتائج ‪134 ..........................................................................................................‬‬ ‫ترتيب النتائج ‪138 ....................................................................................................................‬‬ ‫استخراج معلومات من أكثر من جدول ‪139 .....................................................................................‬‬ ‫تمارين ‪151 ............................................................................................................................‬‬ ‫األمر ‪151 ................................................................................................... SELECT INTO‬‬ ‫اإلدخال ‪155 ...........................................................................................................................‬‬ ‫التحديث ‪157 ..........................................................................................................................‬‬ ‫الحذف ‪159 ............................................................................................................................‬‬ ‫الدوال ‪161 .............................................................................................................................‬‬ ‫إنشاء مجموعات في جدول النتيجة‪165 .........................................................................................‬‬

‫‪167............................................................... ADO.NET‬‬ ‫ما هي تقنية ‪167 ....................................................................................................... Ado.net‬‬ ‫خطوات الوصول الى البيانات ‪168 ................................................................................................‬‬ ‫تنفيذ استعالمات أي ‪171 ........................................................................... SELECT Queries‬‬ ‫تنفيذ أوامر ‪ SQL‬ال تعيد جدول نتيجة ‪173 ....................................................................................‬‬

‫‪179............................................. 3-Level Architecture‬‬ ‫‪180........................................................ Data Controls‬‬ ‫األداة ‪181 ............................................................................................................ GridView‬‬ ‫تمارين ‪190 ............................................................................................................................‬‬ ‫‪ GridView‬والتأكد من صحة االدخال ‪244 ...................................................................................‬‬ ‫األداة ‪249 ............................................................................................................ Repeater‬‬ ‫األداة ‪257 ............................................................................................................. DataList‬‬

‫‪3‬‬


‫الملفات ‪275.......................................................................‬‬ ‫حفظ الملفات ضمن منظومة الملفات ‪276 .......................................................................................‬‬ ‫حفظ الصورة ضمن منظومة الملفات ‪287 .......................................................................................‬‬ ‫تمارين ‪289 ............................................................................................................................‬‬

‫لغة ‪290................................................................... XML‬‬ ‫خدمات الوب ‪310.................................................................‬‬ ‫ما هي خدمات الوب‪310 ............................................................................................................‬‬ ‫استخدام خدمة وب معينة متوفرة في الوب ‪311 ...............................................................................‬‬ ‫البروتوكول ‪314 .......................................................................................................... SOAP‬‬ ‫‪317 ......................................................................................................................... WSDL‬‬ ‫‪318 ........................................................................................................................ DISCO‬‬ ‫‪319 ........................................................................................................................... UDDI‬‬ ‫تمارين ‪327 ............................................................................................................................‬‬ ‫تصميم وكتابة خدمة وب ‪328 ......................................................................................................‬‬ ‫استعمال الخدمة من قبل زبون‪337 ...............................................................................................‬‬

‫‪4‬‬


‫مقدمة‬ ‫‪ ASP.NET‬عبارة عن منظومة متقدمة جدا توفرها شركة مايكروسوفت لتطوير المواقع الديناميكية‪ .‬هذه المنظومة‬ ‫تستند إلى البرمجة موجهة الكائنات )‪ (Object Oriented Programming‬والبرمجة بتسويق األحداث‬ ‫)‪ .(Events Driven Programming‬وتدعم ‪ ASP.NET‬التواصل مع قواعد البيانات بأساليب مختلفة من‬ ‫خالل توفير إطار )‪ (Framework‬كامل من الفئات التي ُتسهل عملية التواصل مع قواعد البيانات‪ .‬الكتاب يبدأ‬ ‫بالتعرف على األدوات األساسية وكيفية استعمالها‪ .‬ثم يشرح مبدأ البرمجة بتسويق األحداث من خالل أمثلة بسيطة‬ ‫ُتعتبر مكونات أساسية في معظم المواقع الديناميكية‪ .‬بعدها نعرض لصفحات الماستر )‪ (Master Pages‬التي‬ ‫تدعم مبدأ عدم تكرار تنسيقات مشتركة بين صفحات الموقع وجعلها أجزاء مشتركة للصفحات‪ .‬وحتى ال تبقى‬ ‫المواقع بدون ذاكرة طويلة المدى أي بدون قاعدة بيانات‪ ،‬فإننا نعرض أسلوبا مبسطا لكيفية الوصول إلى قاعدة‬ ‫البيانات للقراءة والكتابة‪ .‬ثم نتعرف على أهم األدوات التي تتخصص بعرض البيانات وتوفير إمكانيات سهلة‬ ‫لتحريرها مثل الـ ‪ GridView‬والـ ‪ Repeater‬والـ ‪ .DataList‬بعد ذلك نعالج قضية رفع وتحميل الملفات‬ ‫العادية أي النصيّة والصور وحفظها ضمن منظومة الملفات (أي الـ ‪ )File System‬وأيضا حفظها داخل قاعدة‬ ‫البيانات بواسطة حقول من نوع ‪ .OLE Object‬وأخيرا نشرح موضوع خدمات الويب (أي الـ‬ ‫‪ )Services‬المؤسسة على لغة ‪ .XML‬طبعا بعد شرح مبسط للغة ‪ XML‬وقواعدها ومكوناتها‪.‬‬ ‫يفترض الكتاب أن لدى القارئ معرفة تفصيلية مُسبقة في المواضيع التالية‪:‬‬ ‫•‬

‫أسس علم الحاسوب بلغة ‪ :C#‬أي أنه يفترض معرفة المواضيع التالية‬ ‫‪ o‬المبنى العام للبرنامج‬ ‫‪ o‬المتغيرات وأنماطها المختلفة مثل ‪int, float, double, char, string, bool‬‬ ‫‪ o‬أوامر الطباعة واالستقبال‬ ‫‪ o‬أمر ‪ if‬البسيط والمركب‬ ‫‪ o‬الحلقات ‪ while‬و ‪for‬‬ ‫‪ o‬العمليات الستاتية·‬ ‫‪ o‬المصفوفات أحادية وثنائية األبعاد·‬ ‫‪ o‬النصوص أي الفئة ‪ string‬وواجهتها‬

‫•‬

‫أسس البرمجة موجهة الكائنات بلغة ‪ :C#‬أي أنه يفترض معرفة المواضيع التالية‬ ‫‪ o‬الفئات ‪Classes -‬‬ ‫‪ o‬الكائنات – ‪Objects‬‬ ‫‪ o‬المؤشرات ‪References -‬‬ ‫‪o‬‬

‫‪static‬‬

‫‪5‬‬

‫‪Web‬‬


‫‪ o‬الوراثة ‪ Inheritance -‬وتعدد األشكال ‪Polymorphism -‬‬ ‫‪ o‬الواجهات ‪Interfaces -‬‬ ‫•‬

‫لغة ‪ HTML‬وجميع وسومها‬

‫•‬

‫ومعرفة أساسية عن لغة ‪JavaScript‬‬

‫•‬

‫أسس قواعد البيانات العالئقية – ‪ Relational Data Bases‬مثل ‪MS Access‬‬

‫كلي أمل بأن يخدم هذا الكتاب كل من أراد معرفة أسس هذا المنهج المتميز من مناهج برمجة المواقع الديناميكية‪.‬‬

‫‪6‬‬


‫البرمجة بتسويق األحداث‬ ‫‪Events-Driven Programming‬‬ ‫موديل الطلب والرد ‪Request – Response Model‬‬

‫الزبون ‪Client -‬‬

‫الخادم ‪Server -‬‬

‫معالجة الطلب‬

‫ارسال الطلب‬ ‫الى الخادم‬

‫بناء الطلب‬

‫انشاء كود الـ‬ ‫‪HTML‬‬

‫ارسال صفحة الـ‬ ‫‪HTML‬‬

‫عرض الصفحة‬

‫الطلب ‪ :Request -‬قد يكون‬ ‫•‬

‫طلب صفحة ما للمرة األولى‬

‫•‬

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

‫معالجة الطلب‪ :‬تتضمن ترجمة العمليات المكتوبة بلغة ‪ C#‬التي تمثل بشكل أساسي معالجات األحداث وعرض‬ ‫النتائج في ملف ‪HTML‬‬ ‫ارسال صفحة الـ ‪Response :HTML‬‬

‫‪7‬‬


‫البرمجة بتسويق األحداث هي إحدى مناهج البرمجة )‪ (Programming Paradigms‬التي يكون فيها مجرى‬ ‫تنفيذ األوامر )‪ (Flow of the program‬خاضعا للترتيب الذي تحصل فيه أحداث معينة‪ .‬فالبرنامج الذي يكون‬ ‫مكتوبا وفق هذا المنهج يبقى بعد تشغيله في وضع االنتظار حيث ينتظر حصول أحداث معينة‪ .‬فإذا حصلت األحداث‬ ‫التي يتوقعها (أي التي ُكتبت دوال وإجراءات لمعالجتها) يقوم حينها بتنفيذ هذه الدوال واإلجراءات‪ .‬وال يمكن‬ ‫للبرنامج أن يتوقع ما الذي سيحدث في الخطوة التالية‪ ،‬لذا فإن البرنامج يقسم إلى عدة وظائف ينفذ كل منها عندما‬ ‫يحدث ما يسمى بالحدث‪ .‬والمقصود باألحداث )‪ (Events‬عمليات الفارة المختلفة التي ينفذها المستخدم ‪Mouse‬‬ ‫‪ .User's Actions‬مثل‪ :‬الضغط مرة واحدة على الزر األيسر للفارة وهو ما يسمى ‪ Left Click Event‬أو‬ ‫الضغط مرتين متتاليتين على الزر األيسر للفارة وهو ما يسمى‬ ‫‪ .Left Double Click Event‬أو الضغط على الزر األيمن للفارة وهو ما يسمى‬ ‫‪ Right Click Event‬وغيرها‪.‬‬ ‫الضغط على أي زر من أزرار لوحة المفاتيح يُعتبر حدثا‪ .‬مثل‪ KeyDown Event :‬و ‪KeyUp Event‬‬ ‫وغيرها‬ ‫المجسات )‪ (Sensors‬المختلفة التي تكون متصلة مع الحاسوب‪ .‬هذه المجسات تبلغ الحاسوب ( أي البرنامج‬ ‫المسئول عن معالجة المعلومات الواصلة من المجسات) من خالل إحداث حدث معين متفق عليه‪ .‬هذه العملية تسمى‬ ‫)‪(Firing an Event‬‬ ‫الرسائل )‪ (Messages‬التي ترسلها البرامج المختلفة للتواصل فيما بينها بهدف تبادل المعلومات‪ .‬مثال‪ :‬البرامج‬ ‫التي تشغل األفالم ترسل عند نهاية عرض فلم ما رسالة أو حدثا يبلغ المعنيين باألمر أن عرض الفل قد انتهى‪ .‬في‬ ‫هذه الحالة تقوم البرامج المعنية بمعالجة هذا الحدث )‪ (Event Handling‬ومثال تشغيل فلم آخر أو مقطع دعاية‬ ‫وما إلى ذلك‪.‬‬ ‫يُعتبر هذا المنهج البرمجي مرتبطا ارتباطا حتميا مع البرمجة المرئية )‪ .(Visual Programming‬في البرمجة‬ ‫المرئية تكون البرامج أو ما يسمى بالتطبيقات )‪ (Applications‬عبارة عن نافذة رئيسية تظهر فيها عادة القائمة‬ ‫الرئيسية التي تحتوي على العمليات الرئيسة التي يوفرها البرنامج‪ .‬وعادة ما تظهر أيضا نوافذ الحوار المختلفة التي‬ ‫تحتوي على ما يسمى باألدوات )‪ (Tools, Controls‬مثل األزرار وصناديق النصوص والقوائم المنسدلة وما‬ ‫شابهها‪ .‬مثال على هذا النوع من التطبيقات جميع برامج أوفس )‪ (MS Office Applications‬مثل ‪MS‬‬ ‫‪ .Word, MS Excel, MS PowerPoint‬أيضا مواقع االنترنت الديناميكية أي التي تحتوي على نماذج‬ ‫)‪ (Forms‬وأدوات تعتبر أمثلة على برامج مرئية )‪ُ .(Visual Programs‬تسمى هذه النوافذ أيضا واجهة‬ ‫المستخدم الرسومية )‪ .(GUI = Graphical User Interface‬ولذلك يُستعمل أحيانا المصطلح‬ ‫‪ Programming‬بدال من المصطلح ‪.Visual Programming‬‬

‫‪8‬‬

‫‪GUI‬‬


‫مراحل برمجة المواقع الديناميكية‬ ‫•‬

‫مرحلة التصميم )‪ (Design Phase‬ويقوم بها عادة مصمم المواقع )‪(Web Designer‬‬

‫•‬

‫مرحلة كتابة معالجات األحداث )‪ (Events Handler Programming Phase‬ويقوم بها عادة‬ ‫مبرمجو المواقع )‪(Web Developer‬‬

‫مهمة مرحلة التصميم تصميم النماذج المختلفة واألدوات التابعة لها بواسطة لغة ‪ .HTML‬هذه المرحلة ُتشرح عادة‬ ‫في كتب الـ ‪ HTML‬المعروفة والمنتشرة‪ .‬في هذا الفصل سنشرح المرحلة الثانية بالتفصيل ومن خالل مهام وأمثلة‪.‬‬ ‫ولكن قبل ذلك سنتحدث عن األدوات‪.‬‬

‫‪9‬‬


‫األدوات ‪Controls‬‬ ‫ُتعتبر األدوات المكونات األساسية للنماذج‪ .‬فاذا ألقينا نظرة على أي نموذج توفره صفحة انترنت ما‪ ،‬نجده مكونا من‬ ‫عدة أنواع من األدوات‪ُ .‬تقسم ‪ ASP.NET‬األدوات من الناحية الوظيفية الى عدة أقسام نجدها في الـ ‪Toolbox‬‬ ‫وهي‪:‬‬

‫المجموعة‬ ‫‪Standard‬‬ ‫‪Data‬‬

‫أمثلة‬

‫الشرح‬

‫…‪TextBox, Label, Button,‬‬

‫األدوات األساسية‬

‫…‪GridView, Repeater, DataList,‬‬

‫األدوات المتخصصة بعرض‬ ‫المعلومات على شكل جداول‬

‫‪Validation‬‬

‫أدوات التحقق من صحة اإلدخال‬

‫‪Navigation‬‬

‫أدوات لالنتقال داخل الموقع بين‬

‫‪RequiredFieldValidator, RangeValidator,‬‬ ‫… ‪RegularExpressionValidator,‬‬ ‫…‪Menu, SiteMapPath, TreeView,‬‬

‫صفحاته المختلفة‬ ‫‪Login‬‬

‫أدوات الدخول الى الموقع‬

‫‪Login, ChangePassword,‬‬ ‫… ‪CreateUserWizzard,‬‬ ‫… ‪CatalogZone, ConnectionZone,‬‬

‫‪WebParts‬‬ ‫‪AJAX‬‬ ‫‪Extensions‬‬

‫…‪ScriptManager, UpdatePanel,‬‬

‫أدوات مبنيّة على أساس‬ ‫‪ JavaScript‬تعمل دون الحاجة‬ ‫الى تحميل جديد للصفحة‪.‬‬

‫‪Dynamic‬‬

‫‪DynamicControl, DynamicDataManager,‬‬

‫‪10‬‬


‫…‬

‫‪Data‬‬ ‫‪Reporting‬‬

‫أدوات تدعم انتاج تقارير عن‬

‫‪ReportViewer‬‬

‫البيانات المختلفة‪.‬‬ ‫‪HTML‬‬

‫… ‪Input, Textarea, Select,‬‬

‫أدوات‪HTML‬‬

‫في هذا الفصل سوف نشرح بعضا من هذه األدوات وهي األكثر شيو ًعا واستعماال في النماذج‪ .‬لكن قبل أن نبدأ‬ ‫بالتفاصيل ال بد من التنويه الى أن األدوات عبارة عن كائنات أي ‪ Objects‬المعروفة لنا من البرمجة موجهة‬ ‫الكائنات )‪ .(Object Oriented Programming‬هناك كانت للكائنات مجموعة من الخصائص والبواني‬ ‫والعمليات‪ .‬هنا ُتضاف إليهم مجموعة من األحداث (أي‬ ‫‪ )Events‬تضيف الى الكائن جانبا تفاعليًا مع المُستخدم وتمكنهم من معرفة ما يطلبه المستخدم وتلبية هذه الطلبات‪.‬‬ ‫أسهل طريقة إلضافة األداة الى النموذج تكون عن طريق سحبها من الـ‬ ‫‪ Toolbox‬ووضعها في المكان المناسب في النموذج‪ .‬بعد ذلك بإمكاننا اعطاء القيم المناسبة للخصائص التي نريد‬ ‫وكتابة معالجات لألحداث التي تهمنا‪ .‬تظهر نافذة الخصائص التابعة لألداة من خالل الضغط على الزر األيمن للفارة‬ ‫وهي موضوعة على األداة واختيار العنصر ‪ Properties‬منها وذلك في عرض الـ ‪ Design‬للصفحة‪ .‬نبين ذلك‬ ‫بالنسبة لألدوات التالية‪:‬‬ ‫األداة ‪TextBox‬‬ ‫و ُتسمى أيضا "صندوق نص" ُتمكن المستخدم من ادخال األنواع األساسية من المعلومات مثل األعداد والنصوص‪.‬‬

‫‪11‬‬


‫بعد اختيار العنصر ‪ Properties‬تظهر نافذة الخصائص التالية‪:‬‬

‫‪12‬‬


13


‫وأيضا بأن‬ ‫ال بد من مالحظة أن محتوى نافذة الخصائص تتغير بتغير األداة التي تم اختيارها وتحديدها بالنموذج‪.‬‬ ‫ً‬ ‫معظم الخصائص ُمشتركة لمعظم األدوات أي أنها متوفرة بنفس األسماء عند معظم األدوات‪ .‬بالنسبة لألداة‬ ‫‪ TextBox‬فان أهم خاصيتين حاليًا هما‪:‬‬ ‫‪:ID‬‬ ‫اسم األداة‪ .‬وهي عبارة عن نص يميز األداة يجب أن تتوفر فيه الشروط التي يجب أن تتوفر في كل المُعرّ فات‬ ‫)‪ (Identifiers‬وهي‪:‬‬ ‫•‬

‫ال يجوز أن يبدأ برقم‬

‫•‬

‫ال يجوز أن يحتوي على فراغات‬

‫•‬

‫ال يجوز أن يحتوي على رموز محجوزة مثل … ‪+,-, /, %, ;,‬‬

‫•‬

‫ً‬ ‫محجوزا مثل … ‪class, for, while,‬‬ ‫ال يجوز أن اسمًا‬

‫من المُفضل اعطاء الكائن اسمًا معبرً ا عن وظيفته وجعل االسم يبدأ بنوع الكائن‪ .‬مثل‬ ‫‪TextBoxName‬‬ ‫‪:Text‬‬ ‫وظيفة هذه الخاصّة‪ ،‬حفظ القيمة الموجودة في األداة‪ .‬وهي قابلة للقراءة والكتابة‪.‬‬ ‫•‬

‫القراءة تعني أننا نستطيع الحصول على القيمة الموجودة في األداة‪.‬‬ ‫مثال‬ ‫;‪string strName = TextBoxName.Text‬‬

‫•‬

‫والمقصود بالكتابة أي أننا نستطيع وضع قيمة في األداة‪.‬‬ ‫مثال‬ ‫;"‪TextBoxName.Text = "Khalil‬‬

‫‪14‬‬


‫الجدول التالي يُلخص بعض الخصائص االضافية لألداة ‪:TextBox‬‬ ‫الخاص ّية‬ ‫‪Visible‬‬

‫الشرح‬ ‫‪ :true‬إلظهار األداة‬ ‫‪ :false‬إلخفائها‬

‫‪Enabled‬‬

‫‪ :true‬يمكن للمستخدم أن يغير محتوى األداة‬ ‫‪ :false‬ال يمكن للمستخدم أن يغير محتوى األداة‬ ‫يمكن الحصول على نفس النتيجة من خالل الخاصيّة ‪ReadOnly‬‬

‫‪TextMode‬‬

‫‪ :SingleLine‬محتوى األداة يُكتب في سطر واحد فقط‬ ‫‪ :MultiLine‬توفر األداة عدة أسطر للكتابة لتصبح شبيهة باألداة ‪ .Textarea‬يمكن تحديد عدد‬ ‫األسطر من خالل الخاصيّة ‪.Rows‬‬ ‫‪ :Password‬محتوى األداة ال يظهر عند الكتابة وانما يظهر الرمز ● مكان كل رمز اُدخل‬

‫‪Width‬‬

‫عرض األداة‬

‫‪Height‬‬

‫ارتفاع األداة‬

‫‪ MaxLength‬العدد األكبر للرموز التي يُمكن ادخالها‬ ‫‪ForeColor‬‬ ‫‪Font‬‬ ‫‪ToolTip‬‬ ‫‪Rows‬‬

‫لون النص أي المحتوى‬ ‫اسم الخط‬ ‫نص يظهر للمستخدم إلرشاده كلما اقترب الماوس من األداة‬ ‫عدد األسطر عندما يكون ‪ TextMode‬من نوع ‪MultiLine‬‬

‫‪15‬‬


‫سيتم شرح باقي الخصائص الح ًقا ألنها تتطلب معرفة اضافية‪ .‬أما كود التنسيق فانه يظهر هكذا‪:‬‬ ‫>‪<body‬‬ ‫>"‪<form id="form1" runat="server‬‬ ‫>‪<div‬‬ ‫>‪<table‬‬ ‫>‪<tr‬‬ ‫>‪<td‬‬ ‫‪Please enter your name‬‬ ‫>‪</td‬‬ ‫>‪<td‬‬ ‫‪<asp:TextBox‬‬

‫الحظ أن الخصائص وقيمها تُكتب‬ ‫في وسم البداية بالضبط كما هو‬ ‫األمر بلغة ‪HTML‬‬

‫"‪ID="TextBoxName‬‬ ‫"‪runat="server‬‬ ‫>"االسم ادخال الرجاء"=‪ToolTip‬‬ ‫>‪</asp:TextBox‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬ ‫>‪</table‬‬ ‫>‪</div‬‬ ‫>‪</form‬‬ ‫>‪</body‬‬

‫‪16‬‬


‫األداة ‪Label‬‬ ‫ُتمكننا هذه األداة من اظهار معلومات للمستخدم للقراءة فقط و ُتستخدم الظهار عناوين ألدوات أخرى أو إلظهار‬ ‫رسائل معينة للمُستخدم‪ .‬مثال بإمكاننا وضع النص ‪ Please enter your name‬الذي يظهر في المثال السابق‬ ‫الى يسار صندوق النص ‪:TextBoxName‬‬ ‫>‪<body‬‬ ‫>"‪<form id="form1" runat="server‬‬ ‫>‪<div‬‬ ‫>‪<table‬‬ ‫>‪<tr‬‬ ‫>‪<td‬‬ ‫‪<asp:Label‬‬ ‫"‪ID="LabelTitle‬‬ ‫"‪runat="server‬‬ ‫>"‪Text="Please enter your name‬‬ ‫>‪</asp:Label‬‬ ‫>‪</td‬‬ ‫>‪<td‬‬ ‫‪<asp:TextBox‬‬ ‫"‪ID="TextBoxName‬‬ ‫"‪runat="server‬‬ ‫>"االسم ادخال الرجاء"=‪ToolTip‬‬ ‫>‪</asp:TextBox‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬ ‫>‪</table‬‬ ‫>‪</div‬‬ ‫>‪</form‬‬ ‫>‪</body‬‬ ‫الصورة التالية ُتظهر مجموعة الخصائص التابعة لألداة ‪ Label‬ويمكننا مالحظة ما قيل ساب ًقا من أن معظم‬ ‫الخصائص التي تم شرحها ساب ًقا متوفرة عند هذه األداة‪:‬‬

‫‪17‬‬


‫هذه األداة قادرة أي ً‬ ‫ضا كما سنبين الح ًقا على أخذ كود ‪ HTML‬كقيمة واظهاره في النموذج بصيغته النهائية أي مُنسقا‬ ‫كما يجب‪.‬‬ ‫‪18‬‬


‫األداة ‪Button‬‬ ‫و ُتسمى أي ً‬ ‫ضا ‪ Command Button‬بمعنى زر أمر أي أنها ُتستعمل لتوفير امكانية للمُستخدم لطلب تنفيذ أوامر‬ ‫مثل‪ .Login, Open, Close, Cancel, Add, Calculate,… :‬الحظ أن جميع هذه الكلمات مكتوبة‬ ‫بصيغة األمر‪.‬‬ ‫نضيف هذه األداة الى مثالنا ليصبح كالتالي‪:‬‬

‫أم كود التنسيق فهو التالي‪:‬‬ ‫>‪<tr‬‬ ‫>"‪<td colspan="2‬‬ ‫‪<asp:Button‬‬ ‫"‪ID="ButtonSayHi‬‬ ‫"‪runat="server‬‬ ‫>‪Text="Say Hi" /‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬

‫واآلن سننتقل الجزء الثاني مما تملكه هذه األدوات وهي مجموعة األحداث‪ .‬لكن قبلها سنتحدث عن البرمجة بتسويق‬ ‫األحداث‪.‬‬ ‫مثال‬ ‫المطلوب تصميم وبرمجة آلة حاسبة بسيطة قادرة على جمع عددين حقيقيين‪ .‬التصميم المقترح هو التالي‪:‬‬

‫‪19‬‬


:‫ المصمم لهذا النموذج هو التالي‬HTML ‫كود الـ‬ <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>A simple calculator</title> </head> <body> <form id="form1" runat="server"> <div> <table align="center" border="1" cellpadding="0" cellspacing="0" width="381" id="AutoNumber1" style="border-bottom: 1.5pt solid black; background-color: silver; border-left-style:none; border-right-style:none; border-top-style:none" fpstyle="9,111111100"> <tr> <td style="width: 100px; color: black; font-weight:bold; backgroundcolor:#FFFFEF" align="center"> <asp:Label ID="Label1" runat="server" Text="Value 1"></asp:Label> </td> <td align="center"> <asp:TextBox ID="TextBoxValue1" runat="server" Width="150px"></asp:TextBox> </td> </tr> <tr> <td style="width: 100px; color: black; font-weight:bold; backgroundcolor:#FFFFEF" align="center">

20


<asp:Label ID="Label2" runat="server" Text="Value 2"></asp:Label> </td> <td align="center"> <asp:TextBox ID="TextBoxValue2" runat="server" Width="150px"></asp:TextBox> </td> </tr> <tr> <tr> <td colspan="2" style="width: 100px; color: black; font-weight:bold; backgroundcolor:#FFFFEF" align="center"> <asp:Label ID="Label3" runat="server" Text="The Result is "></asp:Label> </td> </tr> <tr> <td colspan="2" align="center"> <table> <tr> <td align="center"> <asp:Button ID="ButtonCalc" runat="server" Text="Calculate" Width="105px" /> </td> <td align="center"> <asp:Button ID="ButtonCancel" runat="server" Text="Cancel" Width="105px" /> </td> </tr> </table> </td> </tr> </table> </div> </form>

21


‫>‪</body‬‬ ‫>‪</html‬‬ ‫يظهر الكود بأن لدينا ‪ 7‬أدوات أو ‪ Controls‬موجودة بداخل جدول لترتيبها بالشكل المطلوب‪ .‬كما هو معلوم فان‬ ‫هذه األدوات يجب أن تكون موجودة ضمن نموذج معين أي ضمن ‪ .Form Tag‬هذه األدوات هي‪:‬‬ ‫االسم أو الـ ‪ID‬‬

‫األداة‬ ‫‪asp:Label‬‬

‫‪Label1‬‬

‫‪asp:Label‬‬

‫‪Label2‬‬

‫‪asp:Label‬‬

‫‪Label3‬‬

‫‪asp:TextBox‬‬

‫‪TextBoxValue1‬‬

‫‪asp:TextBox‬‬

‫‪TextBoxValue2‬‬

‫‪asp:Button‬‬

‫‪ButtonCalc‬‬

‫‪asp:Button‬‬

‫‪ButtonCancel‬‬

‫الشرح‬ ‫تستعمل هذه األداة إلظهار نصوص‬ ‫ثابتة كعناوين ألدوات أخرى‪ .‬هنا‬ ‫استعملناها لتوجيه المستخدم بأن‬ ‫يدخل في صندوق النص الذي إلى‬ ‫يمين األداة القيمة األولى المُراد‬ ‫جمعها‪.‬‬ ‫توجه المستخدم بأن يدخل في‬ ‫صندوق النص الذي إلى يمين‬ ‫األداة القيمة الثانية المُراد جمعها‪.‬‬ ‫تستعمل هذه األداة لعرض نتيجة‬ ‫الجمع‪.‬‬ ‫صندوق النص الذي سيستوعب‬ ‫القيمة األولى‬ ‫صندوق النص الذي سيستوعب‬ ‫القيمة الثانية‬ ‫الزر الذي سيكون على المستخدم‬ ‫أن ينقره نقرة واحدة بالزر األيسر‬ ‫للفارة وذلك بعد أن يُزود القيم‬ ‫الالزمة لتنفيذ عملية الجمع وإظهار‬ ‫النتيجة‪.‬‬

‫‪22‬‬

‫الزر الذي سيكون على المستخدم‬ ‫أن ينقره نقرة واحدة بالزر األيسر‬ ‫للفارة وذلك لتفريغ صناديق‬ ‫النصوص من محتواها‪.‬‬


‫البرمجة‬ ‫إذا شغلنا هذا الموقع سوف يظهر النموذج بالضبط كما نريده وبإمكاننا أن نستعمل صناديق النصوص لكتابة األعداد‬ ‫لكن إذا نقرنا بالزر األيسر للفارة على احد األزرار )‪ (Calculate or Cancel‬فان شيئا ً لن يحدث‪ .‬لماذا؟‬ ‫اإلجابة بسيطة ألننا لم نكتب معالجا ً لهذه األحداث‪ .‬أي لم نكتب االجراء الذي سيأخذ القيم من صناديق النصوص ثم‬ ‫يجمعها ويضع النتيجة في األداة ‪.Label3‬‬ ‫كتابة معالجات األحداث‬ ‫يوجد لكل أداة من األدوات المتوفرة في القائمة التي يعرضها الـ ‪ Toolbox‬مجموعة من األحداث التي تدعمها هذه‬ ‫األدوات‪ .‬عند التعرف على أية أداة سيكون التعرف على األحداث التي تدعمها األداة جزءا من عملية دراسة األداة‬ ‫ودراسة إمكانياتها وخصائصها‪. .‬لمعرفة مجموعة األحداث ألية أداة نختار األداة في عرض الـ ‪ Design‬ومن ثم‬ ‫نحضر نافذة الخصائص من خالل النقر على الزر األيمن للفارة واختيار ‪ Properties‬من القائمة التي تظهر‬ ‫(احرص على وضع الفارة على األداة حين النقر على الزر األيمن للفارة) ‪.‬‬

‫‪23‬‬


‫حينها ستظهر(عادة) في الجهة اليمنى من الـ ‪ Web Developer‬نافذة الخصائص التي تحتوي على جميع‬ ‫خصائص األداة بما في ذلك أيضا األحداث‪ .‬الوصول إلى قائمة األحداث يكون من خالل النقر على الزر‬ ‫الموجود أعلى نافذة الخصائص‬ ‫النقر على زر األحداث يظهر قائمة األحداث التالية‬

‫انتبه إلى أن محتوى القائمة سيكون مختلفا حسب األداة التي نختارها في عرض الـ ‪ !Design‬الحظ أيضا المالحظة‬ ‫الموجودة في نهاية نافذة األحداث التي تشرح متى يحصل الحدث المُختار‪.‬‬ ‫لكتابة معالج لحدث ما أُكتب اسما في الصندوق الذي إلى يمين اسم الحدث ثم أضغط ‪ .Enter‬انتبه إلى أن االسم‬ ‫الذي تكتبه هنا هو اسم ألجراء أي لعملية ولذلك عليه أن يكون محققا لشروط تسمية المُعرفات ‪(Identifiers‬‬ ‫)‪ .Naming Rules‬مثال ‪ OnClickCalculate‬فقد جرت العادة عند مبرمجي الـ ‪ GUIs‬أن يبدأ اسم معالج‬ ‫الحدث بالنص ‪ On‬للداللة على أن هذا هو المقطع الذي يستدعى كردة فعل على الحدث ‪(Response On an‬‬ ‫)‪ .Event‬في االسم يظهر أيضا اسم الحدث ‪ Click‬واسم يدل على الزر‪ .‬طبعا هذه الطريقة للتسمية ليست ملزمة‬ ‫وإنما هي إحدى اإلمكانيات للتسمية‪ .‬بعد إعطاء االسم والضغط على ‪ Enter‬سينقُلُك ‪ Web Developer‬إلى‬ ‫النافذة التي ستكتب فيها ما يسمى بالـ ‪ Code Behind‬أي جميع معالجات األحداث وجميع الفئات والعمليات‬ ‫التابعة لصفحة انترنت معينة‪.‬‬ ‫‪public partial class _Default : System.Web.UI.Page‬‬ ‫{‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫{‬ ‫}‬

‫‪24‬‬


‫)‪protected void OnClickCalculate(object sender, EventArgs e‬‬ ‫{‬ ‫}‬ ‫}‬ ‫انتبه إلى رأس اإلجراء‬ ‫)‪protected void OnClickCalculate(object sender, EventArgs e‬‬ ‫في األمثلة المتقدمة التي ستأتي الحقا سوف نتعرف أكثر على قائمة البرامترات ووظيفة كل بارامتر‪.‬‬ ‫هنالك إمكانية ثانية لكتابة معالج الحدث بدون استعمال نافذة األحداث وذلك من خالل النقر مرتين بالفارة ‪Double‬‬ ‫‪Click‬على األداة التي نريد أن نكتب لها معالج حدث‪ .‬حينها سيكتب ‪ Web Developer‬معالجا لما يُسمى‬ ‫بالحدث االفتراضي لألداة )‪ (Default Event‬وهو يختلف من أداة إلى أُخرى‪ .‬فمثال الحدث االفتراضي لألداة‬ ‫‪ asp:Button‬هو الحدث ‪ Click‬الذي يحصل عندما ينقر المستخدم بالزر األيسر للفارة على األداة‪ .‬بينما الحدث‬ ‫االفتراضي لألداة ‪ asp:TextBox‬هو الحدث ‪ TextChanged‬الذي يحصل عندما يتغير محتوى صندوق‬ ‫النص‪ .‬يجب أن تالحظ أيضا أن االسم الذي يعطيه ‪ Web Developer‬للمعالج عند استعمال هذه اإلمكانية هو‬ ‫كالتالي‪:‬‬ ‫)‪protected void ButtonCalc_Click(object sender, EventArgs e‬‬ ‫فقد سار ‪ Web Developer‬في التسمية حسب القاعدة "اسم الحدث_اسم األداة"‪.‬‬ ‫ننتقل اآلن إلى برمجة معالج الحدث ‪ OnClickCalculate‬بشكل كامل‪ .‬األلغوريثم التالي يبين ال ُخ ُ‬ ‫طوات الالزمة‪:‬‬ ‫‪ُ .1‬خذ النص الموجود في الصندوق ‪ TextBoxValue1‬وحوله إلى عدد حقيقي وأحفظ العدد في المتغير ‪Val1‬‬ ‫‪ُ .2‬خذ النص الموجود في الصندوق ‪ TextBoxValue2‬وحوله إلى عدد حقيقي وأحفظ العدد في المتغير ‪Val2‬‬ ‫‪ .3‬أحفظ القيمة ‪ Val1 + Val2‬في المتغير ‪Result‬‬ ‫‪ .4‬حول قيمة المتغير ‪ Result‬إلى نص بواسطة الدالة )(‪ ToString‬وأعطه كقيمة إلى الخاصية ‪ Text‬التابعة‬ ‫لألداة ‪ Label3‬وصلها بجملة توضيح مناسبة مثل ‪The Result is‬‬ ‫في لغة ‪ C#‬تكون البرمجة كالتالي‪:‬‬ ‫)‪protected void OnClickCalculate(object sender, EventArgs e‬‬ ‫{‬ ‫;)‪double Val1 = double.Parse(TextBoxValue1.Text‬‬ ‫;)‪double Val2 = double.Parse(TextBoxValue2.Text‬‬ ‫;‪double Result = Val1 + Val2‬‬ ‫;)(‪Label3.Text = "The Result is " + Result.ToString‬‬ ‫}‬

‫‪25‬‬


‫اآلن إذا شغلنا الموقع وأدخلنا القيم التي نريد ثم نقرنا الزر ‪ Calculate‬ستظهر النتيجة كالتالي‪:‬‬

‫‪26‬‬


‫تمارين‬ ‫تمرين رقم ‪1‬‬ ‫أُكتب معالج حدث للحدث ‪ Click‬للزر ‪ .Cancel‬على معالج الحدث أن يُفرغ صناديق النصوص من محتواها وأن‬ ‫يحذف اإلجابة األخيرة أيضا‪.‬‬ ‫الحظ أنه بإمكانك القيام بذلك من خالل استدعاء الصفحة بواسطة األمر‬

‫;)"‪Response.Redirect("Default.aspx‬‬ ‫القاعدة العامة لنقل المستخدم الى صفحة معينة من صفحات الموقع‪:‬‬ ‫;)"‪Response.Redirect("PageName.aspx‬‬ ‫تمرين رقم ‪2‬‬ ‫المطلوب تصميم وبرمجة إمكانية تمكن المستخدم من اختيار تاريخ ما‪ .‬إحدى المشاكل التي نواجهها عندما نطلب من‬ ‫ً‬ ‫تاريخا‬ ‫المستخدم تاريخا ما (مثال تاريخ ميالد المستخدم) هي فحص هل القيمة أو القيم التي أُدخلت تمثل ح ًقا‬ ‫صحيحا‪ .‬الحل األمثل لمثل هذه المهام إعطاء المستخدم إمكانية االختيار بدال من إمكانية الكتابة‪ .‬هذا الحل ُيعتبر‬ ‫أيضا لطيفا مع المستخدم أي ‪ .User-friendly‬فاالختيار أسهل وأسرع من الكتابة ويحول دون حصول أخطاء‪.‬‬ ‫سوف نستعمل هنا أداة القائمة المنسدلة ‪ asp:DropDownList‬ثالث مرات على النحو وبالتنسيق التالي‪:‬‬

‫األداة‬ ‫‪asp:DropDownList‬‬

‫االسم أو الـ ‪ID‬‬ ‫‪DropDownListDay‬‬

‫الشرح‬ ‫تستعمل هذه األداة إلظهار األيام‬ ‫الممكنة وذلك وفقا للسنة والشهر‬ ‫المختارين‪ .‬مثال إذا كانت السنة‬ ‫المختارة كبيسة والشهر المختار‬ ‫شباط فسيكون مجال األيام من ‪1‬‬ ‫إلى ‪ .29‬خالف ذلك ستحتوي هذه‬ ‫القائمة بالنسبة لشهر شباط فقط ‪28‬‬ ‫يوما‪ .‬كذلك األمر بالنسبة لألشهر‬ ‫التي فيها ‪ 30‬يوما وتلك التي فيها‬ ‫‪ 31‬يوما‪.‬‬

‫‪27‬‬


‫‪asp:DropDownList‬‬

‫‪DropDownListMonth‬‬

‫‪asp:DropDownList‬‬

‫‪DropDownListYear‬‬

‫تستعمل هذه األداة إلظهار األشهر‬ ‫من ‪ 1‬إلى ‪12‬‬ ‫تستعمل هذه األداة إلظهار السنين‬ ‫من المجال الذي نريده‪.‬‬

‫من الواضح أن أكثر قائمة من بين القوائم الثالث تحتاج إلى حرص ودقة في البرمجة هي قائمة األيام أي‬ ‫‪ .DropDownListDay‬محتوى باقي القوائم ثابت بينما محتوى هذه القائمة متعلق بالسنة والشهر المختارين‪ .‬متى‬ ‫سنمأل هذه القائمة بالقيم الممكنة؟ الجواب‬ ‫•‬

‫في المرة األولى التي ُتستدعى فيها الصفحة (أي عندما تكون الخاصية ‪ IsPostBack‬التابعة لكائن الصفحة‬ ‫أي الـ ‪ .)false Page‬هنا علينا برمجة معالج الحدث ‪ Load‬الخاص بالصفحة‪.‬انتبه الى أن ‪Web‬‬ ‫‪ Developer‬يضيف لكل صفحة ‪ aspx‬معالجا فارغا لهذا الحدث‪ .‬سنأخذ التاريخ الحالي ومن ثم نقوم‬ ‫باختيار أجزائه المختلفة (اليوم والشهر والسنة) في القوائم الثالث‪.‬‬

‫•‬

‫في كل مرة يتم فيها تغيير السنة أو الشهر من قبل المستخدم‪ .‬السؤال هنا هو كيف نعرف (أي كيف يعرف‬ ‫الموقع) أن المستخدم غير اختيار السنة أو الشهر؟ هذا يكون من خالل الحدث‬ ‫‪( SelectedIndexChanged‬أو الحدث ‪ (SelectedTextChanged‬الذي ُتحدثه القائمة المنسدلة‪.‬‬ ‫هذا الحدث هو أيضا الحدث االفتراضي للقائمة المنسدلة‪ .‬انتبه إلى أن القائمة سوف ال تحدث الحدث إال إذا‬ ‫كانت قيمة الخاصية ‪ AutoPostBack‬التابعة لألداة ‪ .True‬هنالك عدة امكانيات لتغيير قيمة هذه الخاصية‪:‬‬ ‫‪ o‬في نافذة الـ ‪ Source‬أكتب "‪ AutoPostBack="True‬داخل وسم البداية ألداة القائمة المنسدلة‪:‬‬

‫>"‪<asp:DropDownList ID="DropDownListDay" AutoPostBack="True‬‬ ‫‪ o‬في نافذة الخصائص التابعة لألداة‪:‬‬

‫‪28‬‬


‫الذي يظهر في أعلى القائمة المنسدلة المُراد تغيير‬

‫ أضغط على السهم‬Design ‫ في نافذة الـ‬o

"Enable ‫قيمة خاصيتها ثم اجعل في القائمة التي تظهر صندوق االختيار المسمى‬ .‫ مُختارا‬AutoPostBack"

.‫انتبه إلى أن السهم يظهر فقط إذا حركنا الفارة فوق القائمة المنسدلة‬ :‫البرمجة‬ ‫معالجات األحداث‬ protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { FillMonthsDDL(); FillYearsDDL(); FillDaysDDL(); } } protected void DropDownListMonth_SelectedIndexChanged(object sender, EventArgs e) { FillDaysDDL(); ShowSelectedDate(); } protected void DropDownListYear_SelectedIndexChanged(object sender, EventArgs e) { FillDaysDDL(); ShowSelectedDate(); } 29


protected void DropDownListDay_SelectedIndexChanged(object sender, EventArgs e) { ShowSelectedDate(); } ‫العمليات المساعدة‬

private void FillMonthsDDL() { DropDownListMonth.Items.Clear(); for (int i = 1; i <= 12; i++) DropDownListMonth.Items.Add(i.ToString()); } private void FillYearsDDL() { DropDownListYear.Items.Clear(); for (int i = 1965; i <= 2010; i++) DropDownListYear.Items.Add(i.ToString()); } private void FillDaysDDL() { int month = int.Parse(DropDownListMonth.SelectedValue.ToString()); int year = int.Parse(DropDownListYear.SelectedValue.ToString()); int days = GetNumOfDays(month, year); DropDownListDay.Items.Clear(); for (int i = 1; i <= days; i++) DropDownListDay.Items.Add(i.ToString()); }

30


private int GetNumOfDays(int month, int year) { switch (month) { case 1: return 31; case 2: if (IsLeapYear(year)) return 29; else return 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; } return 0; }

31


‫)‪private bool IsLeapYear(int year‬‬ ‫{‬ ‫))‪return (((year % 4 == 0) && (year % 100 != 0‬‬ ‫;))‪|| (year % 400 == 0‬‬ ‫}‬ ‫)(‪private void ShowSelectedDate‬‬ ‫{‬ ‫"‪string strDate = DropDownListDay.Text + "/" + DropDownListMonth.Text + "/‬‬ ‫;‪+ DropDownListYear.Text‬‬ ‫;‪LabelDate.Text = "The selected date is " + strDate‬‬ ‫}‬ ‫تمرين رقم ‪3‬‬ ‫في هذه المهمة نريد التعرف على بارامترات معالجات األحداث‪ .‬يظهر في رأس معالج الحدث بارامتران وهما‬ ‫•‬

‫‪ :object sender‬هذا البرامتر عبارة عن كائن من نوع ‪ object‬ومعروف أن الفئة ‪ object‬هي الفئة التي‬ ‫ُتشتق منها كل الفئات التي نكتبها بلغة ‪ .C#‬معنى ذلك أن هذا الكائن قد يكون حين المناداة على معالج الحدث‬ ‫)‪ (At Runtime‬كائنا من نوع فئة مُستقة (أي ‪ )Derived Subclass‬وذلك بواسطة منهج تحويل أنماط‬ ‫الكائنات (‪ .)Objetct Type Casting‬التحويل الذي تم في هذه الحالة هو تحويل الى أعلى أي‬ ‫‪ . Upcasting‬وبالفعل فقد يكون هذا الكائن من نوع ‪ Button‬اذا كان مُرسل الحدث زرً ا من نوع ‪.Button‬‬ ‫أو قد يكون من نوع ‪ DropDownList‬اذا كان مُرسل الحدث من نوع ‪ DropDownList‬وهكذا ‪ً ...‬اذا هذا‬ ‫البرامتر وكما يدل على ذلك اسمه فانه يشير الى األداة التي أرسلت الحدث‪.‬‬

‫•‬

‫‪ :EventArgs e‬هذا البرامتر يحتوي على معلومات اضافية عن الحدث وما أرسله مُرسل الحدث‪ .‬شرح هذا‬ ‫البرامتر سيكون الح ًقا من خالل مثال عملي‪.‬‬

‫‪32‬‬


‫المطلوب تصميم وبرمجة آلة حاسبة متطورة نوعا ما‪ .‬التصميم المطلوب هو التالي‪:‬‬

‫الحظ أن معظم األدوات عبارة عن أزرار ألرقام‪ .‬ما الذي يجب أن يحصل عندما ينقر المُستخدم على أحد أزرار‬ ‫األرقام؟ عند النقر على أحد هذه األزرار يجب على معالج الحدث أن يضيف (عن طريق الوصل) الرقم الذي ُنقر‬ ‫عليه الى العدد الموجود في صندوق النص‪ .‬العشرة أزرار تشترك في هذه العملية (أي عملية الوصل)‪ .‬عادة علينا‬ ‫أن نكتب عشرة معالجات أحداث بحيث نكتب لكل زر من األزرار معالجا خاصا به‪ .‬لكن وبما أن كود معالجات‬ ‫األحداث سيكون متشابها جدا وللحيلولة دون اضافة ُنسخ كود متشابهة وغير ضرورية نقترح كتابة معالج حدث‬ ‫واحد اذا كان بإمكاننا أن نعرف داخل معالج الحدث الرقم الذي يجب أن يضاف الى صندوق النص‪ .‬هنا يأتي دور‬ ‫البارامتر ‪ sender‬الذي يحتوي على معلومات عن المُرسل‪ً .‬اذا نضيف للعشرة أزرار معالج حدث واحد باسم عام‬ ‫وواحد مثل ‪ .OnDigitClick‬اضافة معالج الحدث تكون بإحدى الطرق التي ناقشناها سابقا‪ .‬الصورة التالية تبين‬ ‫وضع البارامتر ‪ sender‬في الـ ‪ Debug Mode‬حين استدعاء معالج الحدث بعد أن نقر المستخدم على الرقم ‪.4‬‬

‫الحظ أن الخاصية ‪ Text‬التابعة للزر والتي تظهر في القائمة التي في الصورة تحمل القيمة ‪ 4‬وقي نفس الرقم الُراد‬ ‫اضافته الى نص الصندوق‪.‬‬ ‫)‪protected void OnDigitClick(object sender, EventArgs e‬‬ ‫{‬ ‫;‪TextBoxResult.Text += ((Button)sender).Text‬‬ ‫}‬

‫‪33‬‬


‫ لنعيده الى نمطه‬DownCast ‫ الى أسفل أي‬sender ‫انتبه الى أنه وجب علينا أن نقوم بعملية تحويل نمط الكائن‬ .‫ المطلوبة‬Text ‫ بدون القيام بعملية التحويل هذه لن يكون بامكاننا الوصول الى الخاصية‬.Button ‫األصلي وهو‬ :‫ التالي‬HTML ‫هذا المعالج مشترك لجميع أزرار األرقام كما يبين ذلك كود الـ‬ <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Untitled Page</title> <style type="text/css"> .styleTD { width: 50px; } .styleTDBig { width: 86px; } .styleButton { width: 50px; } .styleButtonBig { width: 86px; }

</style> </head>

34


<body> <form id="form1" runat="server"> <div> <table> <tr> <td> <asp:TextBox ID="TextBoxResult" runat="server" Width="258px"></asp:TextBox> </td> </tr> </table> <hr /> <table> <tr> <td class="styleTDBig"> <asp:Button CssClass="styleButtonBig" ID="ButtonBS" ForeColor="Red" runat="server" Text="BackSpace" /> </td> <td class="styleTDBig"> <asp:Button CssClass="styleButtonBig" ID="ButtonCE" ForeColor="Red" runat="server" Text="CE" /> </td> <td class="styleTDBig"> <asp:Button CssClass="styleButtonBig" ID="ButtonC" ForeColor="Red" runat="server" Text="C" /> </td> </tr> </table> <table> <tr> <td class="styleTD">

35


<asp:Button CssClass="styleButton" ID="Button7" Font-Size="Large" ForeColor="Blue" runat="server" Text="7" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button8" Font-Size="Large" ForeColor="Blue" runat="server" Text="8" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button9" Font-Size="Large" ForeColor="Blue" runat="server" Text="9" onclick="OnDigitClick" /> </td>

<td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonDiv" Font-Size="Large" ForeColor="Blue" runat="server" Text="/" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonSqrt" Font-Size="Large" ForeColor="Blue" runat="server" Text="sqrt" /> </td> </tr>

<tr> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button4" Font-Size="Large" ForeColor="Blue" runat="server" Text="4" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button5" Font-Size="Large" ForeColor="Blue" runat="server" Text="5" onclick="OnDigitClick" /> </td>

36


<td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button6" Font-Size="Large" ForeColor="Blue" runat="server" Text="6" onclick="OnDigitClick" /> </td>

<td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonMult" Font-Size="Large" ForeColor="Blue" runat="server" Text="*" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonPerc" Font-Size="Large" ForeColor="Blue" runat="server" Text="%" /> </td> </tr> <tr> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button1" Font-Size="Large" ForeColor="Blue" runat="server" Text="1" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button2" Font-Size="Large" ForeColor="Blue" runat="server" Text="2" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button3" Font-Size="Large" ForeColor="Blue" runat="server" Text="3" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonMinus" Font-Size="Large" ForeColor="Blue" runat="server" Text="-" /> </td>

37


<td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonInver" Font-Size="Large" ForeColor="Blue" runat="server" Text="1/x" /> </td> </tr> <tr> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button0" Font-Size="Large" ForeColor="Blue" runat="server" Text="0" onclick="OnDigitClick" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonPlusMinus" Font-Size="Large" ForeColor="Blue" runat="server" Text="+/-" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonPoint" Font-Size="Large" ForeColor="Blue" runat="server" Text="." /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="ButtonAdd" Font-Size="Large" ForeColor="Blue" runat="server" Text="+" /> </td> <td class="styleTD"> <asp:Button CssClass="styleButton" ID="Button1Calc" Font-Size="Large" ForeColor="Blue" runat="server" Text="=" /> </td> </tr> </table> </div> </form> </body></html>

38


‫باإلضافة إلى ذلك فأن اسم األداة المرسلة يظهر في الخاصية ‪ .((Button)sender).ID‬أي أنه بإمكاننا أن نكتب‬ ‫نفس المعالج ألكثر من أداة ومعالجة الحدث حسب األداة وذلك بالتفريق بينهم من خالل الـ ‪.ID‬‬ ‫مثال‪:‬‬ ‫•‬

‫معالجة الحدث ‪ Click‬زر النقطة‪ .‬علينا أن نفحص الـ ‪ ID‬فان كانت ‪ ButtonPoint‬نقوم بفحص العدد‬ ‫الموجود في صندوق النص وفق القواعد التالية‬ ‫ال يجوز للعدد أن يبدأ بنقطة‬

‫•‬

‫ال يجوز للعدد أن ينتهي بنقطة‪ .‬هذا الشرط ال يمكن فحصه اال عندما ينهي المستخدم ادخال العدد األول وهذا‬ ‫يحصل عندما ينقر احدى العمليات المتوفرة مثل … ‪ +, -, *,‬أو عندما ينهي المستخدم ادخال العدد الثاني‬ ‫وبعدها ينقر الزر =‬

‫•‬

‫ال يجوز للعدد أن يحتوي على أكثر من نقطة‬

‫في حال عدم تحقق واحد من هذه الشروط فإننا نتجاهل محاولة االدخال‪ .‬أي أن معالج الحدث ال يفعل شيئا‪.‬‬ ‫واليكم برمجة المعالج الجديد‪:‬‬ ‫)‪protected void OnDigitClick(object sender, EventArgs e‬‬ ‫{‬ ‫;)(‪string strID = ((Button)sender).ID.ToString‬‬ ‫)‪switch(strID‬‬ ‫‪{ case "Button0":‬‬ ‫‪case "Button1":‬‬ ‫‪case "Button2":‬‬ ‫‪case "Button3":‬‬ ‫‪case "Button4":‬‬ ‫‪case "Button5":‬‬ ‫‪case "Button6":‬‬ ‫‪case "Button7":‬‬ ‫‪case "Button8":‬‬ ‫‪case "Button9":‬‬ ‫{‬ ‫;‪TextBoxResult.Text += ((Button)sender).Text‬‬ ‫;‪break‬‬ ‫}‬ ‫‪39‬‬


case "ButtonPoint": { // If the number is empty ignore the request // the user tries to insert a point at the beginnig // or the user tries to insert more than one point if (TextBoxResult.Text.Equals(string.Empty) || (Count(".") >= 1)) break; // Ignore it TextBoxResult.Text += ((Button)sender).Text; break; } } } private int Count(string str) { int c = 0; int nIndex = TextBoxResult.Text.IndexOf(str); while (nIndex != -1) { c++; nIndex = TextBoxResult.Text.IndexOf(str, nIndex+1); } return c; } 4 ‫تمرين رقم‬ ‫ على معالج الحدث أن يحذف من العدد المُدخل آخر رقم تم‬.BackSpace ‫ للزر‬Click ‫أُكتب معالج حدث للحدث‬ string ‫ التابعة للفئة‬Substring ‫ بإمكانك االستعانة بالعملية‬.‫ادخاله‬

40


‫تمرين رقم ‪5‬‬ ‫أُكتب معالج حدث للحدث ‪ Click‬للزر ‪ sqrt‬أي حساب الجذر التربيعي لعدد ما‪ .‬بإمكانك االستعانة بالعملية ‪Sqrt‬‬ ‫التابعة لفئة الخدمة ‪.Math‬‬ ‫تمرين رقم ‪6‬‬ ‫أضف أزرارا الى اآللة الحاسبة لحساب الدوال الهندسية )‪ (Sin, Cos, Tan‬عليك كتابة معالج حدث واحد فقط‬ ‫لهذه الدوال باسم ‪ ..OnGeometricClick‬بإمكانك االستعانة بالعمليات الهندسية التابعة لفئة الخدمة ‪ .Math‬انتبه‬ ‫الى أن هذه الدوال تطلب أن تكون الزوايا المُمرة لها كبارامترات بالرادين )‪ (Radians‬وليس بالدرجات‬ ‫)‪ .(Degrees‬لتحويل الدرجات الى رادين عليك ضرب القيمة بـ ‪.Math.PI/180‬‬ ‫لمزيد من المعلومات‪:‬‬ ‫‪http://msdn.microsoft.com/en-us/library/system.math.cos.aspx‬‬

‫بإمكان المستخدم استعمال لوحة المفاتيح إلدخال األعداد‪ .‬األداة تدعم االدخال عبر لوحة المفاتيح بدون الحاجة الى‬ ‫أية برمجة اضافية‪ .‬ولكن السؤال هنا هو كيف سيكون بإمكاننا في هذه الحالة التأكد من صحة االدخال؟‬ ‫•‬

‫االمكانية األولى تكمن في استعمال ‪ .JavaScript‬والحقيقة أن برمجة آالت حاسبة وما شابهها يكون عادة‬ ‫بواسطة ‪ JavaScript‬ألننا ال نحتاج أية شئ من الخادم (أي الـ ‪ )Server‬وانما بامكانن اجراء كل الحسابات‬ ‫بحاسوب الزبون‪ .‬وكما هو معروف فان لغة ‪ُ JavaScript‬تعتبر أفضل اللغات الداعمة للبرمجة في جهة‬ ‫حاسوب الزبون‪ .‬والهدف من األمثلة هذه هو تعريف الطالب على برمجة معالجات األحداث بشكل مُيسر‪( .‬مثال‬ ‫على ذلك تجدونه في الموقع ‪.)http://forums.asp.net/t/1169582.aspx‬‬

‫•‬

‫االمكانية الثانية تكمن في استعمال ما يُسمى بأدوات التحقق من صحة االدخال أي الـ ‪Validation‬‬ ‫‪ .Controls‬وسنناقش هذه األدوات الحقا في فصل مستقل‪.‬‬

‫‪41‬‬


‫االستثناءات‬ ‫هنالك عدة أنواع من األخطاء المتعلقة بالبرامج والمواقع‪:‬‬ ‫أخطاء قواعد – ‪ :Syntax Errors‬وهي عبارة عن أخطاء سببها عدم االلتزام بقواعد اللغة‪ .‬تظهر هذه األخطاء‬ ‫خالل عملية ترجمة البرنامج أي خالل عملية الـ ‪ Compilation‬ولذلك فان الـ ‪ Compiler‬هو المسؤول عنها‪.‬‬ ‫ُتسمى هذه األخطاء أيضا ‪Compile Time Error‬‬ ‫أمثلة‪:‬‬ ‫األمر‬

‫الخطأ‬ ‫; ناقصة‬

‫‪int x‬‬

‫( ناقص‬

‫;‪if x == 5) x++‬‬

‫العملية ‪ parse‬غير معروفة‬

‫;))(‪int x = int.parse(Console.ReadLine‬‬

‫استثناءات – ‪ :Exceptions‬وهي عبارة عن أخطاء تظهر خالل عملية تنفيذ البرنامج ولذلك ُتسمى أيضا ‪Run‬‬ ‫‪ُ .Time Error‬ي عتبر هذا النوع من األخطاء من أشد األخطاء خطورة ألنه يحصل زمن تشغيل البرنامج أي عندما‬ ‫يكون البرنامج عند الزبون الذي اشتراه‪ .‬واألشد من ذلك أن االستثناء إذا حصل ولم يكن المبرمج قد عالجه فان‬ ‫البرنامج يتوقف عن العمل بشكل غير طبيعي – ‪ .Unhandled Exceptions‬أمثلة لهذه األخطاء‪:‬‬ ‫•‬

‫محاولة التقسيم على ‪ Attempted to divide by zero - 0‬وبشكل أدق ُتسمى‬ ‫‪ .DivideByZeroException‬مثال‪:‬‬ ‫;‪int x = 0‬‬ ‫;‪int y = 5 / x‬‬ ‫عند التنفيذ تظهر نافذة الخطأ التالية‪:‬‬

‫‪42‬‬


‫•‬

‫االدخال غير صالح ‪ Input string was not in a correct format‬وبشكل أدق ُتسمى‬ ‫‪ .FormatException‬معنى ذلك أن البرنامج توقع مثال أن يكون االدخال عددا ولكنه كان مثال نصا‬ ‫غير قابل للتحويل الى عدد‪ .‬مثال‪:‬‬

‫;)"‪int x = int.Parse("Me‬‬ ‫‪ Me‬ليست عددا‪ .‬عند التنفيذ تظهر نافذة الخطأ التالية‪:‬‬

‫•‬

‫محاولة للوصول الى خلية من خاليا مصفوفة برقم غير صالح أي برقم ليس من المجال المسموح به‬ ‫‪ Index was outside the bounds of the array‬وبشكل أدق ُتسمى‬ ‫‪ .IndexOutOfRangeException‬مثال‪:‬‬

‫;]‪int[] A = new int[5‬‬ ‫;‪A[5] = 2‬‬ ‫عند التنفيذ تظهر نافذة الخطأ التالية‪:‬‬

‫‪43‬‬


‫•‬

‫محاولة فتح قاعدة بيانات غير موجودة أو ال يمكن الوصول اليها ‪FileNotFoundException‬‬

‫•‬

‫أخطاء قواعد باستعالمات ‪ SQL‬كما سيبين ذلك الحقا‬

‫كيف ُيمكننا معالجة هذه األخطاء؟‬ ‫علينا احاطة المقطع الذي نتوقع أن تحدث معه أخطاء خالل تنفيذ البرنامج باألوامر ‪ try‬و‪ catch‬على النحو التالي‪:‬‬ ‫‪try‬‬ ‫{‬ ‫العمليات التي قد ال ينجح تنفيذها خالل التشغيل‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫اظهار الخطأ للمستخدم بشكل مفهوم أو انهاء عمل العملية أو ‪...‬‬ ‫أي بشكل عام معالجة االستثناء أو الخطأ‬ ‫}‬ ‫مثال‬ ‫‪try‬‬ ‫{‬ ‫;)"‪int x = int.Parse("Me‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫;"!‪LabelMessage.Text = "Error: Me is not a number‬‬ ‫}‬ ‫‪ :try‬أي حاول تنفيذ األوامر التالية‬ ‫‪ :catch‬أي التقط األخطاء التي أُرسلت أو رُميت من قبل العمليات على شكل كائن من نوع ‪ Exception‬باسم‬ ‫‪ .ex‬الحظ أن األمر )‪ catch (Exception ex‬يبدو وكأنه رأس عملية باسم ‪ catch‬تتلقى بارامتر من نوع‬ ‫‪ Exception‬باسم ‪ . ex‬والحقيقة أن هذا الكائن يتم انشاؤه وتجهيزه من قبل العملية التي حصل معها الخطأ‪ .‬يوفر‬ ‫هذا الكائن الخاصية النصية ‪ Message‬التي تحتوي على شرح مفصل عن الخطأ الذي حصل وهو يكون عادة‬ ‫النص الذي يظهر في السطر الثاني في نوافذ األخطاء آنفة الذكر‪:‬‬

‫‪44‬‬


‫الحظ أن الفئة ‪ Exception‬هي الـ ‪ Base Class‬لجميع فئات االستثناءات الموجودة في ‪ C#‬أي أن الكائن يمر‬ ‫بعملية ‪ UpCasting‬من فئته الخاصة الى الفئة ‪ .Exception‬لكن بإمكاننا أن نكتب أكثر من أمر ‪ catch‬في‬ ‫نفس السلسلة بحسب األخطاء التي نتوقها (الحقيقة أن كل عملية أو أمر يحدث أو يرمي استثناء ‪Throws an -‬‬ ‫‪ - Exception‬خالل زمن التشغيل توثق ذلك بشكل واضح)‪:‬‬ ‫‪try‬‬ ‫{‬ ‫العمليات التي قد ال ينجح تنفيذها خالل التشغيل‬ ‫}‬ ‫)‪catch (FileNotFoundException ex‬‬ ‫{‬ ‫اظهار أو معالجة الخطأ بأن الملف غير موجود‬ ‫}‬ ‫)‪catch (IndexOutOfRangeException ex‬‬ ‫{‬ ‫اظهار أو معالجة الخطأ بأن أرقام الخاليا غير صالحة‬ ‫}‬ ‫)‪catch (DivideByZeroException ex‬‬ ‫{‬ ‫اظهار أو معالجة الخطأ بأن التقسيم على ‪ 0‬غير ممكن‬ ‫}‬ ‫‪finally‬‬ ‫{‬ ‫اظهار أو معالجة الخطأ بشكل عام‬ ‫}‬ ‫هذه الطريقة ُتسهل علينا اظهار نصوص واضحة للمستخدم النهائي (‪ )End User‬بلغة بعيدة عن لغة المبرمجين‪.‬‬

‫‪45‬‬


‫يجب التنويه الى أنه بإمكان العمليات التي يكتبها المبرمج بنفسه أيضا رمي استثناءات بواسطة األمر ‪ throw‬لكننا‬ ‫سنترك دراسة هذا األمر للقارئ لعدم أهميته هنا‪ .‬الموقع التالي يوفر مادة ال بأس فيها عن الموضوع‪:‬‬

‫)‪Creating and Throwing Exceptions (C# Programming Guide‬‬ ‫‪http://msdn.microsoft.com/en-us/library/ms173163.aspx‬‬

‫‪46‬‬


‫صفحات الماستر‬ ‫‪Master Pages‬‬ ‫ال شك بأنك الحظت خالل تصفحك لمواقع الوب المختلفة أن معظم صفحات الموقع تشترك ببعض األجزاء أي‬ ‫بعض المكونات مثل‬ ‫•‬

‫ظهور صورة شعار الموقع (أو بانر الموقع) في كل هذه الصفحات وهو ما يسمى أحيانا بـ ‪Header‬‬

‫•‬

‫أو ظهور القائمة الرئيسية في كل هذه الصفحات‬

‫•‬

‫أو ظهور شريط آلخر األخبار كنصوص متحركة )‪(Marquees‬‬

‫•‬

‫أو ظهور معلومات عن أصحاب الموقع وعنوانهم في أسفل كل صفحة وهو ما يسمى أحيانا بـ ‪Footer‬‬

‫•‬

‫وما الى ذلك‬

‫الوضع الطبيعي يكون بوجوب اضافة هذه األجزاء الى كل صفحة من صفحات الموقع التي يجب أن تظهر بها هذه‬ ‫األجزاء‪ .‬ال شك بأن هذه االمكانية ال تمثل الحل الصحيح والمريح وذلك ألننا إذا أردنا على سبيل المثال أن نغير‬ ‫شيئا ما من هذه األجزاء علينا تغيير كل الصفحات التي تحتوي على هذا الجزء‪ .‬اضافة الى ذلك فإننا نضخم حجم‬ ‫الصفحات بصورة غير مقبولة‪ .‬لذلك كان ال بد لمصممي لغة ‪ ASP.NET‬أن يفكروا بتوفير حل ناجع ومريح جدا‬ ‫لهذا الطلب‪ .‬جاء الحل الذي نريد أن نناقشه هنا مع ‪ ASP.NET 2.0‬وهو الصفحات الرئيسية أو الـ‬ ‫‪.MasterPages‬‬ ‫أحد الحلول التي كانت متبعة قبل ‪ ASP.NET 2.0‬كان في استعمال االطارات أي الـ ‪ .Frames‬لكن نجد أن‬ ‫معظم المبرمجين واجهوا مشاكال كبيرة في ذلك خاصة فيما يتعلق بالمواقع الديناميكية التي تحتاج الى اعادة بناء أو‬ ‫تجديد مظهر الصفحة أي القيام بعملية ‪ .Page Refresh‬ومن أجمل ما قرأت عن رأي المبرمجين في االطارات‬ ‫ما يلي‪:‬‬

‫‪Ok, My list of suggestion for Frames.‬‬ ‫‪1. Frames are Evil‬‬ ‫‪2. The Devil created Frames‬‬ ‫‪3. If you are having a problem related to the Target, refer to item 1‬‬ ‫‪4. If you are trying to refresh data in a particular frame, refer to 1‬‬ ‫المصدر‪http://www.15seconds.com/issue/030528.htm :‬‬

‫‪47‬‬


‫الحظ أيضا أن ‪ MS Visual Web Developer‬ال يدعم االطارات أي أن التحرير الذكي ‪(IntelliSense‬‬ ‫)‪ Editing‬ال يعمل بالنسبة لإلطارات‪.‬‬ ‫ولذلك سوف أشرح هنا فقط استعمال الـ ‪.MasterPage‬‬ ‫من الممكن النظر الى الـ ‪ MasterPage‬على أنها قالبا يتم تصميمه وفق النسق المطلوب ومن ثم يُجعل أساسا‬ ‫للصفحات التي يجب أن تحتوي على ما يحتويه هذا القالب‪ .‬أي أننا سنضع جميع األجزاء المشتركة في هذا القالب‬ ‫فقط‪ .‬وعليه نكون قد تجاوزنا االشكاليات التي ُذكرت سابقا‪.‬‬ ‫كيفية استعمال الصفحات الرئيسية‬ ‫•‬

‫ستقوم بإنشاء صفحة رئيسية لتحديد كل من مظهر الصفحة ومحتواها أي ملف الـ ‪ aspx‬والسلوكيات الرئيسية‬ ‫أي الـ ‪Code Behind‬‬

‫•‬

‫عند انشاء صفحة جيدة من الصفحات التي يجب أن تحتوي على ما تحتويه صفحة الـ ‪ MasterPage‬نقوم‬ ‫بتحديد الـ ‪" MasterPage‬وربطها" بها كجزء من عملية اضافة الصفحة الجديدة كما سنين ذلك الحقا‪.‬‬

‫خطوات إنشاء صفحة الـ ‪MasterPage‬‬ ‫•‬

‫أنقر على الزر األيمن للفارة في الـ ‪ Solution Explorer‬بعد أن تضع الفارة على اسم المشروع‬

‫•‬

‫اختر من القائمة التي تظهر األمر …‪Add New Item‬‬

‫•‬

‫اختر في نافذة الحوار التي تظهر النوع ‪ Master Page‬وأعط للملف اسما‪ .‬بإمكانك أخذ االسم الذي يقترحه‬ ‫الـ ‪ Web Developer‬اال إذا علمت أنك ستستعمل أكثر من ‪ .MasterPages‬حينها يُفضّل اعطاء اسم‬ ‫مميز‪ .‬الحظ أن ملحق الملف ليس ‪ aspx‬وانما ‪.master‬‬

‫•‬

‫اغلق النافذة بالضغط على الزر ‪Add‬‬

‫الصورة التالية تظهر نافذة الحوار المذكورة أعاله‬

‫‪48‬‬


:‫ وهي على النحو التالي‬MasterPage ‫ الى صفحة التنسيق التابعة للـ‬Web Developer ‫بعد ذلك سينقلك الـ‬ <%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Untitled Page</title> <asp:ContentPlaceHolder id="head" runat="server"> </asp:ContentPlaceHolder> </head> <body> <form id="form1" runat="server"> <div> <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server"> </asp:ContentPlaceHolder>

49


‫>‪</div‬‬ ‫>‪</form‬‬ ‫>‪</body‬‬ ‫>‪</html‬‬ ‫ًإذا الـ ‪ MasterPage‬التي أمامنا اآلن هي الصفحة التي نقوم فيها بتصميم الجزء الذى نريد ان نكرره في أكثر‬ ‫من صفحه في الموقع‪ .‬السطر األول في هذه الصفحة يحتوي على الموجه (‪ (Master Directive‬للتدليل على أن‬ ‫هذه الصفحة ليست صفحة عادية وإنما صفحة ‪ .MasterPage‬يحتوي هذا السطر أيضا على معلومات عن اسم‬ ‫الفئة التي تمثل صفحة الـ ‪ MasterPage‬في الـ ‪ Code Behind‬من خالل الخاصية ‪ Inherits‬وأيضا‬ ‫معلومات عن اسم الفئة التي تحتوي على الـ ‪ Code Behind‬من خالل الخاصية ‪( CodeFile‬هذا إذا طلبت أن‬ ‫تفصل الكود عن التنسيق عند إنشائك للصفحات المختلفة ‪.) Place code in separate file‬‬ ‫هنالك أداة جديدة داخل صفحة الـ ‪ MasterPage‬وهي األداة ‪ .asp:ContentPlaceHolder‬تسمى هذه‬ ‫األداة بأداة النائب )‪ (PlaceHolder‬الخاص بالمحتوى )‪ .(Content‬تشير أداة النائب الخاصة بالمحتوى إلى‬ ‫مناطق محتوى قابلة لالستبدال على الصفحة الرئيسية من قبل الصفحات التي تربط الحقا بصفحة الـ‬ ‫‪ .MasterPage‬هذه الصفحات تسمى أيضا ‪ Content Page‬وسميت بهذا االسم ألنها ستحتوي على الجزء‬ ‫الخاص بكل صفحة‪ .‬الحظ أنه بإمكاننا اضافة عدة أدوات من هذا النوع بحسب ما يتطلبه تنسيق الموقع‪.‬‬ ‫الكلمة ‪ Inherits‬تذكرنا بمبدأ الوراثة (أي ‪ )Inheritance‬والذي يُعتبر من أهم آليات البرمجة موجهة الكائنات‪.‬‬ ‫هذه اآللية تسمح لنا بإنشاء عالقة مشاركة ببعض خصائص وسلوكيات الفئات‪ .‬فالفئة التي ترث من فئة أخرى فإنها‬ ‫تحصل على كل الخصائص والعمليات (أي السلوكيات) التي تسمح بها الفئة المُوّ رثة (أي التي تكون ‪ public‬أو‬ ‫‪ )protected‬أي تشاركها في هذه الخصائص والعمليات‪ .‬وهذا بالضبط ما يحصل هنا فان الـ ‪Content Page‬‬ ‫ترث كل ما تسمح به الـ ‪ Master Page‬من تنسيقات وما إلى ذلك‪ .‬بعد ذلك تقوم الـ ‪ Content Page‬بإضافة‬ ‫المحتوى الخاص بها أي تقوم بعملية تخصيص (أي ‪ .)Specialization‬وكل ما هو موجود في الـ ‪Master‬‬ ‫‪ Page‬يكون عاما أي أنها تقوم بعملية تعميم (أي ‪ .)Generalization‬هذا التصرف يعتبر أيضا من مميزات‬ ‫مبدأ الوراثة‪ ،‬فكلما سرنا في مُخطط الوراثة (مثال في ‪ )UML Diagram‬باتجاه الفئات المورثة نجد خصائص‬ ‫وعمليات عامة بينما إذا سرنا باتجاه الفئات الوارثة فإننا نجد خصائص وعمليات خاصة‪.‬‬ ‫مهمة برمجية‪:‬‬ ‫دعونا اآلن نصمم موقعا فيه معلومات عن لغة ‪ .ASP.NET‬الموقع سيكون مؤلفا من‬ ‫•‬

‫صفحة ‪MasterPage‬‬

‫•‬

‫‪ 4‬صفحات وهي‪:‬‬

‫‪50‬‬


‫المحتوى‬

‫اسم الصفحة‬ ‫‪Default.aspx‬‬

‫الصفحة األولى وتحتوي جملتين توضيحيتين‬

‫‪ASPNetVersions.aspx‬‬

‫يحتوي على معلومات عن النسخ المختلفة للغة ‪ASP.NET‬‬

‫‪MSWebDeveloper.aspx‬‬

‫تحتوي على صورة خاصة بالـ ‪ MS Web Developer‬مع‬ ‫ارتباط تشعبي على هذه الصورة لموقع تنزيل البرنامج‪:‬‬ ‫‪http://www.microsoft.com/express/vwd/‬‬ ‫قائمة باسماء المواقع المفضلة المتعلقة بتعلم لغة ‪ASP.NET‬‬

‫‪UsefullLinks.aspx‬‬

‫تصميم صفحة الـ ‪ MasterPage‬سيكون كالتالي‪:‬‬ ‫الـ ‪ Header‬وفيه شعار الموقع‬ ‫القائمة الرئيسية – ‪ Menu Control‬وفيه قائمة لكل صفحة من الصفحات األربع‬ ‫محتوى خاص بكل صفحة من الصفحات األربع المذكورة أعاله‬

‫آخر األخبار‬

‫‪asp:ContentPlaceHolder‬‬ ‫الـ ‪ Footer‬وفيه عناوين‬

‫الجزء الوحيد الذي سيكون محتواه مختلفا من صفحة الى أخرى هو الجزء األوسط ولذك سنضع له األداة‬ ‫‪ asp:ContentPlaceHolder‬حتى تتمكن كل صفحة من الصفحات األربع بوضع محتواها الخاص بداخله‪.‬‬ ‫سوف أستعمل الوسم ‪ table‬للحصول على التصميم النهائي‬ ‫برمجة الجزء المسئول عن عرض آخر األخبار‬ ‫استعملت لهذا الغرض األداة ‪ asp:Literal‬ألنني سوف أُحدد محتوى هذا الجزء في الـ ‪ .Code Behind‬وذلك‬ ‫ألن هذا المحتوى أي آخر األخبار والتي تكون عادة عبارة عن نصوص تشعبيه (أي ‪ Anchor‬أو ‪)HyperLink‬‬ ‫ومتحركة (أي ‪ )Marquee‬يُقرأ من قاعدة البيانات‪ .‬أضفت الكود التالي إلى الملف ‪Master.master‬‬

‫>"‪<td align="left" valign="top" width="200px" height="400px‬‬ ‫>‪<asp:Literal ID="LiteralLastNews" runat="server"></asp:Literal‬‬ ‫>‪</td‬‬ ‫وأضفت الكود التالي إلى الملف ‪Master.master.cs‬‬

‫‪51‬‬


protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { try { string strMarquee = "<font size=\"3\" face=\"Arial\"> "; strMarquee += "<marquee direction=\"up\" behaviour=\"scroll\" "; strMarquee += "scrolldelay=\"85\" SCROLLAMOUNT=\"3\""; strMarquee += "onMouseOver=\"this.stop()\" onMouseOut=\"this.start()\">"; strMarquee += "<a target=\"_blank\" href=http://www.asp.net>"; strMarquee += "‫ منتديات‬ASP.NET"; strMarquee += "</a>"; strMarquee += "</marquee> </font>"; LiteralLastNews.Text = strMarquee; } catch (Exception ex) { LiteralLastNews.Text = ex.Message.ToString(); }}} ‫ بالضبط بنفس الطريقة كما هو األمر‬Master.master.cs ‫الحظ أنه بإمكانك كتابة معالجات أحداث في الملف‬ ."‫مع الملفات "العادية‬ MasterPage ‫خطوات إنشاء صفحة إضافية مؤسسة على الـ‬ ‫ بعد أن تضع الفارة على اسم المشروع‬Solution Explorer ‫أنقر على الزر األيمن للفارة في الـ‬

Add New Item… ‫اختر من القائمة التي تظهر األمر‬

Default.aspx ‫ االسم هنا هو‬.‫ وأعط للملف اسما‬Web Form ‫اختر في نافذة الحوار التي تظهر النوع‬

‫ بحيث يُصبح‬Select master page ‫أنقر صندوق االختيار الذي يظهر في أسفل نافذة الحوار والمسمى‬

‫مختارا‬ Add ‫اغلق النافذة بالضغط على الزر‬

52


‫الصورة التالية تظهر نافذة الحوار المذكورة أعاله‬ ‫بعد ذلك سينقلك الـ ‪ Web Developer‬إلى نافذة حوار الختيار الـ ‪ MasterPage‬التي تريد‪ .‬هذا يدل على أنه‬ ‫وكما ُذكر آنفا أنه بإمكانك تصميم أكثر من ‪ MasterPage‬في نفس الموقع‪ .‬ولذلك كان ال بد من سؤالك عن الـ‬

‫‪ MasterPage‬التي تريد‪ .‬وهي على النحو التالي‪:‬‬

‫اختر اسم الـ ‪ MasterPage‬التي تظهر في القسم األيمن من نافذة الحوار في القائمة المسماة ‪Contents of‬‬ ‫‪ .folder‬ثم قم باغالق نافذة الحوار من خالل النقر على الزر المسمى ‪.OK‬‬

‫‪53‬‬


‫بعد ذلك سينقلك الـ ‪ Web Developer‬الى صفحة التنسيق التابعة للـصفحة ‪ Default.aspx‬وهي على النحو‬ ‫التالي‪:‬‬ ‫"‪<%@ Page Language="C#" MasterPageFile="~/MasterPage.master‬‬ ‫"‪AutoEventWireup="true‬‬ ‫>‪CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %‬‬ ‫>"‪<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server‬‬ ‫>‪</asp:Content‬‬ ‫"‪<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1‬‬ ‫>"‪Runat="Server‬‬ ‫>‪</asp:Content‬‬ ‫السطر األول في هذه الصفحة يحتوي على الموجه (‪ . (Page Directive‬هذا الموجه يحتوي على الخاصية‬ ‫‪ MasterPageFile‬التي تأخذ كقيمة اسم الملف الذي يحتوي على الـ ‪.MasterPage‬‬ ‫الحظ أن الـ ‪ Web Developer‬قد قام باستبدال أدوات الـ ‪ asp:ContenPlaceHolder‬الموجودة في الـ‬ ‫‪ MasterPage‬بأدوات جديدة من نوع ‪ .asp:Content‬هذه األدوات هي التي تمكنك من اضافة المحتوى‬ ‫الخاص الى صفحة الـ ‪ MasterPage‬بحيث يظهر الشكل النهائي المطلوب بعد القيام بعملية دمج الصفحتين معا‬ ‫وذلك في زمن طلب الصفحة أي ‪ .At RunTime‬هنا سوف نكتب بين وسم البداية ‪ asp:Content‬المسمى‬ ‫‪ ContenPlaceHolder1‬ووسم النهاية >‪ </asp:Content‬المحتوى الذي نريد‪ .‬يجوز لهذا المحتوى أن‬ ‫يكون ما نريد من أدوات اضافية ووسوم وما الى ذلك باستثناء الوسم ‪ form‬فانه موجود في الـ ‪MasterPage‬‬ ‫وال يجوز أن يكون في الصفحة الواحدة أكثر من ‪ form‬واحد‪.‬‬ ‫في هذا المثال أضفنا سطرين بنسق >‪<h1‬‬

‫"‪<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1‬‬ ‫>"‪Runat="Server‬‬ ‫>‪<h1>This is the default page</h1‬‬ ‫>‪<h1>of the my ASP.NET Wbe Site</h1‬‬ ‫>‪</asp:Content‬‬ ‫بعد ذلك بإمكانكم تكرار نفس العمليات إلضافة باقي الصفحات‪.‬‬

‫‪54‬‬


‫واآلن علينا ربط عناصر القائمة الرئيسية بالصفحات التي يجب أن يُذهب اليها حين النقر على هذه العناصر وذلك‬ ‫يكون على سبيل المثال من خالل اعطاء الخاصية ‪ NavigateUrl‬التابعة لألداة ‪ asp:MenuItem‬كقيمة اسم‬ ‫الصفحة الكامل أي مع الملحق ‪ .aspx‬تحديد الـ ‪ NavigateUrl‬لكل عنصر يكون في نافذة تحرير عناصر القائمة‬ ‫)‪ (Menu Item Editor‬التي تظهر في النافذة التالية‪:‬‬

‫الحظ أنه بإمكانك اختيار اسم الصفحة من خالل النقر على الزر‬

‫الذي يظهر الى يمين الخاصية‬

‫‪ . NavigateUrl‬بعد النقر عليه ستظهر نافذة حوار تحتوي على جميع الصفحات الموجودة في الموقع كما تبين‬ ‫الصورة التالية‪:‬‬

‫‪55‬‬


‫استدعاء هذه النافذة يكون من خالل الضغط على السهم الذي يظهر في أعلى األداة في عرض الـ ‪ Design‬ومن ثم‬ ‫النقر على االرتباط ‪ Edit Menu Items‬كما يظهر في الصورة التالية‪:‬‬

‫‪56‬‬


‫الشكل النهائي للموقع عند عرض الصفحة األولى أي صفحة الـ ‪ Default.aspx‬هي كالتالي‪:‬‬

‫أعطيت الخاصية ‪ border‬التابعة لوسم الجدول أي ‪ table‬القيمة ‪ 1‬إلظهار األجزاء المشتركة بين الصفحات‪.‬‬ ‫إذا نقرنا بالفارة على عنصر القائمة ‪ ASP.NET Versions‬حينها ُتستدعى الصفحة‬ ‫‪ ASPNetVersions.apsx‬ويتم دمجها مع صفحة الـ ‪ MasterPage‬لتكون النتيجة النهائية كالتالي‪:‬‬

‫‪57‬‬


‫الحظ أن الفرق الوحيد بين الصورتين األخيرتين (وكذلك األمر بالنسبة للصورتين التاليتين) هو في الخلية الوسطى‬ ‫من الجهة اليمنى‪ ،‬أي الخلية التي تحتوي على األداة ‪ .asp:ContenPlaceHolder‬وهكذا بالنسبة لباقي‬ ‫الصفحات‪.‬‬ ‫صفحة ‪MSWebDeveloper.aspx‬‬

‫صفحة ‪UsefullLinks.aspx‬‬

‫‪58‬‬


‫فعالية برمجية‪:‬‬ ‫المطلوب تصميم موقع فيه معلومات شخصية عنك‪ .‬الموقع يجب أن يكون مؤلفا على األقل من‬ ‫•‬

‫صفحة ‪ MasterPage‬وتحتوي على صورة الشعار وقائمة رئيسية و ‪ Footer‬وآخر األخبار‪ .‬حاول أن‬ ‫تستعمل تقسيما مختلفا عن الذي في المثال أعاله‪.‬‬

‫•‬

‫‪ 4‬صفحات وهي‪:‬‬ ‫المحتوى‬

‫اسم الصفحة‬ ‫‪Default.aspx‬‬

‫االسم‪ ،‬العنوان‪ ،‬ترحيب ‪...‬اجعلها الصفحة األولى‬

‫‪MyCV.aspx‬‬

‫السيرة الذاتية وخاصة المدرسية والتخصص‬ ‫والمستقبل العلمي‪... ،‬‬

‫‪MyFavorites.aspx‬‬

‫قائمة باسماء المواقع المفضلة لديك مقسمة الى أصناف‬ ‫مثل تعليمية أو هوايات‪... ،‬‬

‫‪Imaegs.aspx‬‬

‫صور مختلفة‬

‫مالحظة عن كيفية جعل صفحة الـ ‪ Default‬الصفحة األولى‬ ‫ذكر سابقا بأن المواقع تتكون من عدة صفحات‪ .‬لذلك أصبح ال بد من تحديد الصفحة التي يجب أن تظهر عندما‬ ‫يطلب المستخدم الموقع‪ .‬الحظ أن المستخدم ال يكتب عادة اسم ملف معين في نهاية العنوان‪ .‬مع ذلك يُحضر‬ ‫المُستكشف الصفحة التي حُدد لها أن تكون األولى‪ .‬وهي عادة الصفحة ‪ .Default.aspx‬واليكم الخطوات الالزمة‬ ‫لجعل صفحة الـ ‪ Default‬الصفحة األولى‪:‬‬ ‫•‬

‫اختر من القائمة الرئيسية للـ ‪ Web Developer‬القائمة ‪ Website‬ثم األمر …‪Start Options‬‬

‫‪59‬‬


‫•‬

‫اختر في نافذة الحوار التي تظهر اإلمكانية ‪Specific page‬‬

‫•‬

‫أنقر عل الزر‬

‫•‬

‫أختر في نافذة الحوار التي تظهر بعده الملف الذي تريد أن تجعله الصفحة األولى‬

‫•‬

‫أغلق نافذة الحوار بالنقر على الزر المُسمى ‪OK‬‬

‫الموجود في نهاية السطر‬

‫‪60‬‬


‫‪Menu Control‬‬ ‫تعتبر اداة القائمة من اهم االدوات التي تمكن المستخدم من التنقل بين صفحات الموقع والوصول الى العمليات‬ ‫المختلفة التي يوفرها الموقع‪ .‬تتبع هذه االداة لمجموعة ادوات التنقل (االبحار) او ما يسمى بـ ‪Navigation‬‬ ‫‪.Controls‬‬ ‫مبنى هذه االداة تراجعي ‪ Recursive Structure‬بمعنى ان كل عنصر من عناصرها )‪ (Menu Item‬قد‬ ‫يكون بحد ذاته قائمة وهكذا نحصل على قائمة مكونة من قوائم‪.‬‬

‫المشترك بين هذه العناصر ان المستخدم ينقر على عناصرها نقرة واحدة ‪ Single Click Event‬ليفتح القائمة‬ ‫الفرعية او لينفذ االمر (أي فتح صفحة جديدة وما الى ذلك) ولذلك تشترك جميع عناصر القائمة بنفس معالج الحدث‬ ‫أي اننا نكتب معالج حدث واحد لجميع هذه العناصر والحدث هو ‪ . MenuItemClick‬في كثير من االحيان لسنا‬ ‫بحاجة الى كتابة معالج هذا الحدث ألنه بإمكاننا ان نحدد لكل عنصر من عناصر القائمة الصفحة التي يجب ان‬ ‫يتفرع اليها حين النقر عليه وذلك من خالل اعطاء الخاصية ‪ NavigateUrl‬كقيمة اسم الصفحة المطلوب التفرع‬ ‫اليها‪ .‬تحديد الـ ‪ NavigateUrl‬لكل عنصر يكون في نافذة تحرير عناصر القائمة )‪ (Menu Item Editor‬التي‬ ‫تظهر‬

‫في‬

‫التالية‪:‬‬

‫النافذة‬

‫‪61‬‬


‫استدعاء هذه النافذة يكون من خالل الضغط على السهم الذي يظهر في أعلى األداة في عرض الـ ‪ Design‬ومن ثم‬ ‫النقر على االرتباط ‪ Edit Menu Items‬كما يظهر في الصورة التالية‪:‬‬

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

‫صفحة الـ ‪ MasterPage‬تحتوي على االشياء المشتركة بين الصفحات المختلفة واهمها الصورة الرئيسية‬ ‫)‪ (Header/Banner‬والقائمة الرئيسية‪ .‬هذه القائمة يجب أن تحتوي على العنصر ‪ Login‬الذي اذا ما ُنقر عليه‬ ‫تظهر صفحة الدخول أي الـ ‪ .Login.aspx‬بعد ان يدخل المستخدم بنجاح نريد ان نغير اسم هذا العنصر الى‬ ‫‪ Logout‬بحيث اذا نقر المستخدم عليه تتم عملية ‪ Logout‬كاملة‪ .‬أي أن عنصر القائمة ‪ Login‬سوف يلعب‬

‫‪62‬‬


‫دورين أو يوفر امكانيتين وهما امكانية الدخول وامكانية الخروج‪ .‬الحظ أيضا أن اسم األداة يتغير بحسب وضع‬ ‫المستخدم هل هو ‪ Logged In‬أم ‪.Logged Out‬‬

‫من اجل ذلك علينا ان نكتب معالج الحدث ‪ MenuItemClick‬لنعالج به هذه الطلبات والتغييرات‪.‬‬ ‫برمجة تصرف عنصر القائمة ‪:Login‬‬ ‫كما ُذكر سابقا‪ ،‬على هذا العنصر أن يوفر عمليتين وهما ‪ Login‬و ‪ .Logout‬في البداية يكون نصه (أي الخاصية‬ ‫‪ Text‬التابعة لهذا العنصر) ‪ .Login‬ولتكن قيمته (أي الخاصية ‪ .LogInOut )Value‬بعد دخول المستخدم‬ ‫بنجاح يجب تغيير نص هذا العنصر إلى ‪ .Logout‬كل هذا يحصل في معالج الحدث ‪ Page_Load‬التابع للـ‬ ‫‪ .MasterPage‬على النحو التالي‪:‬‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫{‬ ‫‪try‬‬ ‫{‬ ‫)]"‪if ((bool)Session["ValidUser‬‬ ‫;)"‪RenameMenuItem("Logout‬‬ ‫‪else‬‬ ‫;)"‪RenameMenuItem("Login‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫}‬ ‫}‬ ‫أي أن األمر يتعلق بقيمة ‪ ValidUser‬داخل الكائن ‪ .Session‬فان كانت قيمته ‪ true‬فان ذلك يعني أن المستخدم‬ ‫داخل بنجاح )‪ (Logged In‬وعلينا أن نوفر له إمكانية الخروج‪ .‬لذلك علينا أن نغير نص العنصر إلى ‪.Logout‬‬ ‫خالف ذلك يبقى النص ‪.Login‬‬

‫‪63‬‬


‫العملية ‪ RenameMenuItem‬يجب أن نكتبها داخل فئة الـ ‪MasterPage‬على النحو التالي‪:‬‬

‫)‪private void RenameMenuItem(string strText‬‬ ‫{‬ ‫;)"‪Menu mainmenu = (Menu)FindControl("Menu1‬‬ ‫)‪if (mainmenu != null‬‬ ‫{‬ ‫;)"‪MenuItem menuToRename = mainmenu.FindItem("LogInOut‬‬ ‫)‪if (menuToRename != null && menuToRename.Parent == null‬‬ ‫{‬ ‫;‪menuToRename.Text = strText‬‬ ‫}‬ ‫}‬ ‫}‬ ‫الدالة ‪ FindControl‬تأخذ كبارا متر اسم أداة موجودة ضمن نموذج معين (أي ‪ )Form‬وتعيد لنا كائنا من نوع‬ ‫هذه األداة إن وجدتها‪ .‬وإال تعيد ‪ .null‬في حالتنا نحن نبحث عن أداة من نوع ‪ Menu‬باسم ‪ .Menu1‬إذا تم العثور‬ ‫على القائمة نبحث حينها داخل هذه القائمة عن العنصر المسمى ‪ LogInOut‬بواسطة الدالة ‪ FindItem‬التابعة‬ ‫للفئة ‪ .Menu‬فان وجدناه (أي كانت قيمة الكائن المرجع ال تساوي ‪ )null‬وكان عنصرا رئيسيا أي ال ‪ Parent‬له‬ ‫نقوم حينها بتغيير نصه إلى النص المُمرر كبارا متر إلى العملية ‪.RenameMenuItem‬‬ ‫بقي علينا أن نقوم ببرمجة تصرف العنصر حين النقر عليه من قبل المستخدم‪ .‬هذا يحصل في معالج الحدث‬ ‫‪ MenuItemClick‬التابع للقائمة ‪ Menu1‬على الشكل التالي‪:‬‬

‫)‪protected void Menu1_MenuItemClick(object sender, MenuEventArgs e‬‬ ‫{‬ ‫))"‪if (e.Item.Value.Equals("LogInOut‬‬ ‫{‬ ‫))"‪if (e.Item.Text.Equals("Login‬‬ ‫;)"‪Response.Redirect("Login.aspx‬‬ ‫‪else‬‬

‫‪64‬‬


‫{‬ ‫;‪Session["ValidUser"] = false‬‬ ‫;)"‪RenameMenuItem("Login‬‬ ‫;)"‪Response.Redirect("Default.aspx‬‬ ‫}‬ ‫}‬ ‫}‬ ‫التصرف يكون كالتالي‪:‬‬ ‫إذا كان نص العنصر ‪ Login‬يتم تحويل المستخدم إلى الصفحة ‪ Login.aspx‬ليقوم بعملية الدخول كما يجب‪.‬‬ ‫خالف ذلك يعني أن النص هو ‪ Logout‬ولذلك تتم عملية إعطاء ‪ ValidUser‬القيمة ‪ false‬ومن ثم تغيير النص‬ ‫إلى ‪ Login‬وتحويل المستخدم إلى الصفحة ‪ .Default.aspx‬الحظ هنا أننا استخدمنا البارامتر الثاني من‬ ‫بارامترات معالج الحدث وهو البارامتر ‪ e‬من نوع ‪ .MenuEventArgs‬اصورة التالية تظهر مبنى هذا الكائن‬ ‫والمعلومات التي يزودنا بها عن عنصر القائمة الذي تم النقر عليه‪:‬‬

‫تمارين‬ ‫تمرين رقم ‪1‬‬ ‫عالج حدث النقر على باقي عناصر القائمة وأنقل المستخدم في كل مرة الى الصفحة المالئمة‪ .‬أي اذا نقر المستخدم‬ ‫على العنصر ‪ Links‬تظهر صفحة ‪ Default.aspx‬واذا نقر المستخدم على العنصر ‪ Home‬تظهر صفحة‬ ‫‪ UsefullLinks.aspx‬وهكذا‪.‬‬

‫‪65‬‬


‫‪Calendar Control‬‬ ‫توفر لنا هذه األداة رزنامة بتنسيقات مختلفة تمكن المستخدم من اختيار تاريخ معين ودون الحاجة لكتابة أي شئ‪.‬‬ ‫عدم الحاجة لكتابة التاريخ يعني أن المستخدم ال يستطيع إدخال قيم غير صالحة ألن تكون تاريخا معينا‪ .‬يوجد لهذه‬ ‫األداة مجموعة كبيرة من الخصائص واألحداث‪.‬‬

‫كود الـ ‪HTML‬‬ ‫>‪<asp:Calendar ID="MyCalendar"runat="server"></asp:Calendar‬‬ ‫الصورة التالية تبين مجموعة األحداث التابعة ألداة الرزنامة‪:‬‬

‫‪66‬‬


‫في هذا الفصل نريد التعرف على بعض هذه األحداث‪.‬‬ ‫الحدث‬

‫الشرح‬

‫‪DayRender‬‬

‫يحصل هذا الحدث كلما ُتنشئ خلية جديدة من خاليا‬ ‫الرزنامة أي كلما يُنشئ يوم من أيام الرزنامة‪ .‬نستطيع‬ ‫أن نعالج هذا الحدث إن أردنا أن ننسق الخلية بنسق‬ ‫خاص )‪ (Cell Formatting‬أو مثال إلغاء إمكانية‬ ‫اختيار أيام معينة أو تغيير محتوى الخاليا كما سيأتي‬ ‫في األمثلة التالية ‪.‬‬

‫‪SelectionChanged‬‬

‫يحصل هذا الحدث كلما غير المستخدم التاريخ المُختار‬ ‫بواسطة الفارة‪.‬‬ ‫يحصل هذا الحدث كلما غير المستخدم الشهر لذي‬

‫‪VisibleMonthChanged‬‬

‫يظهر حاليا‪ .‬الحظ أن بامكان المستخدم الذهاب الى‬ ‫الشهر السابق أو الى الشهر الالحق‪ .‬وذلك من خالل‬ ‫النقر على االرتباط‬

‫أو االرتباط‬

‫يظهران في الجهة العليا من الرزنامة‪.‬‬

‫‪67‬‬

‫الذين‬


2 ‫تمرين رقم‬ ‫ من أجل هذا الغرض‬.‫يريد أصحاب مطعم ما تمكين زبائنهم من حجز أماكن في المطعم بواسطة الموقع الخاص بهم‬ .‫ المطلوب إظهار أن المطعم مغلق أيام الجمعة وبالتالي ال يمكن اختيار هذا اليوم‬.‫نريد استعمال أداة الرزنامة‬ ‫باإلضافة إلى ذلك يجب كتابة اسم العيد بجانب التاريخ في كل يوم يكون فيه عيد (على سبيل المثال عيد الفطر‬ ‫ أي يجب أن تظهر الرزنامة في شهر سبتمبر‬.)22.09.09 ‫ ولغاية‬20.09.09 ‫سيكون على األرجح من تاريخ‬ :‫بالشكل التالي‬

protected void MyCalendar_DayRender(object sender, DayRenderEventArgs e) {

// Add custom text to cell in the Calendar control. if (e.Day.Date.Month == 9 && e.Day.Date.Year == 2009 && e.Day.Date.Day >= 20 && e.Day.Date.Day <= 22) e.Cell.Controls.Add(new LiteralControl("<br />‫;))"الفطر عيد‬ // Change the background color of each friday days // in the month to gray and disable it. if (e.Day.Date.DayOfWeek == DayOfWeek.Friday) { e.Cell.BackColor = System.Drawing.Color.Gray; e.Cell.Controls.Add(new LiteralControl("<br />‫;))"مغلق‬ }

}

68


‫شرح معالج الحدث ‪DayRender‬‬ ‫المهم في معالج هذا الحدث البارمتر الثاني ‪ e‬الذي هو عبارة عن كائن من الفئة ‪.DayRenderEventArgs‬‬ ‫الصورة التالية تبين أن لهذا البارامتر عدة مكونات أو خصائص أهمها بالنسبة لنا في هذا المثال‬

‫الخاصية ‪ Cell‬والخاصية ‪:Day‬‬ ‫الخاصية ‪ Day‬تحتوي على الخاصية ‪ Date‬وهي عبارة عن كائن يحتوي بداخله على تاريخ الخلية‪ .‬وتاريخ الخلية‬ ‫يحتوي على الخاصية ‪ Month‬التي تعيد الشهر بالنسبة لهذا التاريخ‪ .‬وهذا بالضبط ما نفعله بمساعدة األمر ‪ if‬الثاني‬ ‫فان كان الشهر الحالي شهر سبتمبر أي ‪ 9‬وكانت السنة ‪ 2009‬حينها نقوم بإضافة أداة جديدة من نوع‬ ‫‪ LabelControl‬إلى مجموعة األدوات ‪ Controls‬التابعة للخلية أي للخاصية ‪ Cell‬الموجودة في البرامتر ‪.e‬‬ ‫إضافة األداة الجديدة تكون بمساعدة العملية ‪ Add‬التابعة للكائن ‪ .Controls‬الحظ أن الباني ‪LabelControl‬‬ ‫يأخذ كبارا متر نصا يحتوي على كود ‪ HTML‬يُعطى للخاصية ‪ Text‬التابعة لهذه األداة بحيث أن المستكشف ينفذ‬ ‫أوامر الـ ‪ HTML‬هذه حين عرض وإظهار الخلية في الرزنامة‪.‬‬ ‫كذلك األمر بالنسبة أليام الجمعة حيث يكون المطعم مغلقا‪ .‬الخاصية ‪ DayOfWeek‬التابعة للخاصية ‪Date‬‬ ‫الموجودة في الخاصية ‪ Day‬التابعة للبارامتر‪ e‬هي من نوع ‪ enum‬أي ترتيب رقمي ألسماء معينة‪ .‬األسماء هنا‬ ‫هي أسماء أيام األسبوع كما تظهر الصورة التالية المأخوذة خالل تنفيذ البرنامج‬

‫‪69‬‬


‫شرح معالج الحدث ‪SelectionChanged‬‬ ‫قلنا سابقا بأن هذا الحدث يحصل كلما غير المستخدم التاريخ المُختار بواسطة الفارة‪ .‬في هذا المثال تكون ردة فعل‬ ‫الموقع أن يأخذ التاريخ المُختار ويعرضه بواسطة أداة ‪ Label‬ونسق ‪.h1‬‬ ‫الحظ أن التاريخ المُختار يكون حينها محفوظا في الخاصية ‪ SelectedDate‬التابعة ألداة الرزنامة‪.‬‬

‫)‪protected void MyCalendar_SelectionChanged(object sender, EventArgs e‬‬ ‫{‬ ‫;" ‪LabelSelectedDate.Text = "<h1>The selected date is‬‬ ‫;)(‪LabelSelectedDate.Text += MyCalendar.SelectedDate.ToString‬‬ ‫;">‪LabelSelectedDate.Text += "</h1‬‬ ‫}‬ ‫الصورة التالية تظهر نتيجة اختيار العاشر من سبتمبر كتاريخ‪:‬‬

‫تمرين رقم ‪3‬‬ ‫عالج الحدث ‪ VisibleMonthChanged‬بحيث تظهر للمستخدم‬ ‫•‬

‫النص تقدمت شهرا الى األمام كلما انتقل المستخدم الى الشهر التالي‬

‫•‬

‫أو النص رجعت شهرا الى الوراء كلما انتقل المستخدم الى الشهر السابق‬

‫استعمل أداة ‪ Label‬لهذا النص الناسب‪.‬‬ ‫الحظ أنه بإمكانك معرفة اتجاه التغيير من خالل المقارنة التالية‪:‬‬

‫‪70‬‬


if (e.NewDate.Month > e.PreviousDate.Month) { LabelMessage.Text = "You moved forward one month."; } else { LabelMessage.Text = "You moved backwards one month."; }

71


‫البرمجة عديمة الحالة‬ ‫‪Stateless Programming‬‬ ‫الخاصية ‪IsPostBack‬‬ ‫في كثير من األحيان نريد أن ننفذ بعض األوامر مرة واحدة فقط وبالذات عند تحميل الصفحة للمرة األولى خالل‬ ‫الجلسة الحالية‪ .‬المكان المناسب لفعل ذلك معالج الحدث ‪ Load‬التابع للصفحة أي االجراء ‪ .Page_Load‬لنأخذ‬ ‫على سبيل المثال الصفحة التالية‪:‬‬

‫وهي مكونة باألساس من قائمة منسدلة (أي ‪ )DropDownList‬الختيار الشهر‪ ،‬وزر إلظهار الشهر باسمه وليس‬ ‫برقمه‪ .‬اذا نقر المستخدم على هذا الزر يظهر اسم الشهر كما تبين ذلك الصفحة التالية‪:‬‬

‫‪72‬‬


‫الحظ ماذا حصل لمحتوى القائمة المنسدلة بعد النقر على الزر ‪:Show Selected Month‬‬

‫‪73‬‬


‫ يحتوي على العملية‬Page_Load ‫ الذي حصل هو أن المعالج‬.‫أي أن محتوى القائمة تضاعف دون الحاجة لذلك‬ ‫) جديدة للصفحة‬Loading ‫ التي تقوم بدورها بتعبئة القائمة كلما حصلت عملية تحميل (أي‬FillDDLMonths :‫ الكود التالي يبين ذلك‬.‫ وما الى ذلك‬F5 ‫ أو‬Refresh ‫من خالل النقر على الزر أو الضغط على‬

protected void Page_Load(object sender, EventArgs e) { FillDDLMonths(); } protected void FillDDLMonths() { for (int i = 1; i <= 12; i++) { DropDownListMonths.Items.Add(i.ToString()); } } protected void ShowNameOfSelectedMonth() { string[] Months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; int Index = int.Parse(DropDownListMonths.Text) - 1; LabelSelectedMonth.Text = "You have selected month:" + Months[Index]; } protected void ButtonShow_Click(object sender, EventArgs e) { ShowNameOfSelectedMonth(); }

‫والسؤال اآلن هو كيف يمكننا منع حصول ذلك وطلب اجراء التعبئة فقط في المرة االولى التي يتم فيها تحميل‬ ‫الصفحة؟‬ :‫ هي إحدى خواص الصفحة وهي عبارة عن متغير بولياني يدل بقيمته على التالي‬IsPostBack .‫ فمعنى ذلك أنها المرة االولى التي يتم فيها تحميل الصفحة خالل الجلسة الحالية‬false ‫إذا كانت قيمته‬

74


‫•‬

‫أما إذا كانت قيمته ‪ true‬د َّل ذلك على حدوث عملية إرسال بيانات إلى الخادم عن طريق الضغط على زر أو‬ ‫اختيار عنصر من عناصر الـ ‪ DropDownList‬أو الـ ‪ ListBox‬أو حتى عند تغير قيمة الـ ‪CheckBox‬‬ ‫أو الـ ‪ PostBack .RadioButton‬معناه أن الصفحة راجعة بعد أن طلب المستخدم من الخادم القيام‬ ‫بتعديالت معينة على الصفحة‪ .‬أي أن القيمة ‪ true‬تدل على أنها ليست المرة االولى التي يتم فيها تحميل‬ ‫الصفحة‬

‫ولذلك حتى نحصل على ما نريد يجب طلب تنفيذ العملية ‪ FillDDLMonths‬فقط في المرة االولى‪ .‬المقطع التالي‬ ‫يبين التغييرات‪:‬‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫{‬ ‫?‪// Is it the first loading of this page‬‬ ‫)‪If (IsPostBack == false‬‬ ‫{‬ ‫;)(‪FillDDLMonths‬‬ ‫}‬ ‫}‬ ‫طبعا بإمكاننا فحص قيمة هذه الخاصية في كل مكان داخل الصفحة‪ .‬فحص هذه الخاصية هي الطريقة الصحيحة‬ ‫للقيام بما يُسمى بإعطاء قيم أولية (‪ )One Time Initialization‬لألدوات والقيام بتعديالت أولية على مظهر‬ ‫ومحتوى الصفحة‪ .‬قد يقول قائل بأنه بإمكاننا اضافة هذه القيم بشكل يدوي الى األداة ‪DropDownListMonths‬‬ ‫في صفحة التنسيق (أي صفحة الـ ‪ .)aspx‬هذا صحيح في هذه الحالة‪ .‬لكن ماذا سنفعل‪ ،‬كما سنبين ذلك الحقا‪،‬‬ ‫عندما يكون المحتوى آتيًا من قاعدة البيانات وال بد من تعبئة األداة في صفحة البرمجة (أي في الصفحة‬ ‫‪)aspx.cs‬؟ الحل الصحيح يكمن في الطريقة التي ُ‬ ‫شرحت أعاله‪ .‬طبعا بإمكانك مثال تفريغ القائمة في كل مرة قبل‬ ‫تعبئتها من خالل األمر‪:‬‬ ‫;)(‪DropDownListMonths.Items.Clear‬‬ ‫اال أن هذا مكلف جدا ألنه يتصل بالخادم ويقرأ معلومات من قاعدة البيانات عددا كبيرا من المرات دون وجود أية‬ ‫حاجة لهذا التكرار المكلف‪.‬‬

‫‪75‬‬


‫تمارين‬ ‫تمرين رقم ‪1‬‬ ‫صمم صفحة انترنت فيها زر مكتوب عليه ‪ Load Page‬وأداة من نوع ‪Label‬‬ ‫•‬

‫في المرة االولى التي يتم فيها تحميل الصفحة تظهر األداة النص‬

‫هذه هي المرة االولى التي يتم فيها تحميل الصفحة‬ ‫•‬

‫وكلما ضغط المستخدم على الزر تظهر األداة النص‬

‫هذه ليست المرة االولى التي يتم فيها تحميل الصفحة‬ ‫بألوان مختلفة يتم اختيارها بشكل عشوائي بحسب األوامر التالية‪:‬‬

‫;)(‪Random Rnd = new Random‬‬ ‫;}‪Color[] Colors = {Color.Red, Color.Blue, Color.Olive, Color.Yellow, Color.Green‬‬ ‫;])‪LabelText.ForeColor = Colors[Rnd.Next(0, Colors.Length‬‬ ‫;" هذه ليست المرة االولى التي يتم فيها تحميل الصفحة" = ‪LabelText.Text‬‬ ‫الحظ أنه يجب اضافة األمر التالي في رأس الصفحة حتى نتمكن من استعمال األلوان‪:‬‬ ‫;‪using System.Drawing‬‬

‫‪76‬‬


‫‪State Management‬‬ ‫ربما ومن خالل تجاربك في تطوير تطبيقات النوافذ (أي ‪ )Windows Applications‬تدرك انه حين تعريفك‬ ‫لمتغير ما فسيبقى هذا المتغير محتفظا ً بقيمته حتى إنهاء البرنامج أو الخروج خارج المجال الذي ُعرّف به هذا‬ ‫المتغير‪ .‬هذا صحيح أيضا بالنسبة للكائنات‪ .‬فالكائن يحتفظ بقيم خصائصه جميعها طالما بقي في المجال الذي تم فيه‬ ‫تعريفه وإنشاؤه‪ُ .‬تسمى قيم الخصائص للكائن بوضع الكائن أو بحالة الكائن (‪ .)Object State‬إال أن هذا األمر‬ ‫مختلف تماما ً في التطبيقات اإلنترنيت (أي ‪ Web Applications‬أو ‪ )Web Sites‬حيث ستضيع قيم المتغيرات‬ ‫مع أول تعديل للصفحة (أي ‪ .)Refresh‬لذا كان من الالزم عليك أن تقوم بتخزين قيم متغيراتك بحيث يمكنك‬ ‫استرجاعها متى شئت‪ .‬إضاعة قيم الخصائص تجعل الصفحة فاقدة للحالة أو للوضع أي بدون وضع (أي‬ ‫‪ .)Stateless‬ويُسمى هذا النوع من البرمجة بالبرمجة عديمة الحالة (أي ‪.)Stateless Programming‬‬ ‫مثال لو أردنا في المثال السابق أن نعرف عدد المرات التي ضغط المستخدم فيها على الزر ‪Show Selected‬‬ ‫‪ Month‬وعرض النتيجة في كل مرة بواسطة األداة ‪ LabelClicksCounter‬لقمنا بتعريف عداد (باسم‬ ‫‪ ) ClicksCounter‬من نوع ‪ int‬كخاصية من خصائص الصفحة وأعطيناه القيمة األولية ‪ 0‬في المرة األولى التي‬ ‫يتم فيها تحميل الصفحة ولقمنا بتحديث العداد في معالج الحدث ‪ ButtonShow_Click‬المسئول عن معالجة‬ ‫حدث النقر على الزر‪ .‬كل هذه الخطوات صحيحة‪.‬‬ ‫الكود التالي يبين هذه الخطوات‪:‬‬ ‫‪public partial class _Default : System.Web.UI.Page‬‬ ‫{‬ ‫;‪int ClicksCounter‬‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫{‬ ‫)‪if (!IsPostBack‬‬ ‫{‬ ‫;‪FillDDLMonths(); ClicksCounter = 0‬‬ ‫}‬ ‫}‬ ‫)‪protected void ButtonShow_Click(object sender, EventArgs e‬‬ ‫{‬ ‫;)(‪ShowNameOfSelectedMonth‬‬ ‫;‪ClicksCounter++‬‬ ‫;‪LabelClicksCounter.Text = "Number of clicks is " + ClicksCounter‬‬ ‫}}‬ ‫إال أن المفاجأة أن العداد سيعرض دائما القيمة ‪ 0‬ألنه يفقد قيمته بعد كل عملية ‪ PostBack‬أي بعد كل عملية‬ ‫تحميل جديدة للصفحة‪.‬‬ ‫‪77‬‬


‫تمكننا ‪ ASP.NET‬من تخزين القيم للحفاظ على حالة أو وضع الصفحة بواحدة من ست طرق أساسية‪:‬‬ ‫‪.1‬‬

‫‪Control state‬‬

‫‪.2‬‬

‫‪ViewState‬‬

‫‪.3‬‬

‫‪Session‬‬

‫‪.4‬‬

‫‪Cookies‬‬

‫‪Control state‬‬ ‫تتوفر لدى جميع األدوات (أي الـ ‪ )Controls‬خاصيّة بوليانية باسم ‪ EnableViewState‬تمكن المبرمج من أن‬ ‫يطلب من األداة أن تحافظ على حالتها بين كل تحميل وتحميل للصفحة‪ .‬القيمة االفتراضية لهذه الخاصيّة ‪ ، true‬ما‬ ‫يعني أن األدوات‪ ،‬وخالفا للمتغيرات العادية‪ ،‬تحتفظ بقيمها‪ .‬الحظ أن هذه اإلمكانية متوفرة فقط لألدوات‪.‬‬

‫‪ViewState‬‬ ‫عبارة عن كائن عام متوفر في كل الصفحات ليُمكن المستخدم من حفط قيم المتغيرات على مستوى الصفحة فقط‪.‬‬ ‫هذا الكائن قادرعلى تخزين واسترجاع جميع أنواع المعلومات األساسية والمُركبة (أي المؤشرات والكائنات)‪.‬‬ ‫طريقة استخدام هذا الكائن شبيهة الى ح ٍد ما بطريقة استخدام المصفوفات األحادية‪ .‬القاعدة العامة لحفظ القيم هي‬ ‫كالتالي‪:‬‬ ‫;‪ViewState["Key"] = Value‬‬ ‫والمقصود بـ ‪ Key‬أي اسم يصلح للمتغير الذي نريد حفظ قيمته‪ .‬وال بأس في استعمال نفس اسم المتغير‪ .‬أما‬ ‫‪ Value‬فقد تكون قيمة (رقمية أو منطقية أو نص) أو أي مؤشر لكائن‪.‬‬ ‫مثال‪ :‬لحل مشكلة العدّاد من المثال السابق نقوم باضافة السطر التالي الى االجراء ‪ Page_Load‬على النحو‬ ‫التالي‪:‬‬ ‫;‪ViewState["ClicksCounter"] = 0‬‬ ‫أي أننا نحفظ تحت االسم ‪ ClicksCounter‬القيمة الصحيحة ‪ .0‬الحظ أننا هذه المرة لسنا بحاجة الى المتغير‬ ‫‪ ClicksCounter‬الذي عرفناه سابقا على مستوى الصفحة‪.‬‬ ‫استخراج القيمة من الكائن ‪ ViewState‬تكون على النحو التالي‪:‬‬

‫‪78‬‬


‫;]"‪VarType VarName = (VarType)ViewState["Key‬‬ ‫الحظ أننا ملزمون بالقيام بعملية تحويل للقيمة (أي ‪ )Down Casting‬المُخزنة حتى تناسب النوع الذي تم تخزينها‬ ‫فيه‪ .‬وذلك ألن الكائن ‪ ViewState‬يحفظ جميع القيم كأنها من نوع ‪ Object‬أي أنه يقوم بعملية ‪UpCasting‬‬ ‫لكل المؤشرات والقيم‪ .‬بهذه الطريقة يكون بامكان ‪ ViewState‬أن يُخزن جميع األنماط ألنها كلها من نوع‬ ‫‪ Object‬وفقا للقاعدة القائلة‪:‬‬ ‫‪In C# everything is an object‬‬ ‫في المثال السابق نقوم بالتالي‪:‬‬

‫)‪protected void ButtonShow_Click(object sender, EventArgs e‬‬ ‫{‬ ‫;)(‪ShowNameOfSelectedMonth‬‬ ‫هنا نستعيد القيمة المحفوظة وهي من نوع صحيح ‪int c = (int)ViewState["ClicksCounter"]; //‬‬ ‫هنا نرفع قيمة العداد ‪c++; //‬‬ ‫هنا نقوم بحفظها مرة أخرى ‪ViewState["ClicksCounter"] = c;//‬‬ ‫ثم نعرض القيمة للمستخدم ‪LabelClicksCounter.Text = "Number of clicks is " + c;//‬‬ ‫}‬ ‫والنتيجة واضحة في الصورة التالية‪:‬‬

‫يمكنك أيضا ازالة أية قيمة من الـ ‪ ViewState‬بمساعدة العملية ‪Remove‬‬

‫‪79‬‬


‫;]"‪ViewState.Remove["Key‬‬ ‫معنى االزالة أن استرجاع قيم بواسطة االسم الذي حُذف أصبح غير ممكن ألن المؤشرعليها أصبح ‪ null‬أي غير‬ ‫صالح لالستعمال‪ .‬لكن الكتابة من جديد تبقى ممكنة‪.‬‬ ‫الجدول التالي يبين بعض العمليات التابعة للكائن ‪:ViewState‬‬ ‫الشرح‬

‫العملية أو الخاصية‬ ‫;)‪StateItem Add(string Key, object Value‬‬

‫تضيف الى الـ ‪ ViewState‬زوجا جديدا من‬ ‫المعلومات‪ .‬أي تحفظ قيمة الـ ‪ Value‬تحت الـ‬ ‫‪.Key‬‬ ‫تحذف جميع األزواج (‪)Key, Value‬‬

‫;)(‪void Clear‬‬

‫الموجودة في الـ ‪ .ViewState‬أي تنفذ العملية‬ ‫‪ Remove‬لكل زوج من األزواج الموجودة‪.‬‬ ‫خاصية تعيد عدد األزواج الموجودة في الـ‬

‫‪int Count‬‬

‫‪ViewState‬‬ ‫خاصية تعيد جميع األسماء أو المفاتيح الموجودة‬

‫‪ICollection Keys‬‬

‫في الـ ‪ViewState‬‬ ‫خاصية تعيد جميع القيم الموجودة في الـ‬

‫‪ICollection Values‬‬

‫‪ViewState‬‬

‫يتم تخزين قيم الـ ‪ ViewState‬بداخل خقل خفي (‪ )Hidden Field‬باسم ‪ _ViewState‬وبطريقة مشفرة‪ .‬لذلك‬ ‫فانه بمجرد مغاددرة الصفحة التي تتبع لها هذه القيم‪ ،‬تضيع هذه القيم‪ .‬يمكننا رؤية ومعاينة هذه القيم بداخل ملف‬ ‫االكود الذي في حاسوب الزبون من خالل الضغط على الزر األيمن للفارة في أي مكان داخل الصفحة واختيار األمر‬ ‫‪ View Source‬من القائمة التي تظهر (في المستكشف ‪ .)Internet Explorer‬في مثالنا السابق سنرى في‬ ‫الملف الذي يظهر المحتوى التالي‪:‬‬

‫"‪<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE‬‬ ‫‪value="/wEPDwUKMTE3ODc0OTEyNw8WAh4NQ2xpY2tzQ291bnRlcmYWAmY‬‬ ‫‪PZBYCAgMPZBYCAgUPZBYCAgEPEGQPFgxmAgECAgIDAgQCBQIGAgcCCA‬‬ ‫‪80‬‬


‫‪IJAgoCCxYMEAUBMQUBMWcQBQEyBQEyZxAFATMFATNnEAUBNAUBNGcQ‬‬ ‫‪BQE1BQE1ZxAFATYFATZnEAUBNwUBN2cQBQE4BQE4ZxAFATkFATlnEAUC‬‬ ‫‪MTAFAjEwZxAFAjExBQIxMWcQBQIxMgUCMTJnZGRkJNe+plaJERNhFqmCUg‬‬ ‫>‪cJJkm4rgllFdMisnAkpyDN4WM=" /‬‬ ‫هنالك العديد من المواقع التي تمكن المستخدم من فك الشيفرة عن هذه النصوص واظهار القيم بشكل مفهوم‪ .‬مثال‬ ‫على ذلك الموقع‪:‬‬ ‫‪http://ignatu.co.uk/ViewStateDecoder.aspx‬‬ ‫حاول فك الشيفرة التي في المثال بمساعدة هذا الموقع‪.‬‬

‫‪Session‬‬ ‫عبارة عن كائن عام متوفر في كل الصفحات ليُمكن المستخدم من حفط قيم المتغيرات على مستوى جميع الصفحات‬ ‫أي على مستوى كل الموقع ولكن لزبون واحد‪ .‬هذا الكائن قادرأيضا تماما مثل الكائن ‪ ViewState‬على تخزين‬ ‫واسترجاع جميع أنواع المعلومات األساسية والمُركبة (أي المؤشرات والكائنات) وبنفس الطريقة التي ُ‬ ‫شرحت عند‬ ‫‪:ViewState‬‬ ‫;"‪Session["Username"] = "Me‬‬ ‫واالستعادة‬ ‫;]" ‪string name = (string) Session["Username‬‬ ‫هنالك خاصية باسم ‪ Timeout‬والتي تحدد فترة احتفاظ الــ ‪ Session‬بقيمتـه بالـدقائق‪ .‬المـدة الزمنيـة االفتراضـية‬ ‫هي ‪ 20‬دقيقة‬ ‫‪Session.Timeout = 5‬‬

‫‪81‬‬


‫الجدول التالي يبين بعض العمليات التابعة للكائن ‪:Session‬‬ ‫الشرح‬

‫العملية أو الخاصية‬

‫;)‪ StateItem Add(string Key, object Value‬تضيف الى الـ ‪ Session‬زوجا جديدا من‬ ‫المعلومات‪ .‬أي تحفظ قيمة الـ ‪ Value‬تحت الـ‬ ‫‪.Key‬‬ ‫;)(‪ void Clear‬تحذف جميع األزواج (‪)Key, Value‬‬ ‫الموجودة في الـ ‪ .Session‬أي تنفذ العملية‬ ‫‪ Remove‬لكل زوج من األزواج الموجودة‪.‬‬ ‫لكن الجلسة الحالية (أي الـ ‪ )Session‬تبقى‬ ‫صالحة‪.‬‬ ‫;‪ int Count‬خاصية تعيد عدد األزواج الموجودة في الـ‬ ‫‪Session‬‬ ‫;)(‪ void Abandon‬تحذف جميع األزواج (‪)Key, Value‬‬ ‫الموجودة في الـ ‪ .Session‬أي تنفذ العملية‬ ‫‪ Remove‬لكل زوج من األزواج الموجودة‪.‬‬ ‫وأيضا الجلسة الحالية (أي الـ ‪ )Session‬تصبح‬ ‫غير صالحة‪.‬‬ ‫;‪ string SessionID‬رقم هوية الجلسة الحالية‪ .‬وهي عبارة عن نص‬ ‫خاص لكل مُستخدم‪ .‬ويتم انشاؤها عن طريق‬ ‫الخادم (أي الـ ‪)Server‬‬ ‫;)‪ void Remove(string Key‬تحذف ‪ Key,‬وقيمته من الـ ‪.Session‬‬ ‫;)(‪ void RemoveAll‬تحذف جميع األزواج (‪)Key, Value‬‬ ‫الموجودة في الـ ‪.Session‬‬ ‫;)‪ void RemoveAt(int Index‬تحذف الزوج (‪ )Key, Value‬الموجود في الـ‬ ‫‪ Session‬في المكان المُشار اليه بـ ‪.Index‬‬

‫مثال لـ ‪SessionID‬‬

‫‪82‬‬


‫كيف نقرر هل نستعمل ‪ Session‬أم ‪ViewState‬؟‬ ‫اذا كان حفظ القيم ضروريا في الصفحة الواحدة فقط نستعمل ‪ .ViewState‬أم اذا أردنا حفظ القيم بحيث تكون‬ ‫متوفرة في أكثر من صفحة حينها نقوم باستعمال الـ ‪.Session‬‬ ‫‪Cookies‬‬ ‫تشكل الكوكيز طريقة لحفظ واسترجاع القيم المتعلقة بالمستخدمين على مستوى كل الموقع وليس على مستى صفحة‬ ‫معينة‪ُ .‬تحفظ القيم في جهاز الزبون (أي في الـ ‪ .)Client‬يتم االحتفاظ بهذه القيم في ملفات نصية صغيرة اذا سمح‬ ‫متصفح الزبون بذلك‪ُ .‬تعتبر هذه الطريقة األكثر شهرة في االحتفاظ بمعلومات الدخول والتسجيل في المواقع‬ ‫المختلفة‪ .‬واآلن سنشرح قواعد استعمالها‪.‬‬ ‫• اذا اردنا أن نعرف ما اذا تم تخزين كوكيز بنجاح تحت اسم معين نقوم بالفحص التالي‪:‬‬

‫)‪if (Request.Cookies["Key"] != null‬‬ ‫• أما اذا اردنا القيام بتخزين كوكيز تحت اسم معين نقوم بالتالي‪:‬‬ ‫‪ .1‬ننشئ كائنا من نوع ‪ .HttpCookie‬الحظ أن الباني يأخذ المفتاح وقيمته‪:‬‬ ‫;)‪HttpCookie Cookie = new HttpCookie("Key", Value‬‬ ‫‪ .2‬نقوم بتحديد فترة صالحية المعلومات لغاية تاريخ معين‪:‬‬ ‫;)"‪Cookie.Expires = DateTime.Parse("01/02/2014‬‬ ‫‪ .3‬نضيف الكوكيز الى مجمع الكوكيز التابع للكائن ‪:Response‬‬ ‫;)‪Response.Cookies.Add(Cookie‬‬ ‫طبعا بامكاننا اضافة الكوكيز بالطريقة التالية‪:‬‬

‫‪83‬‬


Response.Cookies["Key"].Value = Value; Response.Cookies["Key"].Expires = DateTime.Now.AddDays(1); :‫أو بالطريقة التالية‬ HttpCookie Cookie = new HttpCookie("Key"); Cookie.Value = Value; Cookie.Expires = DateTime.Now.AddDays(1);// ‫أي يوم واحد من لحظة الدخول‬ Response.Cookies.Add(Cookie); ‫مثال‬ Response.Cookies["UserName"].Value = "Khalil"; Response.Cookies["UserName "].Expires = DateTime.Now.AddDays(1); ‫أو‬ HttpCookie aCookie = new HttpCookie("LastVisit"); aCookie.Value = DateTime.Now.ToString(); aCookie.Expires = DateTime.Now.AddDays(1); Response.Cookies.Add(aCookie); ‫مثال‬ ‫ بامكان المستخدم‬.‫ حيث سنقوم بتسجيل اختيار المستخدم للون خلفية الصفحة‬، ‫سنجرب اآلن مثاالً على الكوكيز‬ ‫ سيتم مع كل تشغيل للموقع استخدام اللون المفضل لديه اذا‬.‫اختيار اللون االخضر أو األحمرعلى سبيل المثال فقط‬ ‫ خالف ذلكس يتم اختيار اللون االفتراضي وهو األصفر‬.)‫سبق له أن حدد اللون (أي اذا وجدنا كوكيز لحفظ اللون‬ .‫مثال‬

84


:‫الكود التالي يوضح الحل‬ <asp:Panel ID="Panel1" runat="server" Font-Size="Larger" Height="134px" Width="446px" BackColor="Yellow"> ‫<المطلوب اللون اختيار الرجاء‬br /> <br /> <asp:RadioButton ID="RadioButton1" runat="server" Text="‫"أخضر‬ GroupName="colors"/> <br /> <asp:RadioButton ID="RadioButton2" runat="server" Text="‫"أحمر‬ GroupName="colors"/> <br /> <br /> <asp:Button ID="ButtonSave" runat="server" Text="‫"اللون احفظ‬ onclick="ButtonSave_Click" /> </asp:Panel> ،‫ سنقوم بانشاء كوكيز نخزن فيه اللون المختار حاليا ً بالشكل التالي في حدث الضغط على زر احفظ اللون‬، ‫واآلن‬ ‫وسنعطي هذا الكوكيز تاريخا ً لالنتهاء‬ protected void ButtonSave_Click(object sender, EventArgs e) { string color; if (RadioButton1.Checked) { color = "Green"; Panel1.BackColor = System.Drawing.Color.Green; } else if (RadioButton2.Checked) { color = "Red"; Panel1.BackColor = System.Drawing.Color.Red; }

85


else color = ""; HttpCookie Cookie = new HttpCookie("myColor", color); Cookie.Expires = DateTime.Parse("01/02/2014"); Response.Cookies.Add(Cookie); } ‫ فسيتم معرفة قيمته‬myColor ‫ وفي حالة وجود‬،‫ نقوم بقراءة الكوكيز‬Form_Load ‫واآلن في المعالج‬ if (Request.Cookies["myColor"] != null) { string color = Request.Cookies["myColor"].Value; if (color == "Green") Panel1.BackColor = System.Drawing.Color.Green; else if (color == "Red") Panel1.BackColor = System.Drawing.Color.Red; } ‫مثال‬ ‫المثال التالي يحفظ معلومات الدخول بالنسبة لزبون معين بواسطة الكوكيز اذا اراد المستخدم ذلك عن طريق الـ‬ :Remember Me ‫ المسمى‬CheckBox :‫نموذج الدخول يبدو كالتالي‬

86


:‫كود التنسيق‬

<form id="form1" runat="server"> <div> <table> <tr> <td> Username </td> <td> <asp:TextBox ID="TextBoxUsername" Width="200" runat="server"></asp:TextBox> </td> </tr> <tr> <td> Password </td> <td> <asp:TextBox ID="TextBoxPassword" Width="200" runat="server" TextMode="Password"></asp:TextBox> </td> </tr> <tr> <td colspan="2" align="left"> <asp:CheckBox ID="CheckBoxRemeberMe" runat="server" Text="Remember Me" /> </td> </tr> <tr> <td colspan="2" align="center"> <asp:Button ID="ButtonLogin" runat="server" Text="Login" onclick="ButtonLogin_Click" /> </td> </tr> </table> </div> </form>

87


:‫البرمجة‬ protected void Page_Load(object sender, EventArgs e) { if (Request.Cookies["LoginData"] != null) { TextBoxUsername.Text = Request.Cookies["LoginData"]["Username"].ToString(); // ‫الحظ أن تخزين كلمة المرور في األداة تتم بطريقة خاصة‬ TextBoxPassword.Attributes["value"] = Request.Cookies["LoginData"]["Password"].ToString(); } } protected void ButtonLogin_Click(object sender, EventArgs e) { if (CheckBoxRemeberMe.Checked) { HttpCookie Cookie = new HttpCookie("LoginData"); Cookie.Values.Add("Username", TextBoxUsername.Text); Cookie.Values.Add("Password", TextBoxPassword.Text); Cookie.Expires = DateTime.Now.AddDays(30); Response.Cookies.Add(Cookie); } } Application Available in all Pages and in all ( ‫عبارة عن كائن عام متوفر في كل الصفحات وفي كل الجلسات‬ ‫ هذا الكائن قادر أيضا تماما‬.‫ ليُمكن المستخدم من حفط قيم المتغيرات على مستوى جميع الزبائن‬.)Sessions ‫ على تخزين واسترجاع جميع أنواع المعلومات األساسية والمُركبة (أي المؤشرات‬ViewState ‫مثل الكائن‬ ُ ‫والكائنات) وبنفس الطريقة التي‬ ‫ من األمثلة الشائعة على استعماله ادارة عدد‬.ViewState ‫شرحت عند‬ .‫زوارالموقع وهو عبارة عن عداد مشترك لجميع الزبائن وليس لزبون واحد فقط‬ ‫تحتوي هذه الفئة على الدوال والخصائص التالية‬ 88


‫يمكنك اضافة ‪ Application‬جديد بالشكل التالي‬ ‫;‪Application["Visitors"] = 1‬‬

‫ويمكن استعادتها في مربع عنوان مثالً‬

‫;]"‪LabelVistor.Text = (string)Application["Visitors‬‬ ‫ولتعديل قيمة مثالً‬ ‫;‪Application["Visitors"]= ((int)Application["Visitors"])+1‬‬ ‫والسؤال اآلن‪ :‬ما هو المكان المناسب العطاء هذا العداد قيمة أولية؟‬

‫‪Global.asax‬‬ ‫توفر لغة ‪ ASP.NET‬ملفا خاصا لهذا الغرض فيه معالجات لألحداث التي تدل على مثال بداية الجلسة ونهايتها‬ ‫وبداية عمل التطبيق أو الموقع في الخادم وما الى ذلك‪ .‬في كل صفحة يوجد ملف واحد بهذا االسم‪ .‬اضافة هذا الملف‬ ‫تكون بواسطة النقر على الزر األيمن للفارة على اسم الموقع في الـ ‪ Solution Explorer‬واختيار ‪:New Item‬‬

‫ثم اخيار ‪Global Application Class‬‬ ‫‪89‬‬


‫الكود التالي يبين جزءًا من محتوى هذا الملف‪:‬‬ ‫‪public class Global : System.Web.HttpApplication‬‬ ‫{‬ ‫)‪protected void Application_Start(object sender, EventArgs e‬‬ ‫{‬ ‫يتم استدعاء هذا المعالج عند بداية عمل التطبيق أو الموقع في الخادم ‪//‬‬ ‫وهو المكان المناسب العطاء قيم أولية للمعلومات المُخزنة في الكائن ‪// Application‬‬ ‫}‬ ‫)‪protected void Session_Start(object sender, EventArgs e‬‬ ‫{‬ ‫يتم استدعاء هذا المعالج عند بداية الجلسة ‪//‬‬ ‫وهو المكان المناسب العطاء قيم أولية للمعلومات المُخزنة في الكائن ‪// Session‬‬ ‫}‬ ‫)‪protected void Session_End(object sender, EventArgs e‬‬ ‫{‬ ‫يتم استدعاء هذا المعالج عند نهاية الجلسة ‪//‬‬ ‫وهو المكان المناسب اللغاء المعلومات المُخزنة في الكائن ‪// Session‬‬ ‫}‬

‫‪90‬‬


‫)‪protected void Application_End(object sender, EventArgs e‬‬ ‫{‬ ‫يتم استدعاء هذا المعالج عند نهاية عمل التطبيق أو الموقع في الخادم ‪//‬‬ ‫وهو المكان المناسب اللغاء المعلومات المُخزنة في الكائن ‪// Application‬‬ ‫}‬ ‫}‬ ‫في مثال عداد الزوار من المُفضل اعطاء العداد القيمة األولية ‪ 0‬عند بدء العمل بالموقع على الخادم أي‪:‬‬ ‫)‪protected void Application_Start(object sender, EventArgs e‬‬ ‫{‬ ‫;‪Application["Visitors"] = 0‬‬ ‫}‬ ‫تمارين‬ ‫تمرين رقم ‪1‬‬ ‫يجب برمجة جميع األمثلة التي في هذا الفصل‪.‬‬

‫‪91‬‬


‫تمرين رقم ‪2‬‬ ‫صمم موق ًعا بلغة ‪ ASP.NET‬لجمع المعلومات عن مرشحين لوظائف معينة فيه األدوات التالية‪:‬‬

‫يُقبل المرشح إذا حصل على األقل على ‪ 30‬نقطة‪.‬‬ ‫كيفية توزيع النقاط‪:‬‬ ‫•‬

‫المرشح المتزوج يحصل على ‪ 12‬نقطة‪ .‬خالف ذلك يحصل على ‪ 10‬نقاط‪.‬‬

‫•‬

‫المرشح الذي سنه أقل من ‪ 25‬سنة يحصل على ‪ 15‬نقطة‪.‬‬

‫•‬

‫المرشح الذي سنه بين ‪ 25‬و‪ 45‬سنة يحصل على ‪ 20‬نقطة‪.‬‬

‫•‬

‫المرشح الذي تجاوز ‪ 45‬سنة يحصل على ‪ 13‬نقطة‪.‬‬

‫•‬

‫المرشح الذي له خبرة خارج البالد يحصل على ‪ 15‬نقطة‪ .‬خالف ذلك يحصل على ‪ 10‬نقاط‪.‬‬

‫عند ظهور الصفحة للمرة األولى يجب ازالة جميع اختيارات الحاالت األفتراضية )‪.(Default Settings‬‬ ‫عند النقر على الزر " افحص المعطيات" على الصفحة أن تفحص أنه بالفعل تم اختيار الحالة الشخصية والجيل ومن‬ ‫ثم حساب عدد النقاط وكتابة النتيجة في صندوق النتيجة‪ .‬يجب كتابة النص "قُبل" أو " لم يُقبل" بنا ًء على عدد النقاط‪.‬‬ ‫اذا لم يتم اختيار المعلومات الالزمة‪ ،‬على الصفحة أن تظهر نص خط ٍأ في مكان ما في الصفحة من خالل استعمال‬ ‫األداة ‪.Label‬‬ ‫عند النقر على الزر " المرشح التالي" على الصفحة أن تزيل جميع األختيارات استعداداً ألستيعاب المعلومات عن‬ ‫المرشح التالي‪.‬‬ ‫أما عند النقر على الزر "النتائج النهائية" على الصفحة أن تظهر عدد المرشحين الكلي وعدد الذين تم قبولهم‪.‬‬

‫‪92‬‬


‫تمرين رقم ‪3‬‬ ‫المواضيع‪:‬‬ ‫•‬

‫األدوات ‪ DropDownList‬و ‪ListBox‬‬

‫•‬

‫الفئة ‪DateTime‬‬

‫•‬

‫االستثناءات‬

‫صمم موق ًعا بلغة ‪ ASP.NET‬لتسجيل طالب لدروس معينة فيه األدوات التالية‪:‬‬

‫تعبئة األيام واألشهر والسنوات الى قوائم تاريخ الميالد يجب أن تتم عند ظهور الصفحة للمرة األولى‪ .‬أما ادخال‬ ‫أسماء الدروس فيجب أن يتم في صفحة الـ ‪.aspx‬‬ ‫عند النقر على الزر " أضف الطالب الى القائمة" على الصفحة أن تفحص أنه بالفعل تم تعبئة جميع المعلومات‬ ‫الالزمة‪ .‬اذا لم يتم ادخال أو اختيار المعلومات الالزمة‪ ،‬على الصفحة أن تظهر نص خط ٍأ في مكان ما في الصفحة‬ ‫من خالل استعمال األداة ‪.Label‬‬

‫‪93‬‬


‫يجب فحص صحة التاريخ بواسطة االستثناءات كالتالي‪:‬‬

‫‪try‬‬ ‫{‬ ‫;)‪int nDay = int.Parse(ddlDay.Text‬‬ ‫;)‪int nMonth = int.Parse(ddlMonth.Text‬‬ ‫;)‪int nYear = int.Parse(ddlYear.Text‬‬ ‫;)‪DateTime dt = new DateTime(nYear, nMonth, nDay‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫;"التاريخ الذي اختير غير صالح" = ‪LabelError.Text‬‬ ‫;‪return‬‬ ‫}‬ ‫إذا كان التاريخ غير صالح فان الباني )…(‪ DateTime‬سوف يحدث استثنا ًء وبذلك يكون بامكاننا "القبض" عليه‬ ‫ومعالجته بواسطة األوامر ‪.try/catch‬‬ ‫بعدها يتم إضافة المعلومات كسطر جديد في قائمة الطالب بالنسق الظاهر في المثال أعاله ويتم تفريغ جميع صناديق‬ ‫النصوص )‪ (TextBoxes‬من محتواها‪.‬‬

‫‪94‬‬


‫أدوات التحقق من صحة اإلدخال‬ ‫توفر منظومة ‪ ASP.NET‬أدوات خاصة للتحقق من صحة اإلدخال‪ .‬وظيفة هذه األدوات هي فحص محتوى‬ ‫األدوات الموجودة في نموذج ما والتحقق من أن هذا المحتوى يحقق الشروط التي حددها المبرمج‪ .‬إذا لم يتحقق أحد‬ ‫هذه الشروط‪ ،‬يُمنع إرسال المعلومات إلى الخادم وتظهر رسالة مناسبة توضح الخطأ الموجود في المعلومات‪ .‬هنالك‬ ‫عدة أدوات من هذا النوع بحسب اإلمكانيات التالية‪:‬‬ ‫األداة القادرة على تنفيذ هذا الفحص‬

‫الفحص المطلوب‬ ‫هل المحتوى فارغ؟‬

‫‪RequiredFieldValidator‬‬

‫هل المحتوى يقع في مجال معين (مجال عددي‪ ،‬مجال تواريخ‪ ،‬مجال‬

‫‪RangeValidator‬‬

‫عمالت‪ ،‬مجال نصوص‪)...،‬؟‬ ‫هل المحتوى مكتوب وفق مبنى معين (مثال عنوان بريد الكتروني‪ ،‬رقم‬

‫‪RegularExpressionValidator‬‬

‫هاتف‪ ،‬رقم بريدي‪)...،‬؟‬ ‫متساو؟ وبشكل عام تقارن محتوى أداتين‪.‬‬ ‫هل محتوى أداتين‬ ‫ٍ‬

‫‪CompareValidator‬‬

‫هنالك أيضا‪:‬‬ ‫•‬

‫األداة ‪ ValidationSummary‬التي تقوم بإظهار ملخص لجميع األخطاء في مكان واحد في الصفحة‪.‬‬

‫•‬

‫واألداة ‪ CustomValidator‬التي تسمح لنا بكتابة عملية التحقق من صحة البيانات بأنفسنا اذا لم توفر أية‬ ‫أداة من التي ُذكرت بالجدول المطلوب‪.‬‬

‫سوف نشرح هذه األدوات الحقا عند األمثلة‪.‬‬ ‫تجد هذه األدوات موجودة في الـ ‪ Toolbox‬ضمن مجموعة الـ ‪:Validation‬‬

‫واآلن سنشرح خصائص هذه األدوات وكيفية استعمالها مع أمثلة توضيحية‪.‬‬

‫‪95‬‬


‫األداة ‪RequiredFieldValidator‬‬ ‫قلنا بأن هذه األداة قادرة على التحقق من أن محتوى أداة إدخال معينة (مثل الـ ‪ )TextBox‬ليس فارغا‪ .‬الجدول‬ ‫التالي يشرح بعض الخصائص المتوفرة لدى هذه األداة‪:‬‬ ‫الشرح‬

‫الخاصية‬

‫مثال‬

‫‪ID‬‬

‫اسم األداة‬

‫‪"ID="rfvUserName‬‬

‫‪runat‬‬

‫قيمتها دائما ‪ server‬للداللة على أن األداة أداة خادم‬

‫"‪runat="server‬‬

‫أي ‪Server Control‬‬ ‫‪ErrorMessage‬‬

‫نص الخطأ الذي يجب أن يظهر إذا كان المحتوى‬

‫"*"=‪ErrorMessage‬‬

‫فارغا‬ ‫‪ForeColor‬‬

‫لون نص الخطأ ‪ErrorMessage‬‬

‫‪ ControlToValidate‬اسم األداة (أي الـ ‪ )ID‬التي نريد التحقق من‬

‫‪Display‬‬

‫"‪ForeColor="Red‬‬ ‫=‪ControlToValidate‬‬

‫محتواها‪.‬‬

‫"‪"TextBoxUsername‬‬

‫تحدد كيفية ظهور نص الخطأ‪ .‬هذه الخاصيّة تأخذ‬

‫"‪Display="Dynamic‬‬

‫واحدة من القيم التالية‪:‬‬ ‫‪ :None‬ال حاجة إلظهار نص الخطأ بالقرب من‬ ‫األداة‪ُ .‬تستعمل عادة القتصار اظهار نصوص الخطأ‬ ‫بواسطة األداة ‪ValidationSummary‬‬ ‫‪ :Static‬تحتل أداة التحقق من صحة االدخال‬ ‫المساحة المُعدة لها بشكل دائم حتى وان لم يظهر‬ ‫خطأ‪.‬‬ ‫‪ :Dynamic‬تحتل أداة التحقق من صحة االدخال‬ ‫المساحة المُعدة لها فقط عند اظهار الخطأ وليس‬ ‫بشكل ثابت‪.‬‬ ‫‪Text‬‬

‫نص الخطأ (سيوضح الحقا)‪.‬‬

‫‪Text="Username is‬‬ ‫"‪a required field‬‬

‫مثال‪:‬‬ ‫نريد التحقق من صحة اإلدخال في نموذج الدخول إلى الموقع (أي الـ ‪ .)Login Form‬في هذا النموذج أداتان‪.‬‬ ‫واحدة إلدخال اسم المُستخدم‪ ،‬ال يجوز أن تكون فارغة عند إرسال البيانات إلى الخادم‪ .‬وواحدة إلدخال كلمة السر‪،‬‬

‫‪96‬‬


‫ لذلك سنستعين بأداتين من نوع‬.‫وأيضا ال يجوز أن تكون فارغة عند إرسال البيانات إلى الخادم‬ .‫ للتحقق من ذلك ومنع إرسال بيانات فارغة للخادم‬RequiredFieldValidator :‫كود تنسيق النموذج‬ <form id="form1" runat="server"> <div> <table> <tr> <td> Username </td> <td> <asp:TextBox ID="TextBoxUsername" Width="200" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvUserName" runat="server" ErrorMessage="*" ForeColor="Red" ControlToValidate="TextBoxUsername" Display="Dynamic" Text="Username is a required field"> </asp:RequiredFieldValidator> </td>

‫من ال ُمفضل إضافة أداة التحقق من صحة اإلدخال وراء‬ ‫ ربما أهم خاصية هنا‬.‫األداة التي نريد التحقق من محتواها‬ ‫ التي تأخذ كقيمة اسم‬ControlToValidate ‫هي الخاصية‬ :‫صندوق النص الذي يُدخل إليه اسم المستخدم‬ ControlToValidate="TextBoxUsername"

</tr> <tr> <td> Password </td> <td> <asp:TextBox ID="TextBoxPassword" Width="200" runat="server" TextMode="Password"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvPassword" runat="server" ErrorMessage="*" ForeColor="Red" ControlToValidate="TextBoxPassword" Display="Dynamic" Text="Password is a required field"> </asp:RequiredFieldValidator> </td>

97

‫هنا أضفنا أيضا أداة للتحقق من أن كلمة المرور ليست‬ TextBoxPassword ‫فارغة أي تحققنا من محتوى األداة‬ ControlToValidate="TextBoxPassword"


‫>‪</tr‬‬ ‫>‪<tr‬‬ ‫>"‪<td colspan="2" align="left‬‬ ‫"‪<asp:CheckBox ID="CheckBoxRemeberMe‬‬ ‫>‪runat="server" Text="Remember Me" /‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬ ‫>‪<tr‬‬ ‫>"‪<td colspan="2" align="center‬‬ ‫"‪<asp:Button ID="ButtonLogin‬‬ ‫"‪runat="server" Text="Login‬‬ ‫>‪onclick="ButtonLogin_Click" /‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬ ‫>‪</table‬‬ ‫>‪</div‬‬ ‫>‪</form‬‬ ‫يتم تفعيل هذه األدوات كلما ضغط المُستخدم على أحد األزرار الموجودة في النموذج‪ .‬في مثالنا كلما ضغط المُستخدم‬ ‫على الزر ‪ .Login‬كما قلنا فان الضغط على هذا الزر عندما تكون األدوات فارغة‪ ،‬فان هذا سيمنع ارسال البيانات‬ ‫الى الخادم وستظهر نصوص الخطأ‪:‬‬

‫نصوص الخطأ التي تظهر تتعلق بما هو ناقص فقط‪:‬‬

‫‪98‬‬


‫لكن ماذا لو أضفنا زرا باسم ‪ Reset‬لتفريغ أحد الصناديق بينما الصندوق اآلخر يكون فارغا؟ سوف يتم تفعيل‬ ‫أدوات التحقق من صحة اإلدخال‪ .‬مع أن هذا غير مطلوب في مثل هذه الحاالت‪.‬‬

‫ما العمل ًاذا؟ يوجد لدى األداة ‪ Button‬خاصية بوليانية باسم ‪ CausesValidation‬يجب أن ُتعطى القيمة‬ ‫‪ false‬اذا أردنا الغاء تفعيل أدوات التحقق من صحة اإلدخال عند النقر على زر معين‪:‬‬

‫"‪<asp:Button ID="ButtonReset" runat="server" Text="Reset‬‬ ‫>‪CausesValidation="false" onclick="ButtonReset_Click"/‬‬

‫األداة ‪ValidationSummary‬‬ ‫قلنا سابقا بأن هنالك أيضا األداة ‪ ValidationSummary‬التي تقوم بإظهار ملخص لجميع األخطاء في مكان‬ ‫واحد في الصفحة‪ .‬تظهر هذه األداة نصوص الخطأ المكتوبة عند الخاصية ‪ ErrorMessage‬والتي ُتكتب عادة‬ ‫مفصلّة أكثر من النص المكتوب عند الخاصيّة ‪ Text‬حيث يُكتفى عادة بكاتبة * باللون األحمر الى جانب األداة التي‬ ‫محتواها خطأ‪ .‬الصورة التالية توضح ذلك‪:‬‬

‫‪99‬‬


:‫كود التنسيق التالي يبين التغييرات التي أُجريت لتحقيق ذلك‬ <form id="form1" runat="server"> <div> <table> <tr> <td> Username </td> <td> <asp:TextBox ID="TextBoxUsername" Width="200" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvUserName" runat="server" ErrorMessage="Username is a required field" ForeColor="Red" ControlToValidate="TextBoxUsername" Display="Dynamic" Text="*" ></asp:RequiredFieldValidator>

‫ تحتوي على‬ErrorMessage ‫الخاصية‬ .‫النص التفصيلي‬ ‫ تحتوي على النص‬Text ‫الخاصية‬ .‫المختصر وعادة ما تكون النجمة‬

</td> </tr> <tr> <td> Password </td> <td> <asp:TextBox ID="TextBoxPassword" Width="200" runat="server" TextMode="Password"></asp:TextBox>

100


<asp:RequiredFieldValidator ID="rfvPassword" runat="server" ErrorMessage="Password is a required field" ForeColor="Red" ControlToValidate="TextBoxPassword" Display="Dynamic" Text="*" ></asp:RequiredFieldValidator>

‫ تحتوي على‬ErrorMessage ‫الخاصية‬ .‫النص التفصيلي‬

‫ تحتوي على النص‬Text ‫الخاصية‬ .‫المختصر وعادة ما تكون النجمة‬

</td> </tr> <tr> <td colspan="2" align="left"> <asp:CheckBox ID="CheckBoxRemeberMe" runat="server" Text="Remember Me" /> </td> </tr> <tr> <td> <asp:Button ID="ButtonLogin" runat="server" Text="Login" onclick="ButtonLogin_Click" /> </td> <td> <asp:Button ID="ButtonReset" runat="server" Text="Reset" CausesValidation="false" onclick="ButtonReset_Click"/> </td> </tr> <tr> ValidationSummary ‫هنا أضفنا األداة‬ <td colspan="2"> <asp:ValidationSummary ID="ValidationSummary1" ‫ تأخذ‬HeaderText ‫الحظ أن الخاصية‬ HeaderText="Please fix the following errors:" .‫كقيمة عنوانا مناسبا لجميع األخطاء‬ runat="server" /> </td> </tr> </table> </div> </form> ValidationSummary ‫ المطلوب برمجة األمثلة أعاله وفحص الخصائص التالية لألداة‬:‫فعالية‬

101

‫ وقيمها المُختلفة‬DisplayMode

ShowMessageBox


‫األداة ‪RangeValidator‬‬ ‫قلنا بأن هذه األداة قادرة على التحقق من أن محتوى أداة إدخال معينة (مثل الـ ‪ )TextBox‬يقع في مجال معين‪ .‬هذا‬ ‫المجال قد يكون مجاال ألعداد‪ ،‬أو مجاال لتواريخ أو لعمالت وما الى ذلك‪.‬‬ ‫دعونا في البداية نلقي نظرة على مجموعة الخصائص التابعة لهذه األداة‪ .‬لكن قبل ذلك يجب التنويه الى أن‬ ‫الخصائص التالية (وأخرى أيضا‪ ،‬لكن ال تهمنا هنا) موجودة ومتوفرة لدى جميع أدوات التحقق من صحة االدخال‪:‬‬ ‫‪.ID, runat, ErrorMessage, ForeColor, ControlToValidate, Display, Text‬‬ ‫أما الخصائص الخاصة باألداة ‪ RangeValidator‬والتي تهمنا هنا هي‪:‬‬ ‫الخاصية‬ ‫‪Type‬‬

‫الشرح‬

‫مثال‬

‫تحدد نوع المعلومات التي يجب التحقق منها‪ .‬تأخذ كقيمة "‪Type="Integer‬‬ ‫احدى القيم التالية‪:‬‬

‫‪MinimumValue‬‬

‫•‬

‫‪Integer‬‬

‫•‬

‫‪Date‬‬

‫•‬

‫‪Double‬‬

‫•‬

‫‪Currency‬‬

‫•‬

‫‪String‬‬

‫تحدد القيمة الصغرى المسموح بها‬

‫‪ MaximumValue‬تحدد القيمة الكبرى المسموح بها‬

‫"‪MinimumValue="1‬‬ ‫"‪MaximumValue="100‬‬

‫عند استعمال هذه األداة يجب أن ُتستعمل األداة ‪ RequiredFieldValidator‬أوال للتحقق من أن محتوى‬ ‫األداة ليس فار ًغا‪.‬‬ ‫مثال‬ ‫النموذج التالي عبارة عن استمارة طلب منتج معين (له رقم مكون من ‪ 4‬منازل) بكمية معينة (من ‪ 1‬ولغاية ‪)100‬‬ ‫وتاريخ ايصال خالل سنة معينة (مثال من ‪1/1/2014‬ولغاية ‪:)31/12/2014‬‬

‫‪102‬‬


‫كود التنسيق‬ <table> <tr> <td colspan="2" style="font-size:x-large; text-align:center"> Order Details </td> </tr> <tr> <td style="font-size:larger">ProductID</td> <td style="font-size:larger"> <asp:TextBox ID="TextBoxProductID" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvProductID" runat="server" ErrorMessage="ProductID value should be from the integeres interval [1000,9999]" ForeColor="Red" ControlToValidate="TextBoxProductID" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> <asp:RangeValidator ID="RangeValidator1" runat="server" ErrorMessage="ProductID value should be from the integeres interval [1000,9999]" ForeColor="Red" ControlToValidate="TextBoxProductID" 103


Display="Dynamic" Text="*" Type="Integer" MinimumValue="1000" MaximumValue="9999" > </asp:RangeValidator> </td> </tr> <tr> <td style="font-size:larger">Amount</td> <td style="font-size:larger"> <asp:TextBox ID="TextBoxAmount" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvAmount" runat="server" ErrorMessage="Amount value should be from the integeres interval [1,100]" ForeColor="Red" ControlToValidate="TextBoxAmount" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> <asp:RangeValidator ID="rvAmount" runat="server" ErrorMessage="Amount value should be from the integeres interval [1,100]" ForeColor="Red" ControlToValidate="TextBoxAmount" Display="Dynamic" Text="*" Type="Integer" MinimumValue="1" MaximumValue="100" > </asp:RangeValidator> </td> </tr> <tr> <td style="font-size:larger">Delivery Date</td> <td style="font-size:larger"> <asp:TextBox ID="TextBoxDeliveryDate" runat="server"></asp:TextBox> <asp:RequiredFieldValidator

104


ID="rfvDeliveryDate" runat="server" ErrorMessage="Delivery Date should be from the dates interval [1/1/2014,31/12/2014]" ForeColor="Red" ControlToValidate="TextBoxDeliveryDate" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> <asp:RangeValidator ID="rvDeliveryDate" runat="server" ErrorMessage="Delivery Date should be from the dates interval [1/1/2014,31/12/2014]" ForeColor="Red" ControlToValidate="TextBoxDeliveryDate" Display="Dynamic" Text="*" Type="Date" MinimumValue="1/1/2014" MaximumValue="31/12/2014" > </asp:RangeValidator> </td> </tr> <tr> <td colspan="2"> <asp:Button ID="ButtonSubmit" style="font-size:larger;" runat="server" Text="Submit" /> </td> </tr> <tr> <td colspan="2"> <asp:ValidationSummary ID="ValidationSummary1" HeaderText="Please fix the following errors:" runat="server" /> </td> </tr></table> RegularExpressionValidator ‫األداة‬ ‫) مكتوب وفق مبنى معين‬TextBox ‫قلنا بأن هذه األداة قادرة على التحقق من أن محتوى أداة إدخال معينة (مثل الـ‬ ‫ ترجمة اسم األداة الى العربية هي‬.‫كأن تكون مثال عنوان بريد الكتروني أو رقم هاتف أو رقم بريدي وما الى ذلك‬

105


‫معان معينة بحيث‬ ‫"أداة التحقق من تعبير نظامي"‪ .‬التعبير النظامي عبارة عن نص مكون من رموز خاصّة لها‬ ‫ٍ‬ ‫تشكل قالبا يمكننا من التحقق من أن نصا آخر مبني وفق هذا القالب أم ال‪.‬‬ ‫يجب أن نبدأ التعبير النظامي بالرمز "\" ويجدب انهاؤه بنفس الرمز‪ .‬تستخدم األقواس [] لتحديد عدد معين من‬ ‫الرموز‪ .‬مثال يطابق التعبير [‪ ]abc‬األحرف ‪ a‬أو ‪ b‬أو ‪ . c‬أما التعبير [‪ ]a-z‬فيطابق جميع االحرف االنكليزية‬ ‫الصغيرة وايضا النمط [‪ ]a-zA-Z0-9‬يطابق جميع االحرف االنكليزية بحالتيها (احرف كبيرة واحرف صغيرة)‬ ‫واالرقام من ‪ 0‬الى ‪ .9‬اما لو أضفنا الرمز ^ بعد فتح قوس المجوعة فهو يشير الى عدم مطابقة مجموعة االحرف‬ ‫التالية مثال التعبير [‪ ]^a‬ال يطابق الحرف ‪ .a‬الجدول التالي يوضح مجموعة من الرموز الخاصة المُستخدمة مع‬ ‫التعابير النظاميّة‪:‬‬ ‫الشرح‬

‫الرمز أو التعبير‬ ‫"‪".‬‬

‫يطابق أي رمز باستثناء رمز السطر الجديد "\‪"n‬‬

‫"؟"‬

‫يطابق تكرار النمط ‪ 0‬أو ‪ 1‬مرة‬

‫"*"‬

‫يطابق تكرار النمط ‪ 0‬مرة أو أكثر‬

‫"‪"+‬‬

‫يطابق تكرار النمط ‪ 1‬مرة أو أكثر‬

‫التعبير {‪}x‬‬

‫يطابق تكرار النمط ‪ x‬مرة‬

‫التعبير {‪}x , y‬‬

‫يطابق تكرار النمط ‪ x‬مرة على االقل و ‪ y‬مرة على االكثر‬

‫"\"‬

‫يلغي المعنى الخاص ألي رمز خاص‬

‫ويوجد مجموعة إضافية من المعرفات يمكن استخدامها في التعابير النظامية‪:‬‬ ‫•‬

‫المُعرف ‪ \d‬يطابق أي رقم‬

‫•‬

‫المُعرف ‪ \D‬يطابق أي رمز باستثناء األرقام‬

‫•‬

‫المُعرف ‪ \s‬يطابق الفراغ‬

‫•‬

‫المُعرف ‪ \S‬يطابق أي رمز باستثناء الفراغات‬

‫•‬

‫المُعرف ‪ \w‬يطابق أي حروف أو ارقام‬

‫‪106‬‬


‫سنشرح مبنى التعبير النظامي من خالل أمثلة بسيطة‪:‬‬ ‫التعبير‬ ‫‪gr[ae]y‬‬

‫الشرح‬ ‫جميع الكلمات التي تبدأ بـ ‪ gr‬وتنتهي بـ ‪ .y‬أما في الوسط فيجوز أن يأتي اما‬ ‫الحرف ‪ a‬أو الحرف ‪ .e‬ولذلك فالكلمات الممكنة هي‪ gray :‬أو ‪grey‬‬

‫‪a[0-9]k‬‬

‫جميع الكلمات التي تبدأ بـ ‪ a‬وتنتهي بـ ‪ .k‬أما في الوسط فيجوز أن يأتي رقم واحد‬ ‫فقط من المجال ‪ 0‬ولغاية ‪ .9‬أمثلة لكلمات تحقق ذلك‪:‬‬ ‫‪a0k, a1k, a2k, …, a9k‬‬

‫]‪[0-9a-zA-Z‬‬

‫]‪x[0-9A-Z‬‬

‫يعني رمزا واحدا فقط وهو إما‬ ‫• رقم من المجال ‪ 0‬ولغاية ‪9‬‬ ‫• أو حرف صغير باللغة االنجليزية‬ ‫• أو حرف كبير باللغة االنجليزية‬ ‫جميع الكلمات التي تبدأ بـ ‪ x‬وتنتهي بـرقم أو بحرف كبير من اللغة االنجليزية‪ .‬أمثلة‬ ‫لكلمات تحقق ذلك‪:‬‬ ‫…‪xA, x8, xM, x6,‬‬

‫‪W*s‬‬

‫جميع الكلمات التي تبدأ بـ ‪ W‬وتنتهي بـ ‪ .s‬أما في الوسط فيجوز أن يأتي أي نص‬ ‫ويجوز أن يكون فارغا‪ .‬أمثلة لكلمات تحقق ذلك‪:‬‬ ‫… ‪Ws, Windows, W0s, WINDOWs,‬‬

‫‪0[2-48-9]([-‬‬

‫يطابق رقم هاتف بيتي‪:‬‬

‫}‪]{0,1})\d{7‬‬

‫‪ :0‬الرقم يبدأ بها‬ ‫‪ :\d‬رقم واحد من المجال ‪ 2‬ولغاية ‪ 3‬أو من المجال ‪ 8‬لغاية ‪9‬‬ ‫)}‪ :([-]{0,1‬إما أن نكتب الرمز – مرة واحدة أو ال‬ ‫}‪ 7 :\d{7‬أرقام بالضبط‬ ‫أمثلة‪:‬‬ ‫… ‪04-3456789, 08-3456789, 021234567,‬‬

‫}‪ 05\d([-]{0,1})\d{7‬يطابق رقم هاتف جوال‪:‬‬ ‫‪ :05‬الرقم يبدأ بـ ‪05‬‬ ‫‪ :\d‬رقم واحد‬ ‫)}‪ :([-]{0,1‬إما أن نكتب الرمز – مرة واحدة أو ال‬ ‫}‪ 7 :\d{7‬أرقام بالضبط‬ ‫أمثلة‪:‬‬ ‫… ‪052-3456789, 052-3456789, 0591234567,‬‬

‫‪107‬‬


‫طبعا ال يمكننا هنا التوسع في هذا الموضوع أكثر من ذلك ألن شرح الموضوع كما يجب يحتاج لكتاب كامل‪ .‬الذي‬ ‫يهمنا هنا فهم أولي لهذا الموضوع ليس أكثر‪ .‬ولمن أراد المزيد فبإمكانه الرجوع إلى مواقع االنترنت الكثيرة التي‬ ‫توفر معلومات قيمة عن الموضوع مثل الموقع‪:‬‬ ‫‪http://www.regular-expressions.info/‬‬ ‫أو االستعانة بأي كتاب متخصص في هذا المجال‪ .‬لحسن الحظ فقد وفرت ‪ ASP.NET‬مجموعة من التعابير‬ ‫النظامية لمعظم االستعماالت مثل عنوان بريد الكتروني أو عنوان موقع انترنت وما الى ذلك‪ .‬الختيار التعبير‬ ‫المناسب نقوم بالخطوات التالية‪:‬‬ ‫•‬

‫نفتح صفحة التنسيق بعرض الـ ‪Design‬‬

‫•‬

‫نضع الفارة على أداة التحقق من صحة االدخال ثم نضغط على الزر األيمن للفارة ونختار ‪Properties‬‬

‫•‬

‫ثم نختار الخاصيّة ‪ ValidationExpression‬ونضغط على الزر‬ ‫‪ValidationExpression‬‬

‫•‬

‫ثم نختار التعبير الذي نريده من القائمة التي تظهر‬

‫الصورة التالية تبين بعضا من هذه الخطوات‪:‬‬

‫‪108‬‬

‫الذي يظهر الى يمين الخاصيّة‬


109


‫مثال‬ ‫ أمثلة على ذلك مواقع‬.‫تتطلب بعض المواقع ادخال البريد االلكتروني كاسم مستخدم حين الدخول الى المنظومة‬ ‫ بحيث يكون علينا‬Login ‫ لذلك نعيد هنا تنسيق صفحة الـ‬.‫ وما شابه ذلك‬DropBox ‫البريد االلكتروني أو موقع‬ :‫ادخال البريد االلكتروني كاسم مستخدم‬

:‫كود التنسيق‬ <form id="form1" runat="server"> <div> <table> <tr> <td>Email</td> <td> <asp:TextBox ID="TextBoxEmail" Width="200" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvEmail" runat="server" ErrorMessage="Email is a required field" ForeColor="Red" ControlToValidate="TextBoxEmail" Display="Dynamic" Text="*" ></asp:RequiredFieldValidator>

110


<asp:RegularExpressionValidator ID="revEmail" RegularExpressionValidator ‫هنا أضفنا األداة‬ runat="server" ‫ تأخذ‬ValidationExpression ‫الحظ أن الخاصية‬ ErrorMessage="Email Address is invalid" ForeColor="Red" ‫كقيمة التعبير النظامي الذي يتحقق من أن المحتوى‬ ControlToValidate="TextBoxEmail" ‫هو عنوان بريد الكتروني‬ Display="Dynamic" Text="*" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" ></asp:RegularExpressionValidator> </td> </tr> <tr> <td> Password </td> <td> <asp:TextBox ID="TextBoxPassword" Width="200" runat="server" TextMode="Password"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvPassword" runat="server" ErrorMessage="Password is a required field" ForeColor="Red" ControlToValidate="TextBoxPassword" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> </td> </tr> <tr> <td colspan="2" align="left"> <asp:CheckBox ID="CheckBoxRemeberMe" runat="server" Text="Remember Me" /> </td> </tr> <tr> <td> <asp:Button ID="ButtonLogin" runat="server" Text="Login"/> </td> <td>

111


<asp:Button ID="ButtonReset" runat="server" Text="Reset" CausesValidation="false"/> </td> </tr> <tr> <td colspan="2"> <asp:ValidationSummary ID="ValidationSummary1" HeaderText="Please fix the following errors:" runat="server" /> </td> </tr> </table> </div> </form> </body>

112


‫األداة ‪CompareValidator‬‬ ‫هذه األداة ُتستعمل لمقارنة محتوى أداتين والتحقق من أنهما تحتويان على نفس النص أو أن قيمة واحدة منهم أكبر أو‬ ‫أصغر من الثانية وهكذا وفقا لخاصيّة باسم ‪ Operator‬نحدد من خاللها معامل أو عملية المقارنة‪ .‬الجدول التالي‬ ‫يلخص بعض الخصائص المُميزة لهذه األداة‪:‬‬ ‫الخاصية‬ ‫‪Type‬‬

‫الشرح‬ ‫تحدد نوع المعلومات التي يجب التحقق‬

‫مثال‬ ‫"‪Type="Integer‬‬

‫منها‪ .‬تأخذ كقيمة احدى القيم التالية‪:‬‬

‫‪ControlToValidate‬‬

‫• ‪Integer‬‬ ‫• ‪Date‬‬ ‫• ‪Double‬‬ ‫• ‪Currency‬‬ ‫• ‪String‬‬ ‫اسم األداة (أي الـ ‪ )ID‬التي نريد التحقق من‬

‫=‪ControlToValidate‬‬

‫محتواها‪.‬‬

‫"‪"TextBoxPasswordRetype‬‬

‫‪ ControlToCompare‬اسم األداة الثانية‬

‫=‪ControlToCompare‬‬ ‫"‪"TextBoxPassword‬‬

‫‪Operator‬‬

‫‪ValueToCompare‬‬

‫عملية المقارنة‪ .‬تأخذ واحدة من القيم التالية‪:‬‬ ‫‪ :DataTypeCheck‬لفحص النوع كما‬ ‫هو مبين عند الخاصيّة ‪ .Type‬أي هل‬ ‫القيمة هي ‪ Integer‬أم ‪ Double‬وما الى‬ ‫ذلك‪.‬‬ ‫‪ :Equal‬يساوي‬ ‫‪ :NotEqual‬ال يساوي‬ ‫‪ :GreaterThan‬أكبر‬ ‫‪ :GreaterThanEqual‬أكبر أو‬ ‫يساوي‬ ‫‪ :LessThan‬أصغر‬ ‫‪ :LessThanEqual‬أصغر أو يساوي‬ ‫تمكننا من مقارنة محتوى األداة مع قيمة‬ ‫ثابتة من النوع المًرح عنه في الخاصيّة‬ ‫‪Type‬‬

‫مثال‬ ‫نريد التحقق من المُدخالت في النموذج التالي‪:‬‬ ‫•‬

‫كلمات المرور يجب أن تكون متساوية‬

‫•‬

‫التاريخ يجب أن يكون بعد التاريخ ‪01/01/2014‬‬

‫•‬

‫السن يجب أن يكون عددا صحيحا‬

‫‪113‬‬

‫"‪Operator="Equal‬‬

‫”‪ValueToCompare=”5‬‬


:‫كود التنسيق‬ <form id="form1" runat="server"> <div> <table> <tr> <td> Password </td> <td> <asp:TextBox ID="TextBoxPassword" Width="200" runat="server" TextMode="Password"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvPassword" runat="server" ErrorMessage="Password is a required field" ForeColor="Red" ControlToValidate="TextBoxPassword" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> </td> </tr> <tr> <td>Retype Password</td>

114


<td> <asp:TextBox ID="TextBoxPasswordRetype" Width="200" runat="server" TextMode="Password"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvPasswordRetype" runat="server" ErrorMessage="Password is a required field" ForeColor="Red" ControlToValidate="TextBoxPasswordRetype" Display="Dynamic" Text="*"> </asp:RequiredFieldValidator> ‫الحظ أن الخاصيّة‬ <asp:CompareValidator ‫ أخذت كقيمة‬ControlToCompare ID="cvPasswords" runat="server" TextBoxPassword ‫اسم األداة األولى‬ ForeColor="Red" Operator="Equal" ErrorMessage="Both passwords must match!" ControlToValidate="TextBoxPasswordRetype" ControlToCompare="TextBoxPassword" Type="String"> </asp:CompareValidator> </td> </tr> <tr> <td> Date Of Application </td> <td> <asp:TextBox ID="TextBoxDateOfApplication" Width="200" runat="server"> </asp:TextBox> <asp:RequiredFieldValidator ID="rfvDateOfApplication" runat="server" ErrorMessage="Date Of Application is a required field" ForeColor="Red" ControlToValidate="TextBoxDateOfApplication" Display="Dynamic" Text="*"> </asp:RequiredFieldValidator>

115


‫الحظ أن الخاصيّة‬ <asp:CompareValidator ‫ أخذت كقيمة‬ValueToCompare ID="cvDateOfApplication" ‫تاريخا ثابتا‬ runat="server" ForeColor="Red" Operator="GreaterThan" ErrorMessage="Date Of Application must be after 01/01/2014!" ControlToValidate="TextBoxDateOfApplication" ValueToCompare="01/01/2014" Type="Date" Display="Dynamic" Text="*"> </asp:CompareValidator> </td> </tr> <tr> <td>Age</td> <td> <asp:TextBox ID="TextBoxAge" Width="200" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvAge" runat="server" ErrorMessage="Age is a required field" ForeColor="Red" ControlToValidate="TextBoxAge" Display="Dynamic" Text="*" > </asp:RequiredFieldValidator> <asp:CompareValidator ID="cvAge" runat="server" ForeColor="Red" ErrorMessage="Age must be a number" ControlToValidate="TextBoxAge" ‫ أخذت‬Operator ‫الحظ أن الخاصيّة‬ Type="Integer" Operator="DataTypeCheck" ‫ ألننا نريد‬DataTypeCheck ‫القيمة‬ Display="Dynamic" ‫التحقق من أن قيمة السن عبارة عن قيمة‬ Text="*" > ‫ عبارة عن‬Type ‫عددية أي أن الـ‬ </asp:CompareValidator> Integer </td> </tr> <tr>

116


<tr> <td>Gender</td> <td> <asp:DropDownList ID="DropDownListGender" runat="server"> <asp:ListItem Text="Select Gender" Value="-1"></asp:ListItem> <asp:ListItem Text="Male" Value="Male"></asp:ListItem> <asp:ListItem Text="Female" Value="Female"></asp:ListItem> </asp:DropDownList> <asp:RequiredFieldValidator ID="rfvGender" runat="server" ErrorMessage="Please select your gender" ‫ أخذت‬InitialValue ‫الحظ أن الخاصيّة‬ ForeColor="Red" ‫ للداللة على أن اختيار النص‬-1 ‫القيمة‬ ControlToValidate="DropDownListGender" Display="Dynamic" ‫" من القائمة المنسدلة‬Select Gender" Text="*" ‫يعني أن ال ُمستخدم لم يقم باختيار القيمة‬ InitialValue="-1"> </asp:RequiredFieldValidator> .Female ‫ أو‬Male ‫الصحيحة‬ </td> </tr> <td> <asp:Button ID="ButtonSave" runat="server" Text="Save"/> </td> <td> <asp:Button ID="ButtonReset" runat="server" Text="Reset" CausesValidation="false"/> </td> </tr> <tr> <td colspan="2"> <asp:ValidationSummary ID="ValidationSummary1" HeaderText="Please fix the following errors:" runat="server" /> </td> </tr> </table> </div> </form>

117


‫األداة ‪CustomValidator‬‬ ‫إذا تبين لنا‪ ،‬أن كل األدوات التي تم شرحها حتى اآلن ال يمكنها فحص ما نريد‪ ،‬حينها تسمح لنا األداة‬ ‫‪ CustomValidator‬بكتابة عملية التحقق من البيانات كما نشاء‪ .‬هنالك إمكانيتان لكتابة هذه العملية‪:‬‬ ‫•‬

‫إما أن نكتبها بلغة ‪JavaScript‬‬

‫•‬

‫أو بلغة ‪.C#‬‬

‫كما هو معلوم فان العملية المكتوبة بلغة ‪ُ JavaScript‬تنفذ على حاسوب الزبون ولذلك نسميها ‪Client Side‬‬ ‫‪ .Code‬أما العملية المكتوبة بلغة ‪ C#‬ف ُتنفذ على حاسوب الخادم ولذلك نسميها ‪ .Server Side Code‬نعرض‬ ‫اآلن بعض الخصائص المُميزة لهذه األدوات‪:‬‬ ‫الخاصية‬ ‫‪OnServerValidate‬‬

‫الشرح‬ ‫اسم العملية بلغة ‪C#‬‬

‫مثال‬ ‫=‪OnServerValidate‬‬ ‫"‪"IsPositiveEven‬‬

‫‪ ClientValidationFunction‬اسم العملية بلغة ‪ClientValidationFunction JavaScript‬‬ ‫"‪="IsPositiveEven‬‬ ‫‪ValidateEmptyText‬‬

‫هل يجب تفعيل عملية التأكد من "‪ValidateEmptyText="true‬‬ ‫صحة المحتوى اذا كان‬ ‫المحتوى فارغا؟‬

‫مثال‬ ‫نريد بناء صفحة تطلب من المستخدم ادخال عدد صحيح موجب وزوجي‪.‬‬

‫سنكتب أوال عملية بلغة ‪ C#‬تتحقق من صحة االدخال‪.‬‬ ‫لتحديد عملية الـ ‪ OnServerValidate‬نقوم بالخطوات التالية‪:‬‬ ‫•‬

‫نفتح صفحة التنسيق بعرض الـ ‪Design‬‬

‫•‬

‫نضع الفارة على أداة التحقق من صحة اإلدخال ثم نضغط على الزر األيمن للفارة ونختار ‪Properties‬‬

‫•‬

‫ثم نضغط على زر األحداث ‪Events‬‬

‫•‬

‫ثم نختار الخاصيّة ‪ ServerValidate‬ونضغط إلى يمينها ضغتين )‪ (Double Click‬لنحصل على العملية‪.‬‬

‫‪118‬‬


‫ثم ننتقل إلى برمجة العملية‬ protected void cvEvenNumber_ServerValidate(object source, ServerValidateEventArgs args) { // ‫هل القيمة فارغة؟‬ if (args.Value == "") { // ‫اذا كانت القيمة فارغة فهي غير صالحة‬ ‫ التابعة للنوع‬TryParse ‫العملية‬ args.IsValid = false; } ‫ تتلقى‬int else ‫ تحاول‬args.Value ‫قيمة هي‬ { int Number; ‫ اذا‬.‫تحويلها الى عدد صحيح‬ bool IsNumber = int.TryParse(args.Value, out Number); ‫ حينها تعيد القيمة بواسطة‬،‫نجحت‬ if (IsNumber && Number >= 0 && Number % 2 == 0) { Number ‫بارامتر االخراج‬ args.IsValid = true; ‫ خالف ذلك ترجع‬.true ‫وترجع‬ } else .false { // ‫القيمة تكون غير صالحة اذا لم تكن عددا أو كانت عددا سالبا أو فرديا‬ args.IsValid = false; } } .Save ‫ يتم فقط عند الضغط على الزر‬CustomValidator ‫الحظ أن تفعيل األداة‬

119


:‫كود التنسيق‬ <form id="form1" runat="server"> <div> <table> <tr> <td style="font-size:x-large">Please enter a positive even number</td> <td> <asp:TextBox ID="TextBoxEvenNumber" runat="server"></asp:TextBox> <asp:CustomValidator ID="cvEvenNumber" runat="server" ErrorMessage="*" ForeColor="Red" ControlToValidate="TextBoxEvenNumber" ValidateEmptyText="true" Display="Dynamic" Text="Input must be a positive even number!" onservervalidate="cvEvenNumber_ServerValidate"> </asp:CustomValidator> </td> </tr> <tr> <td colspan="2"> <asp:Button ID="ButtonSave" Font-Size="X-Large" runat="server" Text="Save" /> </td> </tr> <tr> <td colspan="2"> <asp:Label ID="LabelMessage" ForeColor="Green" Font-Size="X-Large" runat="server"

120


‫>""=‪Text‬‬ ‫>‪</asp:Label‬‬ ‫>‪</td‬‬ ‫>‪</tr‬‬ ‫>‪</table‬‬ ‫>‪</div‬‬ ‫>‪</form‬‬ ‫واآلن سنكتب العملية بلغة ‪ JavaScript‬للتحقق من صحة االدخال‪.‬‬ ‫لتحديد عملية الـ ‪ ClientValidationFunction‬نقوم بالخطوات التالية‪:‬‬ ‫•‬

‫نضيف وسم >‪ <script‬جديد داخل الوسم >‪<head‬‬

‫•‬

‫ثم نقوم ببرمجة العملية‬

‫•‬

‫ثم نعطي اسم العملية كقيمة للخاصيّة ‪ClientValidationFunction‬‬ ‫>"‪<script type="text/javascript" language="javascript‬‬ ‫)‪function IsEvenNumber(source, args‬‬ ‫{‬ ‫)"" == ‪if (args.Value‬‬ ‫{‬ ‫;‪args.IsValid = false‬‬ ‫}‬ ‫‪else‬‬ ‫{‬ ‫)‪if (args.Value % 2 == 0‬‬ ‫{‬ ‫;‪args.IsValid = true‬‬ ‫}‬ ‫‪else‬‬ ‫{‬ ‫;‪args.IsValid = false‬‬ ‫}‬ ‫}‬ ‫}‬ ‫>‪</script‬‬

‫الحظ أن تفعيل األداة ‪ CustomValidator‬في هذه الحالة يتم بمجرد تغيير محتوى صندوق النص وأن الصفحة‬ ‫ال تمر بعملية تحميل من جديد وال يحصل أي تواصل مع الخادم ألن الكود يُنفذ على حاسوب الزبون‪.‬‬

‫‪121‬‬


‫‪Page.IsValid‬‬ ‫لقد ذكرنا بأن أدوات التحقق من صحة اإلدخال تحتوي على كود ‪ Script‬أو للدقة ‪ JavaScript‬يُنفذ على حاسوب‬ ‫الزبون‪ .‬لكن ماذا لو منع الزبون تنفيذ أي ‪ Scripting Code‬على حاسوبه من خالل الغاء السماح بذلك في‬ ‫المتصفح (أي في الـ ‪ Internet Explorer‬أو الـ ‪ Chrome‬أو أي متصفح آخر)؟‬ ‫مثال يمكن منع ذلك في الـ ‪ Internet Explorer‬من خالل الخطوات التالية‪:‬‬

‫‪122‬‬


‫الحقيقة أن األدوات ُتفعّل كما يجب لكن ال يتم منع ارسال البيانات الى الخادم حتى ولو كانت غير صالحة‪ .‬ما العمل‬ ‫ًاذا؟ لحسن الحظ فان ‪ ASP.NET‬توفر الخاصيّة البوليانية ‪ Page.IsValid‬التي تكون قيمتها ‪ false‬اذا لم تكن‬ ‫البيانات صالحة‪ .‬وخالف ذلك تكون قيمتها ‪ .true‬ولذلك ما يجب فعله في كل األحوال هو فحص هذه الخاصيّة في‬ ‫معالجات األحداث المناسبة في الـ ‪ .Code Behind‬ولمزيد من االطمئنان يمكننا طلب تفعيل أدوات التحقق من‬ ‫صحة البيانات من خالل استدعاء العملية )(‪.Page.Validate‬‬ ‫مثال‪:‬‬

‫‪123‬‬


protected void ButtonSave_Click(object sender, EventArgs e) { Page.Validate(); if (Page.IsValid) LabelMessage.Text = "Successfully Saved."; else { LabelMessage.ForeColor = System.Drawing.Color.Red; LabelMessage.Text = "Failed to save data."; }} ‫تمارين‬ ‫المطلوب القيام ببرمجة األمثلة المشروحة أعاله‬

124


‫‪SQL‬‬ ‫‪Structured Query Language‬‬ ‫لغة االستعالم الهيكلية‬ ‫واسع ج ًدا للوصول للمعلومات (البيانات) المُخزنة في قواعد البيانات‬ ‫نطاق‬ ‫لغة ‪ SQL‬هي لغة مُستخدمة على‬ ‫ٍ‬ ‫ٍ‬ ‫االرتباطية أو العالئقية )‪ (Relational Databases‬أي التي ُتخزن المعلومات في جداول تربط بينها عالقات‬ ‫ُ‬ ‫وتحديثها وحذفُها‪ .‬هذه األمثلة من العمليات هي على سبيل‬ ‫معينة‪ .‬والمقصود بالوصول للمعلومات أي استخراجُها‬ ‫المثال ال الحصر‪ .‬وهي ُتعطي تفسيرً ا لسبب تسمية هذه اللغة بهذا االسم‪ .‬إال أن هذه التسمية مغلوطة نوعً ا ما ألن‬ ‫اللغة قادرة على أكثر من ذلك فهي قادرة على إنشاء وحذف مكونات كائنات قاعدة البيانات من جداول وحقول داخل‬ ‫هذه الجداول وخصائص هذه الحقول كالفهارس أو المفاتيح (‪ )Indexes, Keys‬والعالقات )‪. (Relations‬‬ ‫إضافة إلى ذلك هي قادرة على تنفيذ مهام إدارية مختلفة لدعم العمليات اليومية التي تتم على قاعدة البيانات‪ .‬تم‬ ‫تطوير هذه اللغة من قبل المهندسين ‪ Donal D. Chamberlin‬و ‪ Raymond F. Boyce‬من شركة ‪IBM‬‬ ‫وذلك في عام ‪ .1970‬وكانت آنذاك تسمى بلغة ‪.SEQUEL‬‬ ‫مالحظة‪:‬‬ ‫من أجل القيام بتجربة وتنفيذ األمثلة التالية عليك القيام بالخطوات التالية التي من شأنها فتح محرر أوامر ‪SQL‬‬ ‫التابع لبرنامج أكسس‪:‬‬ ‫• أفتح برنامج أكسس‬ ‫• أنشئ الجداول الالزمة والعالقات التي تربطها‬ ‫• أمأل الجداول بقيم من اختيارك أو بتلك المبينة في األمثلة‬ ‫• اختر بعد ذلك كله الكائن استعالمات (שאילתות) (انظر في الشكل التالي إلى الرقم ‪)1‬‬ ‫• أنقر الزر جديد חדש (انظر في الشكل التالي إلى الرقم ‪)2‬‬ ‫• اختر (יצירת שאילתה בתצוגת עיצוב) (انظر في الشكل التالي إلى الرقم ‪)3‬‬ ‫• اختر في نافذة الحوار التي تظهر(תצוגת עיצוב) ثم انقر الزر אישור (انظر في الشكل التالي إلى الرقم ‪+ 4‬‬ ‫‪)5‬‬ ‫• أغلق نافذة الحوار التي تظهر(הצגת טבלה) من خالل النقر على الزر סגור (انظر في الشكل التالي إلى الرقم‬ ‫‪)6‬‬ ‫• أنقر على الزر األيمن للفارة في المنطقة العليا من النافذة التي تظهر (انظر في الشكل التالي إلى الرقم ‪ )7‬ثم أختر‬ ‫من القائمة التي تظهر األمر תצוגת ‪( SQL‬انظر في الشكل التالي إلى الرقم ‪)8‬‬ ‫• ابدأ بكتابة استعالمك في النافذة التي تظهر (انظر في الشكل التالي إلى الرقم ‪)9‬‬

‫‪125‬‬


‫• لتنفيذ االستعالم أنقر على الزر‬

‫في شريط األدوات (انظر في الشكل التالي إلى الرقم ‪)10‬‬

‫• عند االنتهاء من تنفيذ وفحص االستعالم بإمكانك حفظه من النقر على الزر‬

‫‪126‬‬

‫في شريط األدوات‪.‬‬


127


‫تتكون لغة ‪ SQL‬كأية لغة من‬ ‫• مجموعة من الرموز والكلمات المحجوزة والتي لكل واحد وواحدة منها معنى معين‪.‬‬ ‫• مجموعة من القواعد التي تبين كيفية استعمال الرموز والكلمات المحجوزة لبناء جمل أو أوامر مركبة‪.‬‬ ‫بشكل أساسي إلى عدة أقسام منها‪:‬‬ ‫تنقسم أوامر لغة ‪SQL‬‬ ‫ٍ‬ ‫• االستعالمات )‪ :)Queries‬األمر ‪SELECT‬‬ ‫• لغة معالجة البيانات (‪ :)DML = Data Manipulation Language‬وهي عبارة عن مجموعة من األوامر‬ ‫لتغيير محتوى الجداول في قاعدة البيانات مثل‬ ‫‪ o‬إضافة سجالت جديدة بواسطة األمر ‪INSERT INTO‬‬ ‫‪128‬‬


‫‪ o‬حذف سجالت موجودة في الجداول بواسطة األمر ‪DELETE‬‬ ‫‪ o‬تحديث سجالت موجودة بواسطة األمر ‪UPDATE‬‬ ‫• لغة تعريف البيانات (‪ :)DDL = Data Definition Language‬وهي عبارة عن مجموعة من األوامر‬ ‫لتغيير مبنى قاعدة البيانات من خالل إنشاء وتغيير الكائنات المكونة لقاعدة البيانات مثل الجداول والحقول داخل‬ ‫هذه الجداول وخصائص هذه الحقول كالفهارس (‪ )Indexes‬والعالقات وما إلى ذلك‪ .‬بل توفر ‪ DML‬أيضً ا‬ ‫أوامر إلنشاء قاعدة البيانات نفسها‪ .‬أمثلة على هذه األوامر‪:‬‬ ‫‪ o‬إضافة جدول جديد بواسطة األمر ‪CREATE TABLE‬‬ ‫‪ o‬حذف جدول موجود بواسطة األمر ‪DROP TABLE‬‬ ‫‪ o‬تحديث مبنى جدول موجود بواسطة األمر ‪ALTER TABLE‬‬ ‫‪ o‬إنشاء قاعدة بيانات جديدة بواسطة األمر ‪CREATE DATABASE‬‬ ‫• لغة التحكم بالبيانات (‪ :)DCL = Data Control Language‬وهي عبارة عن مجموعة من األوامر للتحكم‬ ‫بامتيازات المستخدمين للوصول إلى البيانات‪ .‬مثل‬ ‫‪ o‬منح امتيازات معيّنة لمجموعة مستخدمين أو مستخدم موجود بواسطة األمر ‪GRANT‬‬ ‫‪ o‬إبطال امتيازات معيّنة لمجموعة مستخدمين أو مستخدم موجود بواسطة األمر ‪REVOKE‬‬ ‫• لغة التحكم بالمعامالت (‪ :)TCL = Transaction Control Language‬وهي عبارة عن مجموعة من‬ ‫األوامر للتحكم ببداية ونهاية تنفيذ المعامالت‪ .‬والمقصود بالمعامالت سلسلة من التغيرات التي يُطلب تنفيذها‬ ‫على البيانات أو مبنى القاعدة والتي يجب أن تعامل كوحدة واحدة أي إما أن تنفذ جميعها بنجاح أو أن ال تنفذ‬ ‫مطل ًقا)‪ .‬أمثلة على هذا النوع من األوامر‬ ‫‪ o‬البدء بتنفيذ معاملة جديدة بواسطة األمر ‪BEGIN TRANSACTION‬‬ ‫‪ o‬إنهاء معاملة من خالل تنفيذ كافة األعمال التي يتم إجراؤها أثناء المعاملة بواسطة األمر ‪COMMIT‬‬ ‫‪TRANSACTION‬‬ ‫‪ o‬إنهاء معاملة بواسطة التراجع عن كافة األعمال التي يتم إجراؤها أثناء المعاملة بواسطة األمر‬ ‫‪ROLLBACK TRANSACTION‬‬

‫‪129‬‬


‫االستعالمات )‪)Queries‬‬ ‫األمر ‪SELECT‬‬ ‫األمر ‪ SELECT‬بأبسط صوره‬ ‫عند االنتهاء من هذا الدرس عليك أن تكون قادراً على‬ ‫• شرح قدرات األمر ‪SELECT‬‬ ‫• تنفيذ أمر ‪ SELECT‬بسيط‬ ‫الستخراج معلومات من قاعدة البيانات علينا أن نستعمل األمر ‪ SELECT‬التابع للغة ‪ .SQL‬ربما تحتاج أيضا ً‬ ‫إلى تحديد النتائج بناءاً على شروط معينة‪ .‬هذا الدرس يشرح جميع صيغ األمر ‪ SQL‬الالزمة لهذا الغرض‪.‬‬ ‫قدرات األمر ‪:SELECT‬‬ ‫األمر ‪ SELECT‬يسترجع معلومات من قاعدة البيانات ‪ .‬وهو قادر على‪:‬‬ ‫• استخراج اسطرمن جدول تحقق شروطا ً معينة وهو ما يسمى باختيار ‪Selection‬‬ ‫• استخراج أعمدة من جدول تحقق شروطا ً معينة وهو ما يسمى باختيار ‪Projection‬‬ ‫• استخراج معلومات مخزنة في أكثر من جدول تربط بينها عالقات أي يشتركون بيتهم بحقل واحد أو أكثر وهو ما‬ ‫يسمى باالتحاد أو الدمج ‪.Join‬‬ ‫المبنى العام لألمر ‪: SELECT‬‬ ‫يكون األمر ‪ SELECT‬في ابسط صوره مكونا من القسمين )‪:(Clauses‬‬ ‫• ‪ : SELECT‬الذي يحدد الحقول التي نريد إظهار معلوماتها‬ ‫• ‪ :From‬الذي يحدد الجدول (الجداول) التي تحتوي على الحقول التي حُددت في قسم ألـ ‪SELECT‬‬ ‫}…]‪SELECT * | {Column1 [as OtherName1], Column2 [as OtherName2‬‬ ‫;‪FROM table‬‬ ‫األقواس ] [ تعني أن الكلمة أي االستعمال غير ملزم‬ ‫الرمز | يعني أو‬ ‫األقواس } { تعني سلسلة أو قائمة‬

‫‪130‬‬


‫شرح‪ :‬األمر ‪ SELECT‬يبدأ بالكلمة ‪ SELECT‬ثم إما أن يأتي الرمز * إلعادة جميع الحقول أو أن تأتي بعد هذه‬ ‫الكلمة سلسلة من أسماء الحقول طولها على األقل حقل واحد‪ .‬تفصل بين أسماء الحقول فواصل‪ .‬بإمكاننا أن نحدد‬ ‫اسما خاصا لكل حقل يعطى له في جدول النتيجة‪ .‬يجب أن ينتهي هذا األمر بالقسم ‪ FROM‬الذي نحدد فيه اسم‬ ‫الجدول (الجداول) الذي يحوي الحقول‬ ‫مالحظات‪:‬‬ ‫• أوامر ‪ SQL‬ليست حساسة لحالة الحرف‪.‬‬ ‫• ليس بالضرورة أن ينتهي األمر في نفس السطر بل يجوز أن يكون مكتوبا ً في أكثر من سطر وذلك لتسهيل قراءة‬ ‫وفهم األمر‪.‬‬ ‫• ال يجوز قطع أو فصل الكلمات المحجوزة مثل‪.FROM, SELECT :‬‬ ‫• من المفضل كتابة الكلمات المحجوزة بأحرف كبيرة لتفريقها عن غيرها من الكلمات‬ ‫أمثلة‪:‬‬ ‫اختيار جميع أعمدة الجدول ‪:Friends‬‬ ‫;‪SELECT * FROM Friends‬‬ ‫هذا األمر يعيد لنا الجدول التالي الذي يحوي جميع أعمدة الجدول ‪ .Friends‬فالنتيجة تكون متطابقة مع األمر‬ ‫التالي الذي يحدد أسماء جميع الحقول‬ ‫‪SELECT IDFriend, FName, LName, BirthDay, Address, Mobile‬‬ ‫‪;FROM Friends‬‬ ‫الحظ انه يجب الفصل بين أسماء الحقول بالفاصلة‪ .‬ترتيب الحقول هنا يحدد ترتيبهم في جدول النتيجة‪.‬‬

‫اختيار أعمدة جدول محددة‪:‬‬ ‫;‪SELECT FName, LName from Friends‬‬

‫‪131‬‬


‫هذا األمر يعيد لنا الجدول التالي والذي يحوي على الحقلين المطلوبين فقط‪.‬‬

‫اختيار أعمدة محددة من الجدول وإعطاؤها أسماء جديدة في جدول النتيجة‪:‬‬ ‫;‪SELECT FName AS [First Name], LName AS [Last Name] from Friends‬‬ ‫هذا األمر يعيد لنا الجدول التالي والذي يحوي على الحقلين المطلوبين فقط‪ .‬في جدول النتيجة يظهر االسم ‪First‬‬ ‫‪ Name‬بدال من االسم ‪ FName‬واالسم ‪ Last Name‬بدال من االسم ‪.LName‬‬ ‫الحظ أن‬ ‫•‬

‫األسماء الجديدة وُ ضعت بين القوسين ][ ألنها تحتوي على فراغات‬

‫•‬

‫الكلمة ‪ AS‬يجب أن تأتي مباشرة بعد اسم الحقل الذي نريد تغيير اسمه في جدول النتيجة‪.‬‬

‫•‬

‫بإمكاننا إعطاء فقط جزء من الحقول أسماء جديدة‬

‫‪132‬‬


‫حذف المعلومات المتكررة‬ ‫حذف المعلومات المتكررة من جدول النتيجة يتم بمساعدة الكلمة ‪( DISTINCT‬أي مختلف) التي تأتي قبل اسم‬ ‫الحقل‪.‬‬ ‫مثال‪ :‬األمر التالي يعيد لنا جميع األسطر التي تحتوي على عناوين مختلفة‪:‬‬

‫‪SELECT DISTINCT Address‬‬ ‫;‪FROM Friends‬‬

‫الحظ أن العنوان سخنين أُعيد مرة واحدة مع أنه موجود مرتين‪.‬‬

‫‪133‬‬


‫تحديد وترتيب النتائج‬ ‫عند االنتهاء من هذا الدرس عليك أن تكون قادراً على‬ ‫• تحديد األسطر بناء ً على شروط معينة‬ ‫• ترتيب األسطر وفق حقول ٍ معينة‬ ‫عند استخراج معلومات من قاعدة البيانات علينا أحيانا أن نحدد النتائج بناء ً على شروط معينة‪ .‬هذا ما ينجزه‬ ‫بالضبط القسم ‪ WHERE‬في األمر ‪:SQL‬‬ ‫}…]‪SELECT * | {Column1 [as OtherName1], Column2 [as OtherName2‬‬ ‫;‪FROM table WHERE Condition‬‬ ‫بحيث أن ‪ Condition‬يمثل شرطا ً‪ .‬الشرط عبارة عن أي تعبير قيمته النهائية إما صدق )‪ (True‬أو كذب‬ ‫)‪ .(False‬التعبير يقارن عادة قيمتين أو أكثر بواسطة عمليات المقارنة‪ .‬هذه المقارنات قد تكون مرتبطة مع بعضها‬ ‫بواسطة عمليات الربط المنطقي‪ .‬القيم قد تكون قيم حقول أو قيم ثابتة أو سلسلة من القيم (انظر إلى استعمال الكلمة‬ ‫‪ IN‬الحقا ً)‪.‬‬ ‫عمليات المقارنة‪:‬‬ ‫عملية المقارنة‬

‫المعنى‬

‫=‬

‫يساوي‬

‫>‬

‫أكبر من‬

‫=>‬

‫أكبر من أو يساوي‬

‫<‬

‫أصغر من‬

‫=<‬

‫أصغر من أو يساوي‬

‫><‬

‫ال يساوي‬

‫‪BETWEEN x‬‬

‫أكبر أو يساوي ‪ x‬و‬

‫‪AND y‬‬

‫أصغر أو يساوي ‪y‬‬

‫)‪IN (list‬‬

‫يساوي أية قيمة من‬ ‫القائمة‬

‫‪LIKE‬‬

‫يشبه بناء ً على تعبير‬ ‫رسمي‬

‫‪134‬‬


‫يساوي ‪NULL‬‬

‫‪IS NULL‬‬ ‫عمليات الربط‪:‬‬ ‫عملية الربط‬

‫المعنى‬

‫‪X AND Y‬‬

‫تعيد ‪ True‬إذا كانت قيمة ‪ X‬و قيمة ‪True Y‬‬

‫‪X OR Y‬‬

‫تعيد ‪ True‬إذا كانت قيمة ‪ X‬أو قيمة ‪True Y‬‬

‫‪NOT X‬‬

‫تعيد ‪ True‬إذا كانت قيمة ‪False X‬‬

‫مثال‪:‬‬ ‫األمر التالي يعيد لنا تفاصيل جميع األصدقاء من سخنين (أي بشرط أن يكونوا من سخنين)‪:‬‬ ‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;'‪WHERE Address='Sakhnin‬‬

‫الحظ أن‬ ‫الكلمة ‪ Sakhnin‬أحيطت بشطائر فردية ألن الحقل من نوع نص‪ .‬أما بالنسبة للقيم العددية فال تحاط القيم العددية‬ ‫بأية شطائر‪.‬‬ ‫عملية المقارنة غير حساسة لحالة الحرف‪ .‬فلو كتبنا ‪ Sakhnin‬أو ‪ sakhnin‬نحصل على نفس النتيجة (صحيح‬ ‫بالنسبة لـ ‪.)MS Access‬‬ ‫األمر التالي يعيد لنا تفاصيل الصديق الذي رقمه ‪:2‬‬ ‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;‪WHERE IDFriend=2‬‬

‫‪135‬‬


‫األمر التالي يعيد لنا تفاصيل األصدقاء الذين أرقامهم أكبر أو تساوي ‪ 4‬و أصغر أو يساوي ‪:5‬‬

‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;‪WHERE IDFriend BETWEEN 4 AND 5‬‬ ‫نفس النتيجة يعيدها األمر‬ ‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;)‪WHERE IDFriend IN (4, 5‬‬

‫انتبه إلى أن نوع القيم يجب أن يكون من نوع الحقل! فمثال األمر التالي يستعمل حقال من نوع نص ولذلك نصوص‬ ‫كقيم وأحيطت بشطائر فردية‪:‬‬

‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;)'‪WHERE Address IN ('Haifa', 'Sakhnin‬‬

‫‪136‬‬


‫نفس النتيجة يعيدها األمر التالي من خالل استعمال الرابط ‪ OR‬أي يعيد لنا تفاصيل األصدقاء الذين يسكنون في‬ ‫‪ Sakhnin‬أو في ‪:Haifa‬‬ ‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;'‪WHERE Address='Sakhnin' OR Address='Haifa‬‬ ‫األمر التالي يعيد لنا تفاصيل األصدقاء الذين اسمهم األول ‪ Sami‬واسم العائلة ‪:Samra‬‬ ‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;'‪WHERE FName='Sami' AND LName='Samra‬‬

‫األمر التالي يعيد من خالل استعمال الرابط ‪ NOT‬تفاصيل األصدقاء الذين ال يسكنون في ‪ Sakhnin‬وأيضا ليس‬ ‫في ‪:Haifa‬‬

‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;)'‪WHERE Address NOT IN ('Sakhnin','Haifa‬‬

‫‪137‬‬


‫الحقا ً سنرى أن عناصر القائمة قد تكون نتيجة استعالم ‪ SELECT‬داخلي‪.‬‬

‫ترتيب النتائج‬ ‫ترتيب األسطر في جدول النتيجة يكون غير معروف ٍ مسبقا ً‪ .‬القسم ‪ ORDER BY‬التابع لألمر ‪SELECT‬‬ ‫يمكننا من ترتيب األسطر في جدول النتيجة‪ .‬هذا القسم يجب أن يكون القسم األخير في أمر الـ ‪ .SELECT‬األمر‬ ‫يوفر لنا إمكانيتين للترتيب‪ (Ascending) ASC .‬للترتيب التصاعدي و ‪ (Descending) DESC‬للترتيب‬ ‫التنازلي‪ .‬الترتيب التصاعدي هو الترتيب االفتراضي‪ .‬وعليه يكون األمر ‪ SELECT‬بصورته األشمل كالتالي‪:‬‬

‫}…]‪SELECT * | {Column1 [as OtherName1], Column2 [as OtherName2‬‬ ‫| ‪FROM table [WHERE Condition] ORDER BY {Column1, Column2 ,…} [ASC‬‬ ‫;]‪DESC‬‬ ‫األمر التالي يعيد لنا جميع األصدقاء مرتبين حسب تاريخ ميالدهم من األصغر إلى األكبر‪:‬‬

‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;‪ORDER BY BirthDay DESC‬‬

‫‪138‬‬


‫الترتيب قد يكون حسب أكثر من حقل‪ .‬مثال ً األمر التالي يعيد لنا جميع األصدقاء مرتبين حسب تاريخ ميالدهم من‬ ‫األصغر إلى األكبر وأيضا ً حسب أسماء العائلة مرتبة ترتيبا ً أبجديا ً تنازليا ً‪:‬‬

‫* ‪SELECT‬‬ ‫‪FROM Friends‬‬ ‫;‪ORDER BY LName DESC , BirthDay DESC‬‬

‫استخراج معلومات من أكثر من جدول‬ ‫من الصعب ج ًدا أن نجد أو أن نصمم قاعدة بيانات تخزن المعلومات في جداول منفصلة عن بعضها البعض‪ .‬وإنما‬ ‫من الطبيعي أن نجد القاعدة مكونة من جداول عدة تربط بين معظمها عالقات‪ .‬ولذلك سنتعلم اآلن كيفية صياغة‬ ‫أوامر ‪ SQL‬الستخراج المعلومات من أكثر من جدول تربط بينها عالقات مختلفة‪ .‬هذه العالقات تنشأ عاد ًة من‬ ‫خالل حقول مفاتيح مشتركة بين هذه الجداول‪ .‬من المعروف أن العالقات الممكنة ثالث‪:‬‬ ‫• واحد لواحد )‪)1  1‬‬ ‫• واحد لكثير )‪)1  N‬‬ ‫• كثير لكثير )‪)M  N‬‬ ‫لنأخذ على سبيل المثال الجدولين التاليين (جدول الدول وجدول العواصم) والذين تربط بينهم عالقة واحد لواحد‬ ‫وذلك من خالل اشتراكهما في الحقل ‪ .IDCapital‬هذا الحقل يكون في الجدول ‪ Capitals‬مفتاحً ا رئيسيًا أي‬ ‫‪ Primary Key‬وفي الجدول الثاني أي ‪ Countries‬يكون مفتاحً ا غريبًا أي ‪Foreign Key‬‬

‫‪139‬‬


140


‫اآلن نريد كتابة استعالم يعيد لنا اسم الدول واسم عاصمتها وعدد سكان العاصمة‪ .‬أي يعيد لنا الجدول التالي‪:‬‬

‫‪141‬‬


‫الحظ أن المعلومات المطلوبة موجودة في جدولين‪ .‬ولذلك علينا أن نعيد األسطر التي تشترك في قيمة الحقل الرابط‬ ‫‪ .IDCapital‬الشكل التالي يوضح ذلك‪:‬‬

‫األمر التالي يعيد لنا النتيجة المطلوبة‪:‬‬

‫‪SELECT Countries.Name, Capitals.Name, Capitals.Inhabitants‬‬ ‫‪FROM Capitals, Countries‬‬ ‫;‪WHERE Capitals.IDCapital=Countries.IDCapital‬‬ ‫الحظ أننا كتبنا قبل كل اسم حقل اسم الجدول الذي يحتويه وذلك للحيلولة دون حصول تعارض بين أسماء الحقول‬ ‫المتساوية‪.‬‬ ‫اإلمكانية الثانية للحصول على هذه النتيجة تكون من خالل استعمال األمر ‪ INNER JOIN‬والذي يعني دمج‬ ‫قيم مشتركةٍ)‪:‬‬ ‫جدولين في جدول نتيج ٍة واح ٍد بنا ًء على حقل مشترك (أي ٍ‬

‫‪142‬‬


‫‪SELECT Countries.Name, Capitals.Name, Capitals.Inhabitants‬‬ ‫‪FROM Capitals‬‬ ‫‪INNER JOIN Countries‬‬ ‫;‪ON Capitals.IDCapital = Countries.IDCapital‬‬

‫أمر االتحاد ‪ INNER JOIN‬يقوم بدمج السجالت أي األسطر من جدولين أينما وجدت قيم متطابقة في حقل‬ ‫مشترك‪ .‬السجالت التي ال تحقق الشرط المذكور في األمر وراء الكلمة ‪ ON‬ال ُتعاد‪ .‬الحظ أن األمر ‪INNER‬‬ ‫‪ JOIN‬يستعمل الكلمة ‪ ON‬بدالً من الكلمة ‪.WHERE‬‬ ‫المبنى العام لألمر‬ ‫‪SELECT columns‬‬ ‫‪FROM table1‬‬ ‫‪INNER JOIN table2‬‬ ‫‪ON table1.field1 compopr table2.field2‬‬ ‫بحيث أن‪:‬‬ ‫‪ table1‬و ‪ table2‬أسماء الجداول التي يتم تجميع السجالت منها‪.‬‬ ‫‪ field1‬و ‪ field2‬أسماء الحقول المتصلة‪.‬‬ ‫‪ compopr‬أية عملية مقارنة منطقية (أي ><‪.)=, <,<=,>,>=,‬‬ ‫مثال لعالقة ‪: 1  N‬‬ ‫الشكل التالي يبين الجدول طالب ‪ Students‬والجدول صفوف ‪ Classes‬ويبين أن العالقة بينهم هي عالقة واحد‬ ‫لكثير‪ .‬ففي الصف الواحد يتعلم كثير من الطالب‪ .‬الحقل الواصل هو الحقل ‪.IDClass‬‬

‫‪143‬‬


‫نريد كتابة أمر دمج يعيد لنا الجدول التالي الذي يعرض بعض تفاصيل الطالب الموجودة في الجدول ‪Students‬‬ ‫وبعض التفاصيل عن صفه الموجودة في الجدول ‪:Classes‬‬

‫األمر التالي يستعين باألمر ‪ INNER JOIN‬إلعادة المعلومات المطلوبة‪:‬‬

‫‪SELECT Students.FirstName, Students.LastName, Students.DayOfBirth,‬‬ ‫‪Classes.Name AS Class, Classes.Room‬‬ ‫‪FROM Classes‬‬ ‫‪INNER JOIN Students‬‬ ‫;‪ON Classes.IDClass = Students.IDClass‬‬ ‫أمر االتحاد ‪ LEFT JOIN‬يعيد جميع السجالت الموجودة في الجدول األيسر (أي ‪ )table1‬حتى لو لم تحقق‬ ‫الشرط المذكور في األمر وراء الكلمة ‪ ON‬أي حتى ولو لم يجد سجالت مناسبة في الجدول األيمن (أي ‪.)table2‬‬ ‫المبنى العام لألمر‬ ‫‪SELECT columns‬‬ ‫‪FROM table1‬‬ ‫‪LEFT JOIN table2‬‬ ‫‪ON table1.field1 compopr table2.field2‬‬

‫‪144‬‬


‫مثالً لدينا الجدول رسائل ‪ Messages‬الذي تربطه عالقة واحد لكثير مع جدول الطالب المذكور أعاله‪ .‬كل طالب‬ ‫يستطيع كتاب ة رسائل كثيرة في منتدى المدرسة على سبيل المثال‪ .‬إذا دمجنا الجدولين بواسطة األمر ‪INNER‬‬ ‫‪ JOIN‬فهذا سيعيد لنا تفاصيل الطالب الذين كتبوا رسائل وتفاصيل الرسائل التي كتبوها‪ .‬أي أن تفاصيل الطالب‬ ‫الذين لم يشاركوا في المنتدى سوف ال تعاد‪ .‬كيف نستطيع الحصول أيضً ا على تفاصيل الطالب الذين لم يشاركوا في‬ ‫المنتدى؟ هذا هو بالضبط ما يقوم به األمر ‪ LEFT JOIN‬الذي يعيد تفاصيل الطالب حتى ولو لم يجد سجالت‬ ‫مناسبة في جدول الرسائل‪:‬‬

‫محتوى الجدولين‪:‬‬

‫‪145‬‬


:‫ التالي‬INNER JOIN ‫األمر‬ SELECT Students.FirstName, Students.LastName, Messages.Message FROM Students INNER JOIN Messages ON Students.IDStudent = Messages.IDStudent; :‫يعيد لنا الجدول التالي الذي يحتوي على تفاصيل الطالب الذين شاركوا في المنتدى‬

‫ التالي‬LEFT JOIN ‫بينما األمر‬

SELECT Students.FirstName, Students.LastName, Messages.Message FROM Students LEFT JOIN Messages ON Students.IDStudent=Messages.IDStudent; :‫سيعيد لنا تفاصيل الجميع أي تفاصيل الذين شاركوا والذين لم يشاركوا‬

146


‫الحظ أن األمر أعاد لنا السجالت من الجدول األيسر مع أنه لم يجد سجالت تحقق الشرط في الجدول األيمن‪.‬‬ ‫مالحظة‪:‬‬ ‫األمر ‪ LEFT JOIN‬يسمى أي ً‬ ‫ضا ‪ LEFT OUTER JOIN‬وبإمكاننا كتابة‬ ‫‪ LEFT OUTER JOIN‬بدالً من ‪ LEFT JOIN‬ونحصل على نفس النتيجة‪.‬‬

‫األمر ‪RIGHT JOIN‬‬ ‫من الممكن أن نسأل السؤال التالي‪ :‬هل يوجد مشاركات في المنتدى ألناس ليسوا طالبًا في المدرسة؟ من الواضح أن‬ ‫وجود مثل هذه السجالت يدل على خطأ في تصميم قاعدة البيانات‪ .‬ألن وجودها يعني استعمال مفتاح غريب في‬ ‫ضا‬ ‫جدول الرسائل ال أصل له في جدول الطالب‪ .‬مع أن الخطوة التي نعرف فيها العالقات بين الجداول تفترض أي َ‬ ‫أن نضمن مبدأ ما يسمى بالتكامل المرجعي (‪ .)Referential Integrity‬هذا المبدأ يُطلب من مدير قاعدة البيانات‬ ‫)‪ (DBMS: Data Base Management System‬ويضمن‪:‬‬ ‫• عدم السماح بإدخال قيم لمفتاح غريب غير موجودة كقيم للمفتاح الرئيسي‪.‬‬ ‫• إذا حُذف سجل أب ُتحذف كل سجالت االبن المرتبطة به وذلك لكي ال تبقى سجالت غير مرتبطة بشيء في‬ ‫الجدول األصلي‪.‬‬

‫‪147‬‬


‫مع ذلك لو حصل الخطأ كيف يمكننا أن نحصل على هذه السجالت؟ هذا هو بالضبط ما يقوم به األمر ‪RIGHT‬‬ ‫‪ .JOIN‬فهو يعيد جميع السجالت الموجودة في الجدول األيمن (أي ‪ )table2‬حتى لو لم تحقق الشرط المذكور في‬ ‫األمر وراء الكلمة ‪ ON‬أي حتى ولو لم يجد سجالت مناسبة في الجدول األيسر (أي ‪.)table1‬‬ ‫أناس غير‬ ‫من أجل إعطاء مثال قمت بإيقاف فحص التكامل المرجعي في قاعدة البيانات وأدخلت رسائل من‬ ‫ٍ‬ ‫موجودين في جدول الطالب ومن ثم نفذت األمر التالي‪:‬‬

‫‪SELECT Students.FirstName, Students.LastName, Messages.Message‬‬ ‫‪FROM Students‬‬ ‫‪RIGHT JOIN Messages‬‬ ‫;‪ON Students.IDStudent=Messages.IDStudent‬‬ ‫وحصلت على الجدول التالي الذي يعرض أي ً‬ ‫ضا رسالة كاتبها غير موجود في جدول الطالب‪:‬‬

‫مثال لعالقة ‪: M  N‬‬ ‫الشكل التالي يبين الجدول طالب( ‪ ( Students‬والجدول دروس ( ‪ ( Lessos‬ويبين أن العالقة بينهم هي عالقة‬ ‫كثير لكثير‪ .‬فكل طالب يتعلم أكثر من درس وكل درس يتعلمه أكثر من طالب‪ .‬معروف أن هذه العالقة ُتصمم‬ ‫ث ( ‪ُ ) LessonsStudents‬تحفظ فيه المفاتيح الرئيسية كمفاتيح غريبة (اندكسات) مع إمكانية‬ ‫جدول ثال ٍ‬ ‫بمساعدة‬ ‫ٍ‬ ‫التكرار( ‪.)IDLesson, IDStudent‬‬

‫‪148‬‬


‫السؤال اآلن هو كيف يمكننا أن نستخرج المعلومات من هذه الجداول الثالث لنحصل على تفاصيل الطالب وتفاصيل‬ ‫الدروس التي يدرسونها؟ أي أن جدول النتيجة يجب أن يعرض من يدرس ماذا كالتالي‪:‬‬

‫من الجداول التالية‪:‬‬

‫‪149‬‬


:‫ بشكل متداخل‬INNER JOIN ‫األمر الذي يعيد لنا المعلومات وفق النسق المطلوب هو أمر مركب يستعمل األمر‬

SELECT Students.FirstName, Students.LastName, Lessons.Name FROM Students INNER JOIN (Lessons INNER JOIN LessonsStudents ON Lessons.IDLesson = LessonsStudents.IDLesson) ON Students.IDStudent = LessonsStudents.IDStudent; ‫األمر الداخلي‬ Lessons INNER JOIN LessonsStudents ON Lessons.IDLesson = LessonsStudents.IDLesson

150


‫يعيد لنا نتيجة دمج الجدول ‪ Lessons‬مع جدول الربط ‪ LessonsStudents‬بنا ًء على الحقل المشترك‬ ‫‪ .IDLesson‬بعد ذلك نقوم بدمج هذه النتيجة مع الجدول ‪ Students‬بنا ًء على الحقل المشترك ‪.IDStudent‬‬ ‫مثال على أوامر ‪ SELECT‬متداخلة‬ ‫أكتب استعالمًا يعيد قائمة بتفاصيل الدروس التي ال يدرسها أحد‪.‬‬ ‫واضح أنه علينا أن نبحث داخل الجدول ‪Lessons‬عن مفاتيح الدروس الموجودة فيه ولكنها ال تظهر في الجدول‬ ‫‪ .LessonsStudents‬ألنها لو كانت ُتدرس من قبل أي طالب لوجب ظهور رقم الدرس في جدول الربط‬ ‫‪ُ .LessonsStudents‬ننجز ذلك من خالل إعادة جميع مفاتيح الدروس المختلفة الموجودة في الجدول‬ ‫‪ LessonsStudents‬بواسطة أمر ‪ SQL‬داخلي يعيد لنا قائمة بهذه األرقام‪:‬‬

‫‪SELECT DISTINCT IDLesson FROM LessonsStudents‬‬ ‫ثم نقوم بواسطة أمر ‪ SQL‬خارجي باستخراج تفاصيل الدروس التي ال تظهر أرقامها في القائمة أنفة الذكر وذلك‬ ‫بمساعدة األمر‪ IN‬مع عملية النفي ‪:NOT‬‬ ‫* ‪SELECT‬‬ ‫‪FROM Lessons‬‬ ‫‪WHERE IDLesson NOT IN (SELECT DISTINCT IDLesson FROM‬‬ ‫;)‪LessonsStudents‬‬

‫تمارين‬ ‫تمرين رقم ‪1‬‬ ‫أكتب استعالمًا يعيد قائمة بأسماء الدروس التي ال يدرسها طالب معين‪.‬‬ ‫األمر ‪SELECT INTO‬‬ ‫ُ‬ ‫وحفظها في جدول جديد يتم إنشاؤه من‬ ‫يُستعمل األمر ‪ SELECT INTO‬الستخراج معلومات من جدول موجود‬ ‫قبل األمر ‪ .SELECT INTO‬إذا كان الجدول الجديد موجو ًدا حينها يتم حذفه ومن ثم إنشاؤه من جديد‪ .‬يُستعمل‬ ‫هذا األمر عاد ًة إلنشاء ُنسخ عن الجداول وهو ما يُسمى باألرشيفات‪.‬‬ ‫المبنى العام لألمر‪:‬‬

‫‪151‬‬


‫‪SELECT * | Columns Names‬‬ ‫]‪INTO New_Table_Name [IN ExternalDataBase‬‬ ‫‪FROM Old_TableName‬‬ ‫يبين المبنى العام لألمر أنه من الممكن إنشاء الجدول الجديد في قاعدة بيانات خارجية‪ .‬يُفترض في القاعدة الخارجية‬ ‫أن تكون موجودة وعدم وجودها يحدث ً‬ ‫خطأ‪ .‬إضافة إلى ذلك ال بد من كتابة المسار المطلق لملف القاعدة الخارجية‪.‬‬ ‫مثال‪:‬‬ ‫األمر التالي ينسخ الجدول ‪ Lessons‬إلى القاعدة الخارجية ‪ D:\SQL\DBArchive.mdb‬ويعطيه هناك نفس‬ ‫االسم‪:‬‬ ‫* ‪SELECT‬‬ ‫'‪INTO Lessons IN 'D:\SQL\DBArchive.mdb‬‬ ‫‪FROM Lessons‬‬ ‫مثال‪:‬‬ ‫األمر التالي ينسخ الجدول ‪ Lessons‬إلى نفس القاعدة لكن يعطيه االسم الجديد ‪:Lessons_BackUp‬‬ ‫* ‪SELECT‬‬ ‫‪INTO Lessons_BackUp‬‬ ‫‪FROM Lessons‬‬ ‫انتبه إلى أنه ال يجوز وجود أكثر من جدول بنفس االسم في نفس قاعدة البيانات‪ .‬من هنا وجب إعطاء الجدول اسمًا‬ ‫ً‬ ‫جديدا مختل ًفا عن االسم الموجود‪.‬‬

‫‪152‬‬


‫تمرين رقم ‪2‬‬ ‫صمم قاعدة بيانات باسم ‪ Courses.mdb‬إلدارة معلومات حول التسجيل لدورات استكمال فيه الجداول التالية‪:‬‬

‫العالقات‬ ‫• بإمكان كل طالب أن يتسجل ألكثر من دورة‬ ‫• الدورة الواحدة يتسجل لها أكثر من طالب‬ ‫الجداول‬ ‫• الجدول ‪ Admins‬يحتوي على كلمات المرور وأسماء المستخدمين لمديري موقع التسجيل للدورات‬ ‫• الجدول ‪ Announcements‬يحتوي على آخر األخبار‬ ‫المطلوب‬ ‫▪ أعد تصميم الجدولين ‪ Students‬و ‪ Courses‬بحيث تحقق العالقات التي يجب أن تكون بينهم‬ ‫▪ أمأل جداول قاعدة البيانات بقيم من اختيارك‪ .‬حاول إدخال بعض القيم بواسطة األمر ‪INSERT INTO‬‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع الطالب ولكل طالب تفاصيل الدورات ال ُمسجل فيها‪.‬‬ ‫▪ أكتب استعالما يعيد لطالب معين تفاصيل الدورات المُسجل فيها‪ .‬االستعالم يطلب ‪ .ID‬الطالب عند تشغيل‬ ‫االستعالم‪.‬‬ ‫▪ أكتب استعالما يعيد لدورة معينة تفاصيل الطالب المُسجلين فيها‪.‬‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع الطالب الذين لم يسجلوا ألية دورة‪.‬‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع األطباء الذين ال يعالجون مرضى بعد أي (الذين ال مرضى لهم)‬

‫‪153‬‬


‫تمرين رقم ‪3‬‬ ‫صمم قاعدة بيانات باسم ‪ Hospital.mdb‬إلدارة معلومات في مستشفى وفق المخطط التالي‪:‬‬

‫المطلوب‬ ‫▪ أمأل جداول قاعدة البيانات ‪ Hospital.mdb‬بقيم من اختيارك‪ .‬حاول إدخال بعض القيم بواسطة األمر‬ ‫‪INSERT INTO‬‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع المرضى ولكل مريض تفاصيل األطباء المعالجين‬ ‫▪ أكتب استعالما يعيد لمريض معين تفاصيل ألطباء المعالجين‪ .‬االستعالم يطلب ‪ ID‬المريض عند تشغيل‬ ‫االستعالم‪.‬‬ ‫▪ أكتب استعالما يعيد لطبيب معين تفاصيل المرضى الذين يعالجهم‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع المرضى الذين ال يعالجون بعد (أي الذين ال طبيب لهم)‬ ‫▪ أكتب استعالما يطبع تفاصيل جميع األطباء الذين ال يعالجون مرضى بعد أي (الذين ال مرضى لهم)‬

‫‪154‬‬


‫اإلدخال‬ ‫يتم إدخال سجالت جديدة إلى جدول ما بواسطة األمر ‪INSERT INTO‬‬ ‫الصيغة العامة لألمر هي كالتالي‪:‬‬ ‫(… ‪INSERT INTO TableName )Column1, Column2,‬‬ ‫;) … ‪VALUES (ValueOfColumn1, ValueOfColumn2,‬‬ ‫أي‬ ‫)قائمة بأسماء الحقول التي نريد إدخال قيم إليها( ‪INSERT INTO TableName‬‬ ‫;)قائمة بقيم الحقول التي نريد إدخالها( ‪VALUES‬‬

‫•‬

‫يجب إحاطة قيمة الحقول التي من نوع نص بشطائر منفردة‪.‬‬

‫•‬

‫يجب إحاطة قيمة الحقول التي من نوع تاريخ ‪ /‬وقت بالرمز ‪.#‬‬

‫•‬

‫إذا وجد في الجدول حقل من نوع ‪ Auto Number‬ال يجوز لنا أن نبقي قائمة أسماء الحقول فارغة‪ ،‬بل‬ ‫يجب كتابة الحقول التي نريد إدخال قيم إليها باستثناء حقل الـ ‪( Auto Number‬أي ال نكتبه ألن الـ ‪DBMS‬‬ ‫مسئول عن إعطاء قيم له)‪ .‬أما في الجدول الذي ال يحتوي على حقل من نوع ‪ Auto Number‬فبإمكاننا أن‬ ‫نترك قائمة أسماء الحقول فارغة وتزويد قيم لكل الحقول‪.‬‬ ‫أمثلة‪ :‬معطاة قاعدة البيانات من الفعالية رقم ‪ .2‬نريد إدخال سجالت جديدة إلى الجدول ‪:Courses‬‬

‫الحل‪:‬‬ ‫)]‪INSERT INTO Courses ([Name], [Hours], [Location], [BeginDate], [EndDate‬‬ ‫)‪VALUES(‘VB’,80,’college’,#1/9/12#,#10/10/12#‬‬

‫‪155‬‬


‫ من المفضل إحاطة جميع أسماء الحقول باألقواس ] [ لمنع أي تعارض مع أي أسماء محجوزة بلغة‬:‫مالحظة‬ .SQL ‫أمثلة‬ :‫ كالتالي‬Courses ‫ليكن محتوى الجدول‬

CourseID 1 2 3 4 5

Code 100 102 104 106 108

Date 20/12/2011 30/12/2011 03/12/2011 11/11/2011 22/09/2010

Category CS CS En Ar CS

Title C# Java English Level 1 Arabic For Beginners SQL Basics

Hours 112 112 56 112 112

:‫أكتب استعالما يضيف سجال جديدا بالمعطيات التالية‬

CourseID 6

Code 110

Date 02/09/2010

Category CS

Title MS Access

Hours 112

‫الحل‬ INSERT INTO Courses ([Code], [Date], [Category], [Title], [Hours]) VALUES (110, #02/09/2010#, 'CS', 'MS Access', 112)

:‫بعد تنفيذ األمر يصبح محتوى الجدول كالتالي‬ CourseID 1 2 3

Code 100 102 104

Date 20/12/2011 30/12/2011 03/12/2011

Category CS CS En

156

Title C# Java English Level 1

Hours 112 112 56


‫‪112‬‬ ‫‪112‬‬ ‫‪112‬‬

‫‪Arabic For Beginners‬‬ ‫‪SQL Basics‬‬ ‫‪MS Access‬‬

‫‪Ar‬‬ ‫‪CS‬‬ ‫‪CS‬‬

‫‪11/11/2011‬‬ ‫‪22/09/2010‬‬ ‫‪02/09/2010‬‬

‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫‪106‬‬ ‫‪108‬‬ ‫‪110‬‬

‫الحظ أن القيم ‪ 6‬أُعطيت للحل ‪ CourseID‬من قبل ‪ Access‬وليس من قبلنا ألن الحقل من نوع ‪Auto‬‬ ‫‪.Number‬‬

‫التحديث‬ ‫تتم حتلنة سجالت موجودة في جدول ما بواسطة األمر ‪UPDATE‬‬ ‫الصيغة العامة لألمر هي كالتالي‪:‬‬ ‫‪UPDATE TableName SET {Column1=Value1, Column2= Value2, …} [WHERE‬‬ ‫]‪Condition‬‬ ‫أي قم بتعديل الجدول المعطى اسمه واجعل قيم الحقول الموجودة في القائمة بحسب القيم المعطاة‪.‬‬ ‫•‬

‫األقواس ] [ تعني أن استعمال الجزء الذي بين األقواس غير ملزم‪ ،‬أي أن كتابة الشرط غير ملزمة‪ .‬الحظ أن‬ ‫استعمال األمر ‪ UPDATE‬بدون شرط سيؤدي إلى تغيير جميع السجالت الموجودة في الجدول‪.‬‬

‫•‬

‫األقواس } { تعني سلسلة أو قائمة غير فارغة أي فيها على األقل زوج واحد مكون من اسم الحقل وقيمته يفصل‬ ‫بينهم الرمز =‪.‬‬

‫أمثلة‬ ‫ليكن محتوى الجدول ‪ Courses‬كالتالي‪:‬‬

‫‪Hours‬‬ ‫‪112‬‬ ‫‪112‬‬ ‫‪56‬‬ ‫‪112‬‬ ‫‪112‬‬ ‫‪112‬‬

‫‪Title‬‬ ‫‪C#‬‬ ‫‪Java‬‬ ‫‪English Level 1‬‬ ‫‪Arabic For Beginners‬‬ ‫‪SQL Basics‬‬ ‫‪MS Access‬‬

‫‪Category‬‬ ‫‪CS‬‬ ‫‪CS‬‬ ‫‪En‬‬ ‫‪Ar‬‬ ‫‪CS‬‬ ‫‪CS‬‬

‫‪157‬‬

‫‪Date‬‬ ‫‪20/12/2011‬‬ ‫‪30/12/2011‬‬ ‫‪03/12/2011‬‬ ‫‪11/11/2011‬‬ ‫‪22/09/2010‬‬ ‫‪02/09/2010‬‬

‫‪Code‬‬ ‫‪100‬‬ ‫‪102‬‬ ‫‪104‬‬ ‫‪106‬‬ ‫‪108‬‬ ‫‪110‬‬

‫‪CourseID‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬


:56 ‫ بدال من‬112 ‫) بحيث يجعل عدد الساعات‬CourseID=3 ‫ (أي‬3 ‫أكتب استعالما يحتلن السجل الذي رقمه‬ ‫الحل‬ UPDATE Courses SET [Hours]=112 WHERE CourseID=3

:‫بعد تنفيذ األمر يصبح محتوى الجدول كالتالي‬

CourseID 1 2 3 4 5 6

Code 100 102 104 106 108 110

Date 20/12/2011 30/12/2011 03/12/2011 11/11/2011 22/09/2010 02/09/2010

Category CS CS En Ar CS CS

Title C# Java English Level 1 Arabic For Beginners SQL Basics MS Access

Hours 112 112 112 112 112 112

112 ‫ بدال من‬56 ‫) بحيث يجعل عدد الساعات‬CourseID=4 ‫ (أي‬4 ‫أكتب استعالما يحتلن السجل الذي رقمه‬ :05/11/2011 ‫ لتصبح‬Date ‫ويغير قيمة الحقل‬ ‫الحل‬ UPDATE Courses SET [Hours]=56, [Date]=# 05/11/2011# WHERE CourseID=4 :‫بعد تنفيذ األمر يصبح محتوى الجدول كالتالي‬ CourseID 1 2 3 4 5 6

Code 100 102 104 106 108 110

Date 20/12/2011 30/12/2011 03/12/2011 05/11/2011 22/09/2010 02/09/2010

Category CS CS En Ar CS CS

158

Title C# Java English Level 1 Arabic For Beginners SQL Basics MS Access

Hours 112 112 112 56 112 112


‫الحذف‬ ‫يتم حذف سجالت موجودة في جدول ما بواسطة األمر ‪DELETE‬‬ ‫الصيغة العامة لألمر هي كالتالي‪:‬‬ ‫]‪DELETE * FROM TableName [WHERE Condition‬‬ ‫أي قم بحذف جميع السجالت من الجدول المعطى وفق الشرط المعطى‪ .‬الحظ أن استعمال األمر ‪ DELETE‬بدون‬ ‫شرط سيؤدي الى حذف جميع السجالت الموجودة في الجدول‪ .‬أي أن األمر‬

‫‪DELETE * FROM TableName‬‬ ‫يُستعمل لتفريغ الجداول من محتواها‪.‬‬ ‫أمثلة‬ ‫ليكن محتوى الجدول ‪ Courses‬كالتالي‪:‬‬

‫‪Hours‬‬ ‫‪112‬‬ ‫‪112‬‬ ‫‪56‬‬ ‫‪112‬‬ ‫‪112‬‬ ‫‪112‬‬

‫‪Title‬‬ ‫‪C#‬‬ ‫‪Java‬‬ ‫‪English Level 1‬‬ ‫‪Arabic For Beginners‬‬ ‫‪SQL Basics‬‬ ‫‪MS Access‬‬

‫‪Category‬‬ ‫‪CS‬‬ ‫‪CS‬‬ ‫‪En‬‬ ‫‪Ar‬‬ ‫‪CS‬‬ ‫‪CS‬‬

‫‪Date‬‬ ‫‪20/12/2011‬‬ ‫‪30/12/2011‬‬ ‫‪03/12/2011‬‬ ‫‪11/11/2011‬‬ ‫‪22/09/2010‬‬ ‫‪02/09/2010‬‬

‫‪Code‬‬ ‫‪100‬‬ ‫‪102‬‬ ‫‪104‬‬ ‫‪106‬‬ ‫‪108‬‬ ‫‪110‬‬

‫‪CourseID‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫أكتب استعالما جميع السجالت التي قيمة الحقل ‪ Category‬عندها ‪CS‬‬ ‫الحل‬ ‫’‪DELETE * FROM Courses WHERE Category=’CS‬‬

‫بعد تنفيذ األمر يصبح محتوى الجدول كالتالي‪:‬‬

‫‪159‬‬


CourseID 3 4

Code 104 106

Date 03/12/2011 11/11/2011

Category En Ar

Title English Level 1 Arabic For Beginners

Hours 112 112

:‫ على النحو التالي‬IN ‫إمكانية أخرى للحل تكمن في استعمال المعامل‬

DELETE * FROM Courses WHERE CourseID IN (1,2,5,6) :‫ على النحو التالي‬BETWEEN ‫أو بواسطة استعمال المعامل‬

DELETE * FROM Courses WHERE CourseID BETWEEN 1 AND 2 OR CourseID BETWEEN 5 AND 6

160


‫الدوال‬ ‫توفر لغة ‪ SQL‬مجموعة من الدوال‬ ‫• الحسابية )‪ (SQL Aggregate Functions‬التي تعيد قيمة واحدة بناء على حسابات تمت لقيم حقل أو عمود‬ ‫كامل‬ ‫• والدوال الثابتة أو السكاالرية )‪ (SQL Scalar functions‬التي تأخذ قيمة واحدة وتعيد أيضا قيمة واحدة فقط‪.‬‬ ‫الجدول التالي يلخص معظم هذه الدوال مع شرح بسيط يليه شرح مفصل مع أمثلة‪:‬‬ ‫الجدول التالي ‪ WorkHours‬سيخدمنا لشرح األمثلة‪ .‬الجدول يخزن عدد ساعات العمل لكل يوم من أيام العمل‬ ‫لعمال معينين‪:‬‬

‫‪Hours‬‬ ‫‪8‬‬ ‫‪9‬‬ ‫‪8‬‬ ‫‪8‬‬ ‫‪10‬‬ ‫‪8‬‬

‫‪MyDate‬‬ ‫‪20/12/2011‬‬ ‫‪21/12/2011‬‬ ‫‪22/12/2011‬‬ ‫‪20/12/2011‬‬ ‫‪21/12/2011‬‬ ‫‪22/12/2011‬‬

‫‪WorkerID‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪106‬‬ ‫‪106‬‬ ‫‪106‬‬

‫‪ID‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫ولنبدأ بشرح الدوال الحسابية‬ ‫الدالة )‪ AVG(Column‬تتلقى اسم حقل من نوع ‪ Number‬وتعيد لنا معدل قيم الحقل ( العمود ) الذي حددناه‬ ‫لها وفقط للسجالت التي ال تكون فارغة‪.‬‬ ‫مثال‪ :‬أكتب استعالما يعيد معدل ساعات العامل الذي رقمه ‪.100‬‬ ‫الحل‪:‬‬ ‫]‪SELECT AVG(Hours) AS [Hours Average‬‬ ‫‪FROM WorkHours‬‬ ‫‪WHERE WorkerID=100‬‬ ‫النتيجة‪:‬‬

‫‪161‬‬


‫‪Hours Average‬‬ ‫‪8.3333333333‬‬ ‫مالحظة‬ ‫من المفضل استعمال المعامل ‪ AS‬إلعطاء اسما جديدا لحقل النتيجة وذلك ألن ‪ Access‬يعطي اسما عشوائيا ال‬ ‫معنى له )‪ (Expr1000‬لهذا الحقل اذا لم نستعمل ‪ .AS‬فجدول النتيجة سيكون كالتالي بدون ‪AS‬‬

‫‪Expr1000‬‬ ‫‪8.3333333333‬‬

‫الدالة )‪ SUM(Column‬تتلقى اسم حقل من نوع ‪ Number‬وتعيد لنا المجموع الكلي لقيم الحقل ( العمود )‬ ‫الذي حددناه لها وفقط للسجالت التي ال تكون فارغة‪.‬‬ ‫مثال‪ :‬أكتب استعالما يعيد مجموع الساعات الكلي للعامل الذي رقمه ‪.106‬‬ ‫الحل‪:‬‬

‫]‪SELECT SUM(Hours) AS [Sum Of Hours‬‬ ‫‪FROM WorkHours‬‬ ‫‪WHERE WorkerID=106‬‬ ‫النتيجة‪:‬‬

‫‪Sum Of Hours‬‬ ‫‪26‬‬

‫مثال‪ :‬أكتب استعالما يعيد مجموع الساعات الكلي لجميع العمال يوم ‪.2011/12/21‬‬ ‫الحل‪:‬‬

‫‪162‬‬


‫]‪SELECT SUM(Hours) AS [Sum Of Hours‬‬ ‫‪FROM WorkHours‬‬ ‫‪WHERE MyDate=#21/12/2011#‬‬ ‫النتيجة‪:‬‬

‫‪Sum Of Hours‬‬ ‫‪19‬‬

‫الدالة )‪ COUNT(Column‬تتلقى اسم حقل وتعيد لنا عدد السجالت التي تحقق شرطا معينا‪ .‬اذا كتبنا مكان اسم‬ ‫الحقل الرمز * (وبدون شرط) فان الدالة تعيد عدد جميع األسطرالموجودة في الجدول‪.‬‬ ‫مالحظة‪ :‬استعمال هذه الدالة يكون في أغلب األحيان بالصيغة الثانية أي مع الرمز *‪.‬‬ ‫مثال‪ :‬أكتب استعالما يعيد حجم الجدول ‪( WorkHours‬أي عدد جميع األسطرالموجودة في الجدول)‪.‬‬ ‫الحل‪:‬‬ ‫]‪SELECT COUNT(*) AS [Total Num Of Rows‬‬ ‫‪FROM WorkHours‬‬ ‫النتيجة‪:‬‬ ‫‪Total Num Of Rows‬‬ ‫‪6‬‬

‫مثال‪ :‬أكتب استعالما يعيد عدد األيام التي عملها العامل الذي رقمه ‪ 100‬خالل شهر ‪ 12‬سنة ‪2011‬‬ ‫الحل‪:‬‬ ‫]‪SELECT COUNT(*) AS [Num Of Days‬‬ ‫‪FROM WorkHours‬‬ ‫‪WHERE WorkerID=100 AND MyDate BETWEEN #1/12/2011# AND‬‬ ‫‪#31/12/2011#‬‬ ‫النتيجة‪:‬‬

‫‪163‬‬


‫‪Num Of Days‬‬ ‫‪3‬‬

‫الدالة )‪ MAX(Column‬تتلقى اسم حقل من نوع ‪ Number‬أو ‪ Date/Time‬وتعيد لنا أكبر قيمة لهذا الحقل (‬ ‫العمود ) الذي حددناه لها وفقط للسجالت التي ال تكون فارغة‪.‬‬ ‫مثال‪ :‬أكتب استعالما يعيد أكبر عدد ساعات عمله العامل الذي رقمه ‪ 100‬خالل شهر ‪ 12‬سنة ‪.2011‬‬ ‫الحل‪:‬‬ ‫]‪SELECT MAX(Hours) AS [Max Hours in 12/2011‬‬ ‫‪FROM WorkHours‬‬ ‫‪WHERE WorkerID=100‬‬ ‫‪AND MyDate BETWEEN #1/12/2011# AND #31/12/2011#‬‬ ‫النتيجة‪:‬‬

‫‪Max Hours in 12/2011‬‬ ‫‪9‬‬

‫واحدة من أهم استعماالت هذه العملية تكمن في استخراج قيمة المفتاح الرئيسي آلخر سجل أُدخل الى جدول مفتاحه‬ ‫الرئيسي من نوع ‪.Auto Number‬‬ ‫الدالة )‪ MIN(Column‬تتلقى اسم حقل من نوع ‪ Number‬أو ‪ Date/Time‬وتعيد لنا أصغر قيمة لهذا الحقل‬ ‫( العمود ) الذي حددناه لها وفقط للسجالت التي ال تكون فارغة‪.‬‬

‫‪164‬‬


‫إنشاء مجموعات في جدول النتيجة‬ ‫اإلضافة ‪ Group BY‬تمكننا من إنشاء مجموعات في جدول النتيجة‪ .‬لبيان ذلك نفرض أننا نريد استخراج‬ ‫معلومات من جدول الطالب التالي بحيث يُعرض معدل كل طالب في جميع المواضيع حسب رقم الطالب‬ ‫)‪:(StudentID‬‬ ‫جدول ‪:Students‬‬ ‫‪Mark‬‬ ‫‪80‬‬ ‫‪90‬‬ ‫‪89‬‬ ‫‪88‬‬ ‫‪100‬‬ ‫‪95‬‬

‫‪SubjectID‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫‪StudentID‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪106‬‬ ‫‪106‬‬ ‫‪106‬‬

‫‪ID‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫جدول النتيجة يجب أن يكون كالتالي‪:‬‬ ‫‪Average‬‬ ‫‪86.333‬‬ ‫‪94.333‬‬

‫‪StudentID‬‬ ‫‪100‬‬ ‫‪106‬‬

‫الحظ أن الجدول يحوي معدل كل طالب في الثالثة مواضيع التي تقدم إليها‪ .‬حتى اآلن كنا نحصل على قيمة واحدة‬ ‫(معدل أو مجموع أو عدد ‪ .)...‬االستعالم التالي ينجز المطلوب‪:‬‬

‫]‪SELECT StudentID, AVG(Mark) AS [Average‬‬ ‫‪FROM Students‬‬ ‫‪GROUP BY StudentID‬‬ ‫الجزء األخير ‪ GROUP BY StudentID‬يؤدي إلى تقسيم الجدول إلى مجموعات ‪ Groups‬بناء على اسم‬ ‫الحقل الذي جاء بعد ‪ GROUP BY‬أي ‪ .StudentID‬بعد هذا التقسيم إلى مجموعات يتم حساب المعدل لكل‬ ‫مجموعة على حدا‪:‬‬

‫‪165‬‬


‫‪Mark‬‬ ‫‪80‬‬ ‫‪90‬‬ ‫‪89‬‬ ‫‪88‬‬ ‫‪100‬‬ ‫‪95‬‬

‫‪SubjectID‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫‪StudentID‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪100‬‬ ‫‪106‬‬ ‫‪106‬‬ ‫‪106‬‬

‫‪ID‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬

‫المجموعة األولى‬

‫المجموعة الثانية‬

‫طبعا بإمكاننا أن نرتب النتائج ترتيبا تنازليا مثال بحسب المعدل المحسوب من خالل استعمال ‪:ORDER BY‬‬ ‫‪SELECT StudentID, AVG(Mark) AS Average‬‬ ‫‪FROM Students‬‬ ‫‪GROUP BY StudentID‬‬ ‫‪ORDER BY AVG(Mark) DESC‬‬ ‫حينها يكون جدول النتيجة كالتالي‪:‬‬ ‫‪Average‬‬ ‫‪94.333‬‬ ‫‪86.333‬‬

‫‪StudentID‬‬ ‫‪106‬‬ ‫‪100‬‬

‫الحظ أنه ليس بالضرورة للحقل الذي يتم تقسيم الجدول بحسبه (‪ StudentID‬في المثال) أن يكون ضمن األمر‬ ‫‪ .SELECT‬لكن حينها ال يظهر هذا الحقل في جدول النتيجة‪ .‬لذلك ألالستعالم التالي يعيد الجدول التالي‪:‬‬ ‫‪SELECT AVG(Mark) AS Average‬‬ ‫‪FROM Students‬‬ ‫‪GROUP BY StudentID ORDER BY AVG(Mark) DESC‬‬

‫‪Average‬‬ ‫‪94.333‬‬ ‫‪86.333‬‬

‫‪166‬‬


‫‪ADO.NET‬‬ ‫‪ActiveX Data Objects‬‬ ‫ما هي تقنية ‪Ado.net‬‬ ‫هي مجموعة من الفئات )‪ (Classes‬المدمجة مع إطار عمل دوت نت )أي الـ ‪ DOT NET (Framework‬و‬ ‫التي تمكننا من الوصول إلى البيانات الموجودة في قاعدة بيانات ما من أجل استخراج المعلومات أو من أجل تعديلها‬ ‫أو حذفها أو إضافة سجالت جديدة إليها‪ .‬أي أننا سنشرح اآلن آلية من اآلليات التي توفر لنا امكانية اضافة ذاكرة‬ ‫طويلة المدى للموقع أي قاعدة بيانات‪ .‬هذه الفئات موجودة ضمن فضاء األسماء ‪ System.Data‬ولذلك يجب‬ ‫علينا أن نضيف هذا الفضاء إلى التطبيق من خالل األمر ;‪ .using System.Data‬الشكل التالي يبين لنا ما‬ ‫يسمى بمعمارية الفئات (أي الـ )‪.ADO.NET Classes Architecture‬‬ ‫المصدر‪http://msdn.microsoft.com/en-us/library/ff647768.aspx :‬‬

‫نقاط هامة لفهم المكونات الظاهرة بالصورة والتي معظمها أسماء لفئات معينة‪:‬‬ ‫‪.1‬الوصول الى المعلومات الموجودة بأي ملف يقتضي معرفة اسم الملف الكامل أي يشمل مكان وجوده في‬ ‫القرص الصلب للحاسوب وهو ما يسمى بالمسار الكامل (‪ .)Full Path‬ثم يتم بعد ذلك فتح الملف‬ ‫لمعالجة المعلومات‪ .‬الفئة المسؤولة عن كل ذلك والتي توفر لنا عمليات مناسبة للقيام بذلك هي الفئة‬ ‫‪ Connection‬أو ‪ .OleDbConnection‬واضح أن االسم يشير الى وظيفة إنشاء اتصال مع‬ ‫قاعدة البيانات‪.‬‬

‫‪167‬‬


‫‪.2‬االتصال المباشر مع قاعدة البيانات يتم من خالل مكتبات )‪ (Software Libraries‬خاصة تسمى‬ ‫مزودات أي ‪ .Providers‬تحتوي هذه المكتبات على مجموعة من العمليات (أي الدوال واإلجراءات)‬ ‫التي تمكننا من الوصول إلى القاعدة وفتحها وقراءة وكتابة البيانات‪.‬‬ ‫‪.3‬معالجة البيانات الموجودة في قاعدة بيانات عالئقية تتم من خالل األستعانة بلغة ‪ SQL‬فقط‪ .‬األوامر المكتوبة‬ ‫بلغة ‪ SQL‬تسمى ‪ Commands‬أو ‪ .SQL Statements‬الفئة المسؤولة عن ادارة مثل هذه األوامر‬ ‫هي الفئة ‪ Command‬أو ‪ .OleDbCommand‬تمكننا هذه الفئة من تنفيذ جميع األوامر التي نعرفها‬ ‫من لغة ‪ SQL‬وهي‪ SELECT, DELETE, UPDATE, INSERT INTO :‬وغيرها‪.‬‬ ‫‪.4‬تسمى قاعدة البيانات ‪ Data Base‬أو ‪.Data Source‬‬ ‫‪.5‬نتيجة تنفيذ األمر ‪ SELECT‬تكون دائما جدول معلومات أي ‪ .DataTable‬هذا الجدول قد يكون فارغا‬ ‫ولكن مبناه حتى عندما يكون فارغا يكون وفق مبنى االستعالم أي وفق مجموعة الحقول التي طلبنا قيمها‪.‬‬ ‫شرح بعض صفات الفئة ‪ DataTable‬سيأتي الح ًقا‪.‬‬ ‫‪.6‬الفئة ‪ DataSet‬عبارة عن مجموعة أي ‪ Set‬من كائنات ‪DataTable‬‬ ‫‪.7‬تنفيذ األوامر وتخزين النتيجة في كائن من نوع ‪ DataTable‬أو ‪ DataSet‬يتم من قبل وسيط ما بين‬ ‫الوسط المادي أي الملف نفسه )‪ (Physical File‬والكائن‪ .‬الفئة التي تقوم بدور هذا الوسيط هي الفئة‬ ‫‪ DataAdapter‬أو ‪ .OleDbDataAdapter‬تمثل هذه الفئة الجسر الذي يربط بين ‪DataSet‬‬ ‫وقاعدة البيانات ويدعم أوامر ‪ Select - Update - Delete - Insert Into‬وبالتالي بإمكانه القيام‬ ‫بعمليات مختلفة على البيانات كما أنه المسؤول عن تحميل أو تعبئة كائن ‪ DataSet‬بالبيانات‪ .‬الحظ أن‬ ‫عمل هذه الفئة يتم باالتجاهين‪ :‬من قاعدة البيانات الى الـ ‪ DataSet‬والعكس‪.‬‬

‫خطوات الوصول الى البيانات‬ ‫•‬

‫الخطوة األولى هي خطوة االرتباط بقاعدة البيانات وفتح االتصال‪ .‬الكود التالي يبين ذلك‪:‬‬ ‫‪ .1‬إنشاء كائن من الفئة ‪OleDbConnection‬‬ ‫;()‪OleDbConnection c = new OleDbConnection‬‬ ‫‪ .2‬تجهيز نص االتصال أي الـ ‪ Connection String‬لقاعدة بيانات من نوع ‪ MS Access‬أو ما يسمى‬ ‫أيضا بـ ‪.MS Jet‬‬ ‫= ‪c.ConnectionString‬‬ ‫;”=‪"Provider=Microsoft.Jet.OLEDB.4.0; Data Source‬‬ ‫‪c.ConnectionString += HttpContext.Current.Server.MapPath("~/App_Data/" +‬‬ ‫;)‪strFileName‬‬

‫‪ :strFileName‬اسم قاعدة البيانات‬

‫‪168‬‬


‫نص االتصال عبارة عن نص يحتوي على معلومات عن قاعدة البيانات وعن المُزود التابع لها‪ .‬نص االتصال مكون‬ ‫من مجموعة من األزواج التي يفصل بينها الرمز ;‪ .‬كل زوج مكون من اسم وقيمة يفصل بينها الرمز =‪ .‬هنا لدينا‬ ‫األزواج التالية‪:‬‬ ‫االسم‬ ‫‪Provider‬‬

‫القيمة‬ ‫‪Microsoft.Jet.OLEDB.4.0‬‬

‫الشرح‬ ‫هذه القيمة تدل على أن قاعدة البيانات من‬ ‫نوع ‪MS Access 2003‬‬ ‫من المعتاد في مواقع ‪ ASP.NET‬حفظ‬

‫‪ Data Source‬المسار الكامل لقاعدة البيانات‪ .‬مثال‪:‬‬

‫‪ C:\Inetpub\wwwroot\Test\Db.mdb‬قاعدة البيانات في مجلد خاص ضمن‬ ‫مجلدات الموقع باسم ‪.App_Data‬‬ ‫العملية ‪ MapPath‬تعيد لنا مسار الموقع‬ ‫أي المكان المخزن به الموقع على‬ ‫الحاسوب مثل‪:‬‬ ‫‪C:\Inetpub\wwwroot\Test‬‬ ‫تتلقى العملية ن ً‬ ‫صا اضافيًا يمثل اسم المجلد‬ ‫‪ App_Data‬واسم قاعدة البيانات‪ :‬هنا‬ ‫أسمها ‪Db.mdb‬‬ ‫الحظ أنه يوجد لكل اصدار من اصدارات ‪ MS Access‬المختلفة ‪ Provider‬خاص باالصدار‪ .‬الجدول التالي‬ ‫يبين أهمها‪:‬‬ ‫‪Provider‬‬ ‫‪Microsoft.Jet.OLEDB.4.0‬‬

‫‪MS Access‬‬ ‫‪Access 97‬‬ ‫‪Access 2000‬‬ ‫‪Access 2002‬‬ ‫‪Access 2003‬‬ ‫‪Microsoft.ACE.OLEDB.12.0 Access 2007‬‬ ‫‪Access 2010‬‬ ‫‪Access 2013‬‬

‫يوفر الموقع التالي نصوص االتصال لمعظم الـ ‪:Providers‬‬ ‫‪http://www.connectionstrings.com/‬‬ ‫‪ .3‬فتح القاعدة بمساعدة العملية ‪ Open‬التابعة للفئة ‪OleDbConnection‬‬ ‫;)(‪c.Open‬‬

‫‪169‬‬


:Dbase ‫ التابعة لفئة مساعدة باسم‬MakeConnection ‫وضعت أوامر الخطوة األولى في العملية الستاتية‬ strFileName ‫ اسم قاعدة البيانات‬:‫ادعاء الدخول‬ OleDbConnection ‫ تعيد مؤشرً ا على كائن االتصال أي كائن من الفئة‬:‫ادعاء الخروج‬ public class Dbase { public static OleDbConnection MakeConnection(string strFileName) { OleDbConnection c = new OleDbConnection(); c.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source="; c.ConnectionString += HttpContext.Current.Server.MapPath("~/App_Data/" + strFileName); c.Open(); return c; // ‫تعيد مؤشرً ا على كائن االتصال‬ } }

170


‫تنفيذ استعالمات أي ‪SELECT Queries‬‬ ‫وضعت أوامر تنفيذ االستعالمات في العملية الستاتية ‪ SelectFromTable‬التابعة لفئة مساعدة باسم ‪:Dbase‬‬ ‫ادعاء الدخول‪ :‬نص االستعالم ‪ strSql‬واسم قاعدة البيانات ‪strFileName‬‬ ‫ادعاء الخروج‪ :‬تنفذ االستعالم على القاعدة وتعيد مؤشرً ا على كائن ‪ DataTable‬يحتوي على نتيجة االستعالم‬

‫)‪public static DataTable SelectFromTable(string strSql, string strFileName‬‬ ‫{‬ ‫انشاء اتصال مع قاعدة البيانات ‪//‬‬ ‫;)‪OleDbConnection c = MakeConnection(strFileName‬‬ ‫انشاء كائن لتنفيذ أوامر ‪// SQL‬‬ ‫;)(‪OleDbCommand comm = new OleDbCommand‬‬ ‫اعطاؤه نص االستعالم ‪//‬‬ ‫;‪comm.CommandText = strSql‬‬ ‫واعطاؤه كائن االتصال ‪//‬‬ ‫;‪comm.Connection = c‬‬ ‫انشاء كائن لحفظ نتيجة االستعالم ‪//‬‬ ‫;)(‪DataTable dt = new DataTable‬‬ ‫انشاء كائن وسيط مسؤول عن تعبئة جدول التنيجة في الكائن ‪// dt‬‬ ‫;)‪OleDbDataAdapter da = new OleDbDataAdapter(comm‬‬ ‫تعبئة تعريفات الجدول ‪ -‬مثل معلومات عن المفاتيح وباقي الحقول ‪ -‬في الكائن ‪// dt‬‬ ‫;)‪da.FillSchema(dt, SchemaType.Mapped‬‬ ‫تعبئة جدول التنيجة في الكائن ‪// dt‬‬ ‫;)‪da.Fill(dt‬‬ ‫اغالق االتصال ‪//‬‬ ‫;)(‪c.Close‬‬ ‫اعادة مؤشرعلى كائن ‪// DataTable‬‬ ‫;‪return dt‬‬ ‫}‬

‫‪171‬‬


‫الفئة ‪DataTable‬‬ ‫ُتعتبر هذه الفئة من المكونات المركزية في الـ ‪ .ADO.NET‬نستعمل كائنات من هذه الفئة لحفظ البيانات اآلتية عادة‬ ‫من قاعدة بيانات ما وذلك كنتيجة لتنفيذ أمر ‪ُ .SELECT‬تحفظ البيانات في كائن من نوع ‪ DataTable‬على شكل‬ ‫جدول أي مصفوفة ثنائية‪ُ .‬نلخص فيما يلي أهم خصائص وصفات هذه الفئة‪:‬‬ ‫العملية ‪ /‬الصفة‬

‫الشرح‬ ‫عملية بنائية‬

‫)(‪DataTable‬‬

‫عبارة عن مصفوفة أعمدة (أي ][‪ُ )DataColumn‬تمثل حقول المفتاح‬

‫‪PrimaryKey‬‬

‫الرئيسي‪ .‬في معظم أمثلة الكتاب سيكون هذا المفتاح عبارة عن حقل‬ ‫واحد‪.‬‬ ‫صفة من نوع ‪ DataColumnCollection‬أي عبارة عن قائمة‬

‫‪Columns‬‬

‫أعمدة الجدول‪ .‬بامكاننا استعمال هذه القائمة كما لو كانت مصفوفة أحادية‬ ‫(أي ‪ .)ArrayList‬عدد األعمدة موجود في الصفة ‪ Count‬التابعة لهذه‬ ‫القائمة‪ .‬لكل عامود يوجد عدة صفات منها اسمه‬ ‫‪ ColumnName‬ونوعه ‪ DataType‬وما الى ذلك‪.‬‬ ‫مثال‪ :‬األمر‬ ‫‪dt.Columns[0].ColumnName‬‬ ‫يُعيد اسم العامود األول (رقم ‪ )0‬في الجدول ‪dt‬‬ ‫صفة من نوع ‪ DataRowCollection‬أي عبارة عن قائمة أسطر‬ ‫الجدول‪ .‬عدد األسطر موجود في الصفة ‪ Count‬التابعة لهذه القائمة‪.‬‬ ‫أيضً ا هنا بامكاننا استعمال هذه القائمة كما لو كانت مصفوفة ثنائية‪.‬‬ ‫مثال‪ :‬األمر‬ ‫]"‪dt.Rows[0]["CourseID‬‬ ‫يُعيد قيمة العامود ‪ CourseID‬في السطر األول (رقم ‪ )0‬في الجدول‬ ‫‪ .dt‬الحظ أننا استعملنا اسم العامود بدال من رقمه في جدول النتيجة‬ ‫(الجدول الذي أُرجع نتيجة لتنفيذ أمر الـ ‪ )SELECT‬وذلك ألن االسم‬ ‫معبر أكثر من الرقم‪ .‬طبعً ا كان بامكاننا كتابة األمر على النحو التالي (اذا‬ ‫كان العامود ‪ CourseID‬هو األول)‪:‬‬ ‫]‪dt.Rows[0][0‬‬

‫‪172‬‬

‫‪Rows‬‬


‫تنفيذ أوامر ‪ SQL‬ال تعيد جدول نتيجة ‪DELETE, UPDATE, INSERT INTO‬‬ ‫وضعت هذه األوامر في العملية الستاتية ‪ ChangeFromTable‬التابعة لفئة مساعدة باسم ‪:Dbase‬‬ ‫ادعاء الدخول‪ :‬نص االستعالم ‪ strSql‬واسم قاعدة البيانات ‪strFileName‬‬ ‫ادعاء الخروج‪ :‬تنفذ االستعالم على القاعدة وتعيد عدد األسطر أو السجالت التي تأثرت من األمر أي أضيفت اذا‬ ‫كان األمر ‪ INSERT INTO‬أو حذفت اذا كان األمر ‪ DELETE‬أو ُعدّلت اذا كان األمر ‪UPDATE‬‬ ‫)‪public static int ChangeTable(string strSql, string strFileName‬‬ ‫{‬ ‫انشاء اتصال مع قاعدة البيانات ‪//‬‬ ‫;)‪OleDbConnection c = MakeConnection(strFileName‬‬ ‫انشاء كائن لتنفيذ أوامر ‪// SQL‬‬ ‫;)(‪OleDbCommand comm = new OleDbCommand‬‬ ‫اعطاؤه نص االستعالم ‪//‬‬ ‫;‪comm.CommandText = strSql‬‬ ‫واعطاؤه كائن االتصال ‪//‬‬ ‫;‪comm.Connection = c‬‬ ‫تنفيذ أوامر ‪ SQL‬ال تعيد جدول نتيجة ‪//‬‬ ‫العملية ‪ ExecuteNonQuery‬تعيد عدد األسطر أو السجالت التي تأثرت من األمر ‪//‬‬ ‫;)(‪int nAffectedRows = comm.ExecuteNonQuery‬‬ ‫اغالق االتصال ‪//‬‬ ‫;)(‪c.Close‬‬ ‫اعادة عدد األسطر أو السجالت ‪//‬‬ ‫;‪return nAffectedRows‬‬ ‫}‬

‫‪173‬‬


:‫ كاملة‬Dbase ‫الفئة‬ public class Dbase { public static OleDbConnection MakeConnection(string strFileName) { OleDbConnection c = new OleDbConnection(); c.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source="; c.ConnectionString += HttpContext.Current.Server.MapPath("~/App_Data/" + strFileName); c.Open(); return c; }

public static DataTable SelectFromTable(string strSql, string strFileName) { OleDbConnection c = MakeConnection(strFileName); OleDbCommand comm = new OleDbCommand(); comm.CommandText = strSql; comm.Connection = c; DataTable dt = new DataTable(); OleDbDataAdapter da = new OleDbDataAdapter(comm); da.FillSchema(dt, SchemaType.Mapped); da.Fill(dt); c.Close(); return dt; }

174


public static int ChangeTable(string strSql, string strFileName) { OleDbConnection c = MakeConnection(strFileName); OleDbCommand comm = new OleDbCommand(); comm.CommandText = strSql; comm.Connection = c; int nAffectedRows = comm.ExecuteNonQuery(); c.Close(); return nAffectedRows; } } :‫ هناك امكانيتان لفعل ذلك‬.‫يجب اضافة هذا الملف الى ملفات الموقع‬ ‫ الحظ أن هذا النوع من‬.‫ ومن ثم نسخ الفئة المعطاة هنا وحفظها بداخل الملف‬Dbase.cs ‫اضافة فئة جديدة باسم‬ :App_Code ‫الملفات يُخزن في مجلد خاص باسم‬ :‫الخطوات إلنشاء هذا المجلد‬ ‫ بعد أن تضع الفارة على اسم المشروع‬Solution Explorer ‫أنقر على الزر األيمن للفارة في الـ‬

Add -> New Folder… ‫اختر من القائمة التي تظهر األمر‬

175


‫ادخل اسم المجلد‪:‬‬

‫‪176‬‬


‫الخطوات‪:‬‬ ‫•‬

‫أنقر على الزر األيمن للفارة في الـ ‪ Solution Explorer‬بعد أن تضع الفارة على اسم المجلد‬ ‫‪App_Code‬‬

‫•‬

‫اختر من القائمة التي تظهر األمر …‪Add -> New Item‬‬

‫•‬

‫اختر في نافذة الحوار التي تظهر النوع ‪ class‬وأعط للملف االسم ‪.Dbase.cs‬‬

‫•‬

‫اغلق النافذة بالضغط على الزر ‪Add‬‬

‫الصورة التالية تظهر نافذة الحوار المذكورة أعاله‬

‫‪177‬‬


‫واآلن نقوم بنسخ الفئة المعطاة هنا وحفظها بداخل هذا الملف‪.‬‬ ‫االمكانية الثانية إلضافة الفئة المعطاة هنا تكون من خالل حفظ الكود بداخل ملف نص عادي (أي ‪ )Text File‬باسم‬ ‫‪ Dbase.cs‬بمساعدة المفكرة مثال (أي برنامج الـ ‪ )NotePad‬وحفظه في مكان معين مثل سطح المكتب‪ .‬ثم‬ ‫نقوم بإضافة الملف الى المجلد ‪ App_Code‬من خالل الخطوات التالية‪:‬‬ ‫•‬

‫أنقر على الزر األيمن للفارة في الـ ‪ Solution Explorer‬بعد أن تضع الفارة على اسم المجلد‬ ‫‪App_Code‬‬

‫•‬

‫اختر من القائمة التي تظهر األمر …‪Add -> Existing Item‬‬

‫•‬

‫اختر في نافذة الحوار التي تظهر الملف ‪ Dbase.cs‬من المكان الذي تم حفظه فيه‪.‬‬

‫•‬

‫اغلق النافذة بالضغط على الزر ‪Add‬‬

‫‪178‬‬


‫‪3-Level Architecture‬‬ ‫يتكون الموقع عادة من عدة مكونات برمجية وهي‬ ‫•‬

‫النماذج أي الـ ‪ Forms‬وظيفتها عرض البيانات لل ُمستخدم‪ .‬وتُسمى أيضا بواجهة ال ُمستخدم التخطيطية‬ ‫‪ (GUI – Graphical User Interface) o‬أو ‪Presentation Layer‬‬

‫•‬

‫البرمجة (العمليات والفئات ال ُمختلفة) التي تتعامل مع قاعدة البيانات أي التي تفتح القاعدة وتقرأ منها‬ ‫معلومات أو بيانات أو تكتب فيها بيانات وتحتلنها وتُسمى أيضا‬ ‫‪Application Layer o‬‬

‫•‬

‫قاعدة (أو قواعد) البيانات وتُسمى أيضا‬ ‫‪Database Layer o‬‬

‫لذلك يُفضل معظم مطوري المواقع استعمال ما يُسمى ببُنية الطبقات الثالث )‪ .(3-Level Architecture‬تفصل‬ ‫هذه البُنية هذه المكونات الثالث عن بعضها البعض أي أنها تفصل ما بين واجهة االستخدام المعروضة وإجراءات‬ ‫المعالجة في التطبيق‪ ،‬وقاعدة البيانات‪ .‬من أهم ايجابيات هذه البُنية أنها تمكننا من بناء تطبيقات أو مواقع َمرنة وقابلة‬ ‫إلعادة االستخدام حيث يمكن تقسيم التطبيق إلى طبقات منفصلة ومستقلة فعند الحاجة إلضافة طبقات أو تعديلها ال‬ ‫توجد حاجة إلعادة كتابة التطبيق بالكامل من جديد‪ .‬وانما يكفي تبديل الطبقة التي تغيرت‪ .‬فمثال إذا تقرر أن ُتغير‬ ‫قاعدة البيانات من مثال ‪ MS Access‬الى ‪ MS SQL Server‬فان الطبقة التي تتأثر بالتغيير هي فقط طبقة‬ ‫قاعدة البيانات‪.‬‬ ‫لذلك سنعتمد في الفصول التالية‪ ،‬التي نبدأ فيها باستعمال قاعدة بيانات‪ ،‬هذه البُنية بحيث نكتب لكل جدول في قاعدة‬ ‫البيانات فئة بلغة ‪ C#‬وبنفس اسم الجدول بعد اضافة الكلمة ‪ Class‬الى بدايتها لتمييزها على أنها فئة خاصة بنا‬ ‫(مثال ‪ ClassUsers‬لجدول باسم ‪ .)Users‬توفر هذه الفئة عادة‬ ‫•‬

‫خاصيّة لكل حقل من حقول الجدول‪ .‬بإمكاننا تعريف جميع الصفات من نوع نص‪.‬‬

‫•‬

‫ومجموعة كافية من العمليات البنائية‬

‫•‬

‫ومجموعة من العمليات التي ُتنفذ معظم أو كل العمليات ال ُم َتوقع تنفيذها على الجدول مثل‬ ‫‪ o‬استرجاع بيانات مُعينة (أي ‪)SELECT‬‬ ‫‪ o‬اضافة بيانات مُعينة (أي ‪)INSERT INTO‬‬ ‫‪ o‬حذف بيانات مُعينة (أي ‪)DELETE‬‬ ‫‪ o‬تحديث أو حتلنة بيانات مُعينة (أي ‪)UPDATE‬‬

‫ُتحفظ هذه الفئات في مجلد خاص ضمن مجلدات الموقع باسم ‪App_Code‬‬ ‫‪179‬‬


‫الصورة التالية ُتوضح هذه البُنية‬

‫‪Presentation Layer‬‬ ‫عرض البيانات‬

‫‪Application Layer‬‬ ‫برمجة التواصل مع القاعدة‬

‫‪Database Layer‬‬

‫قاعدة البيانات‬

‫‪180‬‬


‫‪Data Controls‬‬ ‫األداة ‪GridView‬‬

‫تعتبر األداة ‪ GridView‬من أهم األدوات لعرض المعلومات على شكل جدول‪ .‬واألهم من ذلك أن هذه األداة تمكننا‬ ‫من تحرير المعلومات الموجودة في الجدول أي تمكننا من‬ ‫•‬

‫حذف هذه المعلومات‬

‫•‬

‫اضافة سطر جديد الى الجدول‬

‫•‬

‫تحديث معلومات سطر من أسطر الجدول‬

‫•‬

‫ترتيب الجدول حسب عامود معين تصاعديا أو تنازليا‬

‫بل وتمكننا من اضافة أدوات أخرى داخل خاليا الجدول وحسب الحاجة‪ .‬اضافة الى ذلك فان هذه األداة تدعم ما‬ ‫يسمى بربط األدوات بمصادر المعلومات (أي ‪ .)DataBinding‬تعتبر ‪ DataBinding‬منهج أساسي من مناهج‬ ‫ميكروسفت المتعلقة بكيفيات عرض المعلومات والتعامل معها وهو ما يسمى بـ ‪Windows Presentation‬‬ ‫)‪ DataBinding .Foundation (WPF‬عبارة عن عملية وصل األداة (التي تمثل واجهة المستخدم الصُورية‬ ‫أي ‪ )Graphical User Interface‬بالكائنات التي تحتوي على المعلومات التي غالبا ما ُتؤخذ من قاعدة‬ ‫البيانات‪ .‬هذا الوصل أو الربط يكون من خالل ما يلي‪:‬‬ ‫•‬

‫اعطاء الكائن الذي يحتوي على المعلومات كقيمة للخاصية ‪( DataSource‬أي مصدر المعلومات)‬ ‫التابعة لألداة التي تدعم هذا المنهج‪.‬‬

‫•‬

‫استدعاء العملية التي تقوم بدورها بنقل المعلومات من الكائن الى األداة أي العملية )(‪ DataBind‬التابعة‬ ‫أيضا لألداة التي تدعم هذا المنهج‪.‬‬

‫مثال‪:‬‬ ‫معطى الجدول التالي‪:‬‬

‫‪181‬‬


:‫وفيه المعلومات التالية‬

‫ طبعا بإمكاننا القيام بذلك "يدويا" من خالل انشاء كود‬.‫نريد تصميم صفحة انترنت لعرض محتوى الجدول كامال‬ .‫ لجدول ما وتنسيقه حسب المطلوب‬،‫ أي خالل طلب الصفحة‬،‫ وبشكل ديناميكي‬HTML :Courses ‫ التي ستمثل الجدول‬ClassCourses ‫نكتب أوال الفئة‬ public class ClassCourses { private string courseID, date, category, title, hours; public ClassCourses() {} public ClassCourses(string courseID, string date, string category, string title, string hours) { this.courseID = courseID; ‫الحظ أن مبنى الفئة يعكس تماما مبنى‬ this.date = date; this.category = category; ‫الجدول أي أننا نجد لكل حقل من حقول‬ this.title = title; this.hours = hours; .‫الجدول خاصيّة من نوع نص‬ }

182


public string CourseID { get { return courseID; } set { courseID = value; } } public string Date { get { return date; } set { date = value; } } public string Category { get { return category; } set { category = value; } } public string Title { get { return title; } set { title = value; } } public string Hours { get { return hours; } set { hours = value; } } } :‫نضيف الى هذه الفئة العمليات التالية للوصول الى المعلومات الموجودة في قاعدة البيانات‬ public DataTable GetAll() { string strSQL; strSQL = "SELECT * FROM Courses "; // 1 DataTable dt = Dbase.SelectFromTable(strSQL, "DB.mdb"); // 2 return dt; } ‫ أوال نكتب‬.DataTable ‫ بالكامل بواسطة كائن من نوع‬Courses ‫مهمة هذه العملية اعادة محتوى الجدول‬ ‫ لتنفيذ‬Dbase ‫ ثم نستعمل الفئة‬.)1 ‫االستعالم الذي يعيد لنا المعلومات المطلوبة من قاعدة البيانات (األمر رقم‬ .)2 ‫ يحتوي على المعلومات (األمر رقم‬DataTable ‫األمر والحصول على كائن‬ :HTML ‫بعدها نضيف العملية التالية التي ُتعيد لنا المعلومات على شكل جدول‬

183


public string GetAllAsHTMLTable() { DataTable dt = this.GetAll(); // 1 string strTable = "<table border=1><tr>"; // 2 for (int i = 0; i < dt.Columns.Count; i++) // 3 strTable += "<td>" + dt.Columns[i].ColumnName + "</td>"; strTable += "</tr>"; for (int i = 0; i < dt.Rows.Count; i++) // 4 { strTable += "<tr>"; for (int j = 0; j < dt.Columns.Count; j++) // 5 strTable += "<td>" + dt.Rows[i][dt.Columns[j].ColumnName] + "</td>"; strTable += "</tr>"; } strTable += "</table>"; return strTable; } ‫ من خالل شرح األسطر المُرقمة‬GetAllAsHTMLTable ‫شرح العملية‬ Courses ‫ لنحصل على محتوى الجدول‬GetAll ‫ نستدعي العملية‬:1 ‫السطر رقم‬ ‫ كنص‬HTML ‫ نبدأ ببناء جدول الـ‬:2 ‫السطر رقم‬ ‫ الحظ أن الحلقة تقوم‬.‫ نبني بواسطة هذه الحلقة السطر األول في الجدول أي سطر عناوين األعمدة‬: 3 ‫السطر رقم‬ .‫ببناء خلية لكل عامود وتخزن فيها اسم العامود‬ dt.Rows ‫ لكل سطر من أسطر‬:4 ‫السطر رقم‬ ‫ كمصفوفة ثنائية‬dt.Rows ‫ الحظ أننا استعملنا‬.‫ ونحفظ في خالياه قيم الحقول‬HTML ‫ نبني سطر‬:5 ‫السطر رقم‬ .‫بحيث أن البعد الثاني كان اسم العامود وليس رقمه‬ ‫ مهمته عرض جدول الـ‬LabelTable ‫ باسم‬Label ‫ أداة‬،‫ التي عليها عرض الجدول‬،‫نضيف الى صفحة التنسيق‬ GetAllAsHTMLTable ‫ الذي ستعيده العملية‬HTML protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BuildTable(); } }

184


‫)(‪private void BuildTable‬‬ ‫{‬ ‫‪try‬‬ ‫{‬ ‫;)(‪ClassCourses obj = new ClassCourses‬‬ ‫;)(‪LabelTable.Text = obj.GetAllAsHTMLTable‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫;‪LabelTable.Text = ex.Message‬‬ ‫}‬ ‫}‬ ‫شرح العملية )(‪BuildTable‬‬ ‫تقوم هذه العملية بانشاء كائن من نوع ‪ ClassCourses‬وتستدعي بمساعدته العملية‬ ‫‪ GetAllAsHTMLTable‬وتعطي النتيجة‪ ،‬التي هي عبارة عن كود ‪ ،HTML‬لألداة ‪ LabelTable‬لعرضها‬ ‫في الصفحة‪ .‬الحظ أننا أحطنا الكود بأوامر ‪ try/catch‬لمعالجة أي استثناء قد يكون خالل زمن التشغيل‪ .‬األخطاء‬ ‫التي قد تحصل ُتعرض للمُستخدم بواسطة األداة ‪.LabelTable‬‬ ‫نتيجة تنفيذ العمليات المذكورة أعاله تكون كالتالي‪:‬‬

‫واآلن نريد استعمال طريقة الـ ‪ DataBinding‬واألداة ‪ GridView‬لعرض نفس المعلومات واالنتباه الى سهولة‬ ‫األمر بالمقارنة مع العرض اليدوي‪:‬‬ ‫•‬

‫نضيف الى صفحة التنسيق أداة ‪( GridView‬في المثال األداة ‪ .)GridViewCourses‬ونختار له‬ ‫تنسيقا من التنسيقات المعرفة مسبقا‪.‬‬

‫•‬

‫ثم نقوم بانشاء كائن من نوع ‪ ClassCourses‬ونستدعي بمساعدته العملية ‪ GetAll‬التي ُتعيد لنا كائنا‬ ‫من نوع ‪ DataTable‬باسم ‪ dt‬يحتوي على المعلومات (األمر رقم ‪.)1‬‬

‫•‬

‫نعطي الكائن ‪ dt‬الذي يحتوي على المعلومات كقيمة للخاصية ‪( DataSource‬أي مصدر المعلومات)‬ ‫التابعة لألداة ‪( GridViewCourses‬األمر رقم ‪.)2‬‬

‫‪185‬‬


‫•‬

‫نستدعي العملية )(‪ DataBind‬التي تقوم بدورها بنقل المعلومات من الكائن ‪ dt‬الى األداة‬ ‫‪( GridViewCourses‬األمر رقم ‪.)3‬‬

‫•‬

‫األخطاء التي قد تحصل ُتعرض لل ُمستخدم بواسطة األداة ‪( LabelMessage‬األمر رقم ‪.)4‬‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫{‬ ‫)‪if (!IsPostBack‬‬ ‫{‬ ‫;)(‪FillGrid‬‬ ‫}‬ ‫}‬ ‫)(‪private void FillGrid‬‬ ‫{‬ ‫‪try‬‬ ‫{‬ ‫‪ClassCourses obj = new ClassCourses(); // 1‬‬ ‫‪GridViewCourses.DataSource = obj.GetAllAsHTMLTable();// 2‬‬ ‫‪GridViewCourses.DataBind();// 3‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫‪LabelMessage.Text = ex.Message; // 4‬‬ ‫}‬ ‫}‬

‫نتيجة تنفيذ العمليات المذكورة أعاله تكون كالتالي‪:‬‬

‫هذا المثال يبين ان ما يجب أن يُكتب يدويا تقوم به العملية ‪ .DataBind‬فهي تقوم بعملية التمشيط التي قمنا بها‬ ‫يدويا وتبني أيضا الجدول‪ .‬بناء الجدول يتم في هذه الحالة فقط إذا كانت قيمة الخاصية‬ ‫‪ AutoGenerateColumns‬التابعة لألداة ‪ .True GridView‬من اسم هذه األداة يتضح أن الـ ‪GridView‬‬ ‫يقوم حينها بإنشاء )‪ (Generate‬األعمدة )‪ (Columns‬بشكل تلقائي )‪ .(Auto‬المعلومات عن األعمدة موجودة‬ ‫في الكائن ‪ .dt‬أسماء األعمدة تكون في هذه الحالة نفس األسماء التي تأتي في االستعالم بعد األمر ‪ .SELECT‬في‬ ‫حال استعمال المعامل ‪ AS‬في االستعالم يكون اسم العامود االسم الذي يأتي بعد المعامل ‪.AS‬‬ ‫‪186‬‬


‫مثال‬ ‫اآلن نريد تمكين المستخدم من التسجيل لدورة معينة من خالل النقر على ارتباط موجود في كل سطر أي لكل دورة‪.‬‬ ‫إذا نقر المستخدم على هذا االرتباط يتم نقله الى صفحة تسجيل خاصة باسم ‪ RegistrationForm.aspx‬ويُرسل‬ ‫اليها كود الدورة كبارامتر‪ .‬الشكل التالي يوضح ما نريد‪:‬‬

‫طبعا بإمكاننا أن نبرمج ذلك بدون ‪ GridView‬من خالل اكمال المثال المذكور أعاله بحيث نضيف الى الجدول‬ ‫عامودا اضافيا فيه ارتباط تشعبي الى الصفحة ‪ RegistrationForm.aspx‬وذلك يكون كما يلي‪:‬‬

‫أوال علينا تعديل العملية ‪ GetAllAsHTMLTable‬بحيث نضيف عامودا جديدا للتسجيل‪ .‬هذا العامود عبارة عن‬ ‫ارتباط تشعبي ينقل المُستخدم الى صفحة التسجيل ويرسل للصفحة رقم الكورس الذي يريد المُستخدم التسجيل له‪.‬‬ ‫الحظ أننا نستعمل الصفة ‪ PrimaryKey‬التابعة للفئة ‪ DataTable‬حتى نحصل على اسم المفتاح الرئيسي الذي‬ ‫هو في هذا المثال ‪.CourseID‬‬ ‫)(‪public string GetAllAsHTMLTable‬‬ ‫{‬ ‫;)(‪DataTable dt = this.GetAll‬‬ ‫;">‪string strTable = "<table border=1‬‬ ‫;">‪strTable += "<tr‬‬ ‫)‪for (int i = 0; i < dt.Columns.Count; i++‬‬ ‫{‬ ‫;">‪strTable += "<td>" + dt.Columns[i].ColumnName + "</td‬‬ ‫}‬

‫‪187‬‬


string strPK = dt.PrimaryKey[0].ColumnName; strTable += "<td>Register</td>"; strTable += "</tr>"; string anchor; for (int i = 0; i < dt.Rows.Count; i++) { strTable += "<tr>"; for (int j = 0; j < dt.Columns.Count; j++) { strTable += "<td>" + dt.Rows[i][dt.Columns[j].ColumnName] + "</td>"; } anchor = "<a href=RegistrationForm.aspx?" + strPK + "=" + dt.Rows[i][strPK].ToString() + ">Register</a>"; strTable += "<td>" + anchor + "</td>"; strTable += "</tr>"; } strTable += "</table>"; return strTable; } HTTP Get Method ‫تمرير بارامترات بين الصفحات بواسطة‬ :‫القاعدة العامة لكيفية تمرير بارامترات معينة مع قيم لهذه البرامترات الى صفحة معينة‬ .‫عند نقل المستخدم الى صفحة معينة نكتب بعد اسم الصفحة الرمز ? ثم قائمة البرامترات‬ ‫) يفصل بين االزواج‬Name=Value Pairs ‫القائمة عبارة عن سلسلة من االزواج (قيمة=اسم‬ :& ‫الرمز‬ DemoPage.aspx?Name1=Value1&Name2=Value2 ‫مثال‬ <a href=RegistrationForm.aspx?CourseID=1>Register</a> .CourseID ‫ واسمه‬RegistrationForm.aspx ‫الحظ أننا نرسل هنا بارامترا واحدا الى الصفحة‬ .1 ‫أما قيمته فهي‬ :‫مثال آخر‬ Response.Redirect("Add.aspx?Num1=5&Num2=7"); ‫ والثاني اسمه‬5 ‫ وقيمته‬Num1 ‫ األول اسمه‬.Add.aspx ‫هنا نرسل بارامترين الى الصفحة‬ :HTTP Get Method ‫ يُطلق على هذه الطريقة االسم‬.7 ‫ وقيمته‬Num2 ‫كيف تستخرج الصفحة التي ارسلت اليها القيم هذه القيم؟‬ :‫ وفق الطريقة التالية‬Request ‫بمساعدة الكائن‬ ParamterType ParamterName = (ParamterType)Request.QueryString["ParamterName"];

188


:‫ استخراج البارامتر الذي أرسل في المثال األول‬:‫مثال‬ string strID=Request.QueryString["CourseID"].ToString(); :‫ استخراج البارامتر الذي أرسل في المثال األول‬:‫مثال‬ int N1 = int.Parse(Request.QueryString["Num1"].ToString()); int N2 = int.Parse(Request.QueryString["Num2"].ToString());

189


‫تمارين‬ ‫تحرير ارتباطات خارجية‬ ‫المواضيع‬ ‫▪‬

‫استعمال الفئة ‪Dbase.cs‬‬

‫▪‬

‫استعمال كائنات من الفئة ‪DataTable‬‬

‫▪‬

‫‪SQL: SELECT, DELETE, UPDATE, INSERT INTO‬‬

‫المطلوب‬ ‫عليكم برمجة جميع األجزاء المطلوبة إلدارة االرتباطات الخارجية في موقع ما وفق ‪3 Level Architecture‬‬ ‫األجزاء المطلوبة‬ ‫▪‬

‫عرض االرتباطات الخارجية في صفحة خاصة‬

‫▪‬

‫حذف االرتباطات الخارجية الموجودة‬

‫▪‬

‫إضافة ارتباطات جديدة‬

‫▪‬

‫تعديل أو تحديث االرتباطات الخارجية الموجودة‬

‫قاعدة البيانات‬ ‫يجب إضافة الجدول ‪ UsefullLinks‬إلى القاعدة وفيه الحقول التالية‪:‬‬ ‫اسم الحقل‬

‫نوع الحقل‬ ‫‪ID‬‬

‫شرح‬

‫‪ Auto Number‬رقم تلقائي لكل ارتباط‪ :‬مفتاح‬ ‫رئيسي‬ ‫‪ String‬نص االرتباط وهو عبارة عن‬

‫‪Title‬‬

‫النص ألتشعبي الذي يظهر‬ ‫للمستخدم وتحته خط‬ ‫‪ String‬العنوان الكامل لالرتباط‬

‫‪URL‬‬

‫‪ DateTime‬تاريخ إضافة االرتباط‬

‫‪Date‬‬

‫عرض االرتباطات الخارجية في صفحة خاصة باسم ‪UsefullLinks.aspx‬‬

‫‪190‬‬


‫كتابة الفئة ‪ClassUsefullLinks‬‬ ‫يجب كتابة فئة باسم ‪ ClassUsefullLinks‬وفق متطلبات ومبنى الجدول ‪ UsefullLinks‬وفيها العمليات التالية‬ ‫اسم العملية‬

‫الشرح‬ ‫ُتعيد محتوى الجدول‬

‫)(‪public static DataTable GetAll‬‬

‫ُتعيد محتوى الجدول على شكل كود ‪ HTML‬فيه أسطر‬

‫)(‪public static DataTable GetAllAsHTML‬‬

‫وفي كل سطر وسم ‪ a‬أي ‪ Anchor‬لكل ارتباط تشعبي‪.‬‬ ‫يجب إضافة سطر جديد بين كل ارتباط من خالل الوسم‬ ‫>‪<br/‬‬ ‫تستخرج تفاصيل السجل الذي رقمه أعطي للكائن‪ .‬العملية‬

‫)(‪public void GetByID‬‬

‫تعبئ الكائن الحالي ‪ this‬بهذه التفاصيل‪.‬‬ ‫ُتضيف سجال جديدا الى القاعدة‪ .‬على المبرمج أن ينشئ‬

‫)(‪public void Insert‬‬

‫كائنا في الصفحة ويُزوده بالقيم التي أدخلها المُستخدم‪.‬‬ ‫ُتعدل سجال موجودا في القاعدة‪ .‬على المبرمج أن ينشئ‬

‫)(‪public void Update‬‬

‫كائنا في الصفحة ويُزوده بالقيم التي أدخلها المُستخدم‬ ‫وخاصة قيمة المُفتاح الرئيسي ‪.ID‬‬ ‫تحذف سجال من القاعدة‪ .‬على المبرمج أن ينشئ كائنا في‬

‫)(‪public void Delete‬‬

‫الصفحة ويُزوده بقيمة المُفتاح الرئيسي ‪.ID‬‬

‫الوصول إلى هذه الصفحة يتم من خالل النقر على عنصر القائمة الرئيسية‪ .‬الحظ أن هذه الصفحة مشتقة من الـ‬

‫‪191‬‬


‫‪ .MasterPage‬محتوى الصفحة أي االرتباطات الخارجية يجب أن يؤخذ من قاعدة البيانات (من الجدول‬ ‫‪ .)UsefullLinks‬يجب استعمال األداة ‪ asp:Label‬لعرض االرتباطات الخارجية‪.‬‬ ‫حذف‪ ،‬إضافة وتحديث االرتباطات الخارجية‬ ‫هذه العمليات يجب أن تكون متوفرة في صفحة واحدة باسم ‪ .UsefullLinksEditor.aspx‬الوصول إلى هذه‬ ‫الصفحة يتم من خالل النقر على عنصر القائمة ‪ UsefullLinksEditor‬الموجود داخل العنصر ‪ Admin‬في‬ ‫القائمة الرئيسية‪ .‬الحظ أن هذه الصفحة مشتقة من الـ ‪ MasterPage‬وأنها محمية أي أن استعمالها متاح فقط‬ ‫للمستخدمين المُسجلين‪.‬‬ ‫الصورة التالية تبين محتوى صفحة التحرير ‪UsefullLinksEditor.aspx‬‬

‫الصفحة تعرض عند ظهورها جميع االرتباطات الخارجية الموجودة في قاعدة البيانات‪ .‬عرضهم يتم بواسطة‬ ‫الجدول الظاهر في الصورة وفيه عامودان فيهما ارتباطات تشعبية لحذف وتحديث االرتباط الموجود في السطر‪ .‬هنا‬ ‫أيضا يجب استعمال األداة ‪ asp:Label‬لعرض تفاصيل االرتباطات الخارجية‪.‬‬ ‫على كل واحد من ارتباطات الحذف أن يحتوي كقيمة للخاصية ‪ href‬التابعة له القيمة‬ ‫‪ DeleteUsefullLink.aspx‬ومعها رقم االرتباط التشعبي الذي في السطر أي قيمة الحقل ‪ ID‬التابعة لالرتباط‬ ‫بحيث تكون الصيغة النهائية الرتباط الحذف كالتالي‪:‬‬ ‫>‪<a href=DeleteUsefullLink.aspx?ID=2>Delete</a‬‬ ‫على افتراض أن رقم االرتباط في السطر هو ‪2‬‬ ‫وأيضا على كل واحد من ارتباطات التحديث أن يحتوي كقيمة للخاصية ‪ href‬التابعة له القيمة‬ ‫‪ UpdateUsefullLink.aspx‬ومعها رقم االرتباط ألتشعبي الذي في السطر أي قيمة الحقل ‪ ID‬التابعة لالرتباط‬ ‫بحيث تكون الصيغة النهائية الرتباط التحديث كالتالي‪:‬‬ ‫>‪<a href=UpdateUsefullLink.aspx?ID=2>Delete</a‬‬ ‫‪192‬‬


‫على افتراض أن رقم االرتباط في السطر هو ‪2‬‬ ‫كيف تتم عملية الحذف؟‬ ‫عند النقر على ارتباط الحذف الموجود في السطر تظهر نافذة حوار للتأكد من أن المستخدم حقا يريد حذف السطر‪:‬‬ ‫لكي تظهر هذه النافذة يجب إضافة جافا سكريبت كود التالي الى الوسم ‪a‬‬ ‫";)'?‪onclick="return confirm('Are you sure‬‬

‫ثم فقط إذا نقر المستخدم على الزر ‪( OK‬هذا التصرف مبرمج وجاهز من قبل الوسم) يتم كما هو مبين أعاله‬ ‫التشعب أو االنتقال إلى صفحة باسم ‪ DeleteUsefullLink.aspx‬ومعها كبرامتر رقم االرتباط ألتشعبي‬ ‫المطلوب حذفه‪ .‬هذه الصفحة ال تحتوي على أية أدوات وإنما تقوم في معالج الحدث ‪ Page_Load‬فقط‬ ‫‪ .1‬باستخراج البارامتر الذي أرسل إليها بمساعدة العملية ‪ QueryString‬التابعة للكائن ‪Request‬‬ ‫‪ .2‬ومن ثم تنفيذ أمر الحذف بواسطة العملية ‪ Delete‬التابعة للفئة ‪.ClassUsefullLinks‬‬ ‫‪ .3‬بعد ذلك يتم تحويل المستخدم إلى الصفحة التي جاء منها وهي الصفحة ‪UsefullLinksEditor.aspx‬‬ ‫وبالتالي يتم بناء جدول االرتباطات الخارجية الموجودة من جديد من قاعدة البيانات ويظهر حينها الجدول‬ ‫بدون االرتباط الذي حُذف‪.‬‬ ‫الكود المُقترح لذلك‪:‬‬ ‫)‪protected void Page_Load(object sender, EventArgs e‬‬ ‫‪{ try‬‬ ‫استخراج البارامتر ‪{ // 1‬‬ ‫;)(‪string strID = Request.QueryString["ID"].ToString‬‬ ‫تنفيذ أمر الحذف ‪// 2‬‬ ‫;)(‪ClassUsefullLinks obj = new ClassUsefullLinks‬‬ ‫;‪obj.ID = strID‬‬ ‫;)(‪obj.Delete‬‬ ‫تحويل المستخدم إلى الصفحة التي جاء منها ‪// 3‬‬ ‫;)"‪Response.Redirect("UsefullLinksEditor.aspx‬‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫;‪LabelMessage.Text = ex.Message‬‬ ‫}‬ ‫}‬

‫‪193‬‬


‫كيف تتم عملية التحديث؟‬ ‫عند النقر على ارتباط التحديث الموجود في السطر يتم كما هو مبين أعاله التشعب أو االنتقال إلى صفحة باسم‬ ‫‪ UpdateUsefullLink.aspx‬ومعها كبرامتر رقم االرتباط التشعبي المطلوب تحديثه‪ .‬هذه الصفحة تحتوي على‬ ‫النموذج التالي‪:‬‬

‫يجب استعمال ‪ Data Validation Controls‬لهذا النموذج‬ ‫>… ‪<asp:RegularExpressionValidator‬‬ ‫>… ‪<asp:RequiredFieldValidator‬‬ ‫تقوم الصفحة في معالج الحدث ‪Page_Load‬‬ ‫‪ .1‬باستخراج البارامتر الذي أرسل إليها بمساعدة العملية ‪ QueryString‬التابعة للكائن ‪Request‬‬ ‫‪ .2‬ومن ثم يتم استدعاء العملية ‪ GetByID‬التابعة للفئة ‪ ClassUsefullLinks‬وتعبئة الصناديق بالقيم‬ ‫التي أعيدت بواسطة الكائن‪.‬‬

‫‪194‬‬


:‫الكود المُقترح‬ // UpdateUsefullLink.aspx.cs protected void Page_Load(object sender, EventArgs e) { if (!((bool)Session["ValidUser"])) Response.Redirect("Login.aspx"); if (!IsPostBack) { try { int ID = int.Parse(Request.QueryString["ID"].ToString()); if (ID != 0) FillTextBoxes(ID); } catch (Exception ex) { LabelMessage.Text = ex.Message; } } } private void FillTextBoxes(int ID) { ClassUsefullLinks obj = new ClassUsefullLinks(); obj.ID = ID; obj.GetByID(); TextBoxTitle.Text = obj.Title; TextBoxURL.Text = obj.URL; } CausesValidation="false" .‫ يُفرّغ الصناديق من محتواها‬Reset ‫النقر على الزر‬ ‫ يعيد المستخدم إلى الصفحة التي جاء منها وهي الصفحة‬Cancel‫النقر على الزر‬ CausesValidation="false" .‫ بدون حفظ التغييرات‬UsefullLinksEditor.aspx ‫ يحفظ المعطيات في قاعدة البيانات ثم يعيد المستخدم إلى الصفحة التي جاء منها وهي‬Save ‫النقر على الزر‬ ‫ وبالتالي يتم بناء جدول االرتباطات الخارجية من جديد من قاعدة البيانات‬UsefullLinksEditor.aspx ‫الصفحة‬ ً ‫ التاريخ الذي يجب حفظه لالرتباط هو التاريخ‬.‫حديثا‬ ‫ويظهر حينها الجدول وفيه االرتباط الذي أضيف‬ DateTime.Now

195


‫كيف تتم عملية إضافة ارتباط جديد؟‬ ‫النموذج الذي نحتاجه لتمكين المستخدم من تحرير ارتباط جديد هو نفس النموذج الذي صممناه لعملية التحديث في‬ ‫الصفحة ‪ .UpdateUsefullLink.aspx‬الوصول إلى هذه الصفحة يتم هذه المرة في معالج الحدث‬ ‫‪ ButtonNew_Click‬التابع للزر ‪ ButtonNew‬الموجود في الصفحة ‪.UsefullLinksEditor.aspx‬‬ ‫ولكن كيف نفرق بين هاتين الحالتين‪ :‬التحديث واإلضافة؟‬ ‫الحل‪ :‬عند التحديث لدينا رقم ارتباط أكبر من ‪ 0‬ألنه ‪ .Auto Number‬ولذلك بإمكاننا أن نرسل للصفحة القيمة ‪0‬‬ ‫للداللة على أننا بصدد إنشاء ارتباط جديد‪ .‬لذلك يظهر معالج الحدث كالتالي‪:‬‬ ‫)‪protected void ButtonNew_Click(object sender, EventArgs e‬‬ ‫{‬ ‫;)"‪Response.Redirect("EditUsefullLinks.aspx?ID=0‬‬ ‫}‬ ‫وعليه فان على معالج الحدث ‪ ButtonSave_Click‬أن يعمل كما يلي‪:‬‬

‫استخراج البارامتر ‪ ID‬الذي أرسل بمساعدة العملية‬ ‫‪ QueryString‬التابعة للكائن ‪Request‬‬ ‫يفحص قيمة الـ ‪ ID‬ومن ثم‬

‫‪Yes‬‬

‫‪No‬‬ ‫‪ID=0‬‬

‫تجهي أمر ‪INSERT INTO‬‬ ‫وتنفيذ‬

‫تجهي أمر ‪UPDATE‬‬ ‫وتنفيذ‬

‫‪196‬‬


:‫الكود المُقترح‬ protected void ButtonSave_Click(object sender, EventArgs e) { try { string strSQL; int ID = int.Parse(Request.QueryString["ID"].ToString()); ClassUsefullLinks obj = new ClassUsefullLinks(); obj.Title = TextBoxTitle.Text; obj.URL = TextBoxURL.Text; obj.Date = DateTime.Now; if (ID == 0) obj.Insert(); else { obj.ID = ID.ToString(); obj.Update(); } Response.Redirect("UsefullLinksEditor.aspx"); } catch (Exception ex) { LabelMessage.Text = ex.Message; } }

197


‫‪English To Arabic Dictionary‬‬ ‫المواضيع‬ ‫▪‬

‫استعمال الفئة ‪Dbase.cs‬‬

‫▪‬

‫استعمال كائنات من الفئة ‪DataTable‬‬

‫▪‬

‫‪SQL: SELECT, DELETE, UPDATE, INSERT INTO‬‬

‫المطلوب‬ ‫عليكم برمجة جميع األجزاء المطلوبة إلدارة قاموس انجليزي عربي‬ ‫األجزاء المطلوبة‬ ‫▪‬

‫عرض محتوى القاموس في صفحة خاصة‬

‫▪‬

‫حذف كلمات موجودة في القاموس‬

‫▪‬

‫إضافة كلمات جديدة الى القاموس‬

‫▪‬

‫تعديل أو تحديث كلمات موجودة في القاموس‬

‫قاعدة البيانات‬ ‫يجب إضافة الجدول ‪ Dictionary‬إلى القاعدة وفيه الحقول التالية‪:‬‬ ‫اسم الحقل‬

‫شرح‬

‫نوع الحقل‬ ‫‪ID‬‬

‫‪Auto Number‬‬

‫رقم أوحد لكل كلمة‪ :‬مفتاح رئيسي‬

‫‪Word‬‬

‫‪String‬‬

‫الكلمة‬

‫‪Meaning‬‬

‫‪String‬‬

‫معنى الكلمة‬

‫عرض محتوى القاموس في الصفحة ‪Default.aspx‬‬

‫‪198‬‬


‫الوصول إلى هذه الصفحة يتم من خالل النقر على عنصر القائمة الرئيسية ‪ .Home‬الحظ أن هذه الصفحة مشتقة‬ ‫من الـ ‪ .MasterPage‬محتوى الصفحة (أي الكلمات الموجودة في القاموس) يجب أن يؤخذ من قاعدة البيانات‬ ‫(من الجدول ‪ .)Dictionary‬يجب استعمال األداة ‪ asp:Label‬لعرض الكلمات الموجودة في القاموس‪.‬‬ ‫حذف‪ ،‬إضافة وتحديث كلمات موجودة في القاموس‬ ‫هذه العمليات يجب أن تكون متوفرة في صفحة واحدة باسم ‪ .DictionaryEditor.aspx‬الوصول إلى هذه‬ ‫الصفحة يتم من خالل النقر على عنصر القائمة ‪ DictionaryEditor‬الموجود داخل العنصر ‪ Admin‬في القائمة‬ ‫الرئيسية‪ .‬الحظ أن هذه الصفحة مشتقة من الـ ‪ MasterPage‬وأنها محمية أي أن استعمالها متاح فقط للمستخدمين‬ ‫المُسجلين‪.‬‬ ‫الصورة التالية تبين محتوى صفحة التحرير ‪DictionaryEditor.aspx‬‬

‫الصفحة تعرض عند ظهورها جميع الكلمات الموجودة في قاعدة البيانات‪ .‬عرضهم يتم بواسطة الجدول الظاهر في‬ ‫الصورة وفيه عامودان فيهما ارتباطات تشعبية لحذف وتحديث الكلمة الموجودة في السطر‪ .‬هنا أيضا يجب استعمال‬ ‫األداة ‪ asp:Label‬لعرض تفاصيل الكلمات‪.‬‬ ‫على كل واحد من ارتباطات الحذف أن يحتوي كقيمة للخاصية ‪ href‬التابعة له القيمة ‪DeleteWord.aspx‬‬ ‫ومعها رقم الكلمة الذي في السطر أي قيمة الحقل ‪ ID‬التابعة للكلمة بحيث تكون الصيغة النهائية الرتباط الحذف‬ ‫كالتالي‪:‬‬ ‫>‪<a href=DeleteWord.aspx?ID=2>Delete</a‬‬ ‫على افتراض أن رقم الكلمة في السطر هو ‪2‬‬ ‫وأيضا على كل واحد من ارتباطات التحديث أن يحتوي كقيمة للخاصية ‪ href‬التابعة له القيمة‬ ‫‪ UpdateWord.aspx‬ومعها رقم الكلمة التي في السطر أي قيمة الحقل ‪ ID‬التابعة للكلمة بحيث تكون الصيغة‬ ‫النهائية الرتباط التحديث كالتالي‪:‬‬ ‫>‪<a href=UpdateWord.aspx?ID=2>Delete</a‬‬ ‫‪199‬‬


‫كيف تتم عملية الحذف؟‬ ‫عند النقر على ارتباط الحذف الموجود في السطر تظهر نافذة حوار للتأكد من أن المستخدم حقا يريد حذف السطر‪:‬‬

‫لكي تظهر هذه النافذة يجب إضافة جافا سكريبت كود التالي الى الوسم ‪a‬‬ ‫";)'?‪onclick="return confirm('Are you sure‬‬ ‫ثم فقط إذا نقر المستخدم على الزر ‪( OK‬هذا التصرف مبرمج وجاهز من قبل الوسم) يتم كما هو مبين أعاله‬ ‫التشعب أو االنتقال إلى صفحة باسم ‪ DeleteWord.aspx‬ومعها كبرامتر رقم الكلمة المطلوب حذفها‪ .‬هذه‬ ‫الصفحة ال تحتوي على أية أدوات وإنما تقوم بعملية الحذف في معالج الحدث ‪ Page_Load‬فقط‪.‬‬ ‫كيف تتم عملية التحديث؟‬ ‫عند النقر على ارتباط التحديث الموجود في السطر يتم كما هو مبين أعاله التشعب أو االنتقال إلى صفحة باسم‬ ‫‪ UpdateWord.aspx‬ومعها كبرامتر رقم الكلمة المطلوب تحديثها‪ .‬هذه الصفحة تحتوي على النموذج التالي‪:‬‬

‫يجب استعمال ‪ Data Validation Controls‬لهذا النموذج‬

‫‪200‬‬


‫النقر على الزر‪ Cancel‬يعيد المستخدم إلى الصفحة التي جاء منها وهي الصفحة ‪DictionaryEditor.aspx‬‬ ‫بدون حفظ التغييرات‪CausesValidation="false" .‬‬ ‫النقر على الزر‪ Update‬يحفظ المعطيات في قاعدة البيانات ثم يعيد المستخدم إلى الصفحة التي جاء منها وهي‬ ‫الصفحة ‪DictionaryEditor.aspx‬‬ ‫كيف تتم عملية اضافة كلمة جديدة؟‬ ‫النموذج الذي نحتاجه لتمكين المستخدم من تحرير ارتباط جديد هو نفس النموذج الذي صممناه لعملية التحديث في‬ ‫الصفحة ‪ .UpdateWord.aspx‬الوصول إلى هذه الصفحة يتم هذه المرة في معالج الحدث‬ ‫‪ ButtonNew_Click‬التابع للزر ‪ ButtonNew‬الموجود في الصفحة ‪.DictionaryEditor.aspx‬‬ ‫كيف تتم عملية البحث عن كلمة؟‬ ‫النموذج الذي نحتاجه لتمكين المستخدم من البحث عن كلمة هو النموذج التالي‪:‬‬

‫‪201‬‬


‫؟‬GridView ‫ كيف نبرمج ذلك بواسطة‬،‫واآلن‬ ‫ هنا ومن أجل برمجة‬.‫قلنا سابقا بأن هذه األداة تمكننا من اضافة أدوات أخرى داخل خاليا الجدول وحسب الحاجة‬ ‫المطلوب في هذا المثال سوف نضيف الى األعمدة التي تضاف بشكل تلقائي عامودا جديدا نضع في خالياه أداة من‬ ‫ والسؤال اآلن هو أين نكتب ذلك؟ نكتب ذلك في مجال‬.‫ والتي تظهر كارتباط تشعبي‬asp:ButtonField ‫نوع‬ :‫< التابع لألداة‬Columns> ‫الوسم‬ <asp:GridView ID="GridViewCourses" runat="server" BackColor="#DEBA84" BorderColor="#DEBA84" BorderStyle="None" BorderWidth="1px" CellPadding="3" CellSpacing="2" DataKeyNames="CourseID" OnRowCommand="GridViewCourses_RowCommand" AutoGenerateColumns="true" HorizontalAlign="Center" > <FooterStyle BackColor="#F7DFB5" ForeColor="#8C4510" /> <RowStyle BackColor="#FFF7E7" ForeColor="#8C4510" /> <PagerStyle ForeColor="#8C4510" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="#A55129" Font-Bold="True" ForeColor="White" /> <Columns> <asp:ButtonField CommandName="Register" Text="Register"> </asp:ButtonField> </Columns> </asp:GridView> ‫ عندما ينقر المستخدم‬.CommandName ‫ تهمنا منها واحدة هي‬.‫ عدة خصائص‬asp:ButtonField ‫لألداة‬ ‫ ولذلك علينا أن نعالج هذا الحدث وأن نصرح‬.RowCommand ‫ الحدث‬GridView ‫على هذه األداة يرسل الـ‬ :‫ ونحدد اسم معالج الحدث كما يلي‬GridView ‫بذلك في خصائص الـ‬ OnRowCommand="GridViewCourses_RowCommand" ‫ لذلك ال بد من وجود امكانية لمعرفة‬GridView ‫هذا المعالج يكون مشتركا لجميع األدوات الموجودة في خاليا الـ‬ ‫ التي تأخذ كقيمة‬CommandName ‫ هذه اإلمكانية هي الخاصية‬.‫األداة التي سنعالج أمرها في معالج الحدث‬

202


‫اسما أوحدا لألمر (من هنا جاءت التسمية ‪ .)Command Name‬في مثالنا أعطينا هذه الخاصية القيمة‬ ‫‪ .Register‬واآلن دعونا نلقي نظرة على معالج الحدث وكيفية معالجته لألمر‪:‬‬

‫•‬

‫‪protected void GridViewCourses_RowCommand(object sender,‬‬ ‫)‪GridViewCommandEventArgs e‬‬ ‫{‬ ‫‪try‬‬ ‫{‬ ‫‪if (e.CommandName.Equals("Register"))// 1‬‬ ‫{‬ ‫‪int nRow = Convert.ToInt32(e.CommandArgument);// 2‬‬ ‫;‪string strID‬‬ ‫‪strID = GridViewCourses.DataKeys[nRow].Values[0].ToString();// 3‬‬ ‫‪Response.Redirect("RegistrationForm.aspx?Code=" + strID); // 4‬‬ ‫}‬ ‫}‬ ‫)‪catch (Exception ex‬‬ ‫{‬ ‫;‪LabelMessage.Text = ex.Message‬‬ ‫}‬ ‫}‬ ‫البارامتر ‪ e‬يأتي محمّال باسم األمر ‪ .e.CommandName‬ولذلك نفحص ان كان األمر هو‬ ‫‪( Register‬األمر رقم ‪)1‬‬

‫•‬

‫فان كان األمر كذلك علينا أن نعرف رقم السطر الذي نقر المستخدم عليه‪ .‬رقم السطر هذا نجده موجودا‬ ‫داخل الخاصية ‪ CommandArgument‬الموجودة في البارامتر ‪ .e‬طبعا الـ ‪ GridView‬هو الذي‬ ‫يخزن هذه القيمة في هذه الخاصية حين ارسال الحدث‪ .‬هذه الخاصية من نوع ‪ object‬ولذلك علينا تحويل‬ ‫القيمة الى عدد صحيح من خالل العملية ‪ ToInt32‬الموجودة في الفئة ‪( Convert‬األمر رقم ‪ .)2‬طبعا‬ ‫بامكاننا أن نقوم بعملية التحويل كالتالي‪:‬‬ ‫;)))‪int nRow = int.Parse(e.CommandArgument.ToString‬‬

‫•‬

‫بقي علينا أن نعرف كود الدورة‪ .‬لقد صرحنا في خصائص الـ ‪ GridView‬بأن قيمة الحقل ‪CourseID‬‬ ‫تعتبر مفتاحا لكل سطر‪ .‬فعلنا ذلك بواسطة الخاصية ‪ DataKeyNames‬والتي تأخذ كقيمة اسم ذلك‬ ‫المفتاح (باالمكان استعمال أكثر من اسم اذا كان المفتاح مركبا من عدة حقول كما مر معنا في ‪Access‬‬ ‫وهو ما يسمى ‪ .)Compound Key‬الـ ‪ GridView‬تخزن هذه القيم لكل سطر في المصفوفة الثنائية‬ ‫‪ .DataKeys‬ولذلك أخذنا من هذه الخاصية القيمة األولى الوحيدة )]‪ (Values[0‬للسطر رقم ‪nRow‬‬ ‫أي‪ GridViewCourses.DataKeys[nRow].Values[0] :‬وحولنا القيمة الى نص ألنها مخزنة‬ ‫من نوع ‪( Object‬األمر رقم ‪.)3‬‬

‫•‬

‫أخيرا أرسلنا هذه القيمة أي كود الدورة الى الصفحة ‪( RegistrationForm.aspx‬األمر رقم ‪)4‬‬

‫‪203‬‬


‫امكانيات التحرير المختلفة‬ ‫نريد تمكين مدير الموقع من‬ ‫•‬

‫اضافة دورات جديدة‬

‫•‬

‫حذف وتحديث دورات موجودة‬

‫هذه المرة ال نريد الخوض في كيفية برمجة ذلك "يدويا" وانما سندرس اإلمكانيات الموجودة لدى الـ ‪GridView‬‬ ‫لفعل ذلك‪.‬‬ ‫التصميم ال ُمقترح هو التالي‪:‬‬

‫المطلوب‪:‬‬ ‫األمر ‪ :Edit‬حين النقر عليه علينا "فتح" خاليا ذلك السطر للتحرير‪ .‬الصورة التالية توضح هذا الوضع‪:‬‬

‫الحظ أن األدوات التي أُظهرت في خاليا السطر هي من نوع ‪ TextBox‬لتمكين المستخدم من إدخال القيم التي‬ ‫يريد‪.‬‬ ‫الحظ أيضا أن مكان األمر ‪ Edit‬ظهر أمران هما ‪ Update‬و ‪.Cancel‬‬ ‫•‬

‫األمر ‪ :Update‬حين النقر عليه علينا إضافة تغييرات ذلك السطر إلى الـ ‪ GridView‬وإلى قاعدة‬ ‫البيانات‪.‬‬

‫•‬

‫األمر ‪ :Cancel‬النقر عليه يمثل عملية "التراجع" عن التغييرات أي ‪ .Undo‬هذا األمر أيضا يُغلق‬ ‫الخاليا ويعيدها إلى وضعها الطبيعي أي يظهر أدوات العرض‪.‬‬

‫‪204‬‬


‫األمر ‪ :Delete‬حين النقر عليه علينا حذف ذلك السطر من الـ ‪ GridView‬ومن قاعدة البيانات‪.‬‬ ‫األمر ‪ :Insert‬حين النقر عليه علينا إضافة ذلك السطر إلى الـ ‪ GridView‬وإلى قاعدة البيانات‪.‬‬ ‫الحقيقة أن لدى الـ ‪ GridView‬أكثر من امكانية لفعل ذلك‪ .‬سأبدأ هنا بشرح الطريقة التي تعتمد على القوالب أي‬ ‫على األداة بمعنى حقل قالب‪ .‬هذه األداة تمكننا من اضافة قوالب اضافية ومن أنواع مختلفة الى الخاليا‪ .‬هنالك ثالثة‬ ‫أنواع من القوالب التي سوف نستعملها هنا‪:‬‬ ‫•‬

‫‪ :ItemTemplate‬نصرح بداخله عن األداة التي ستظهر في الخلية عندما يظهر الـ ‪ GridView‬في‬ ‫الوضع العرض (أي الـ ‪.)View Mode‬‬

‫•‬

‫‪ :EditItemTemplate‬نصرح بداخله عن األداة التي ستظهر في الخلية عندما يظهر الـ ‪GridView‬‬ ‫في وضع التحرير (أي الـ ‪.)Edit Mode‬‬

‫•‬

‫‪ :FooterTemplate‬نصرح بداخله عن األداة التي ستظهر في الخلية عندما يظهر الـ ‪ GridView‬في‬ ‫وضع األضافة (أي الـ ‪ .)Insert Mode‬هذه الخاليا هي خاليا السطر األخير أي الـ ‪ .Footer‬الحظ أن‬ ‫سطر الـ ‪ Footer‬ال يظهر اال اذا كانت الخاصية ‪ ShowFooter‬التابعة للـ ‪.true GridView‬‬

‫ًإذا لكل خلية من خاليا الـ ‪ GridView‬توجد ثالث حاالت وهي‬ ‫•‬

‫‪View Mode‬‬

‫•‬

‫‪Edit Mode‬‬

‫•‬

‫‪Insert Mode‬‬

‫ولذلك علينا استعمال القوالب الثالث المذكورة أعاله‬ ‫)‪ )ItemTemplate, EditItemTemplate, FooterTemplate‬لكل عامود ودمجها داخل الـ‬ ‫‪ .TemplateField‬المقطع التالي يبين ذلك للعامود األول المسئول عن الـ ‪:CourseID‬‬

‫‪205‬‬


‫الخاصية‬

‫"‪<asp:TemplateField HeaderText="Course ID‬‬ ‫>"‪HeaderStyle-HorizontalAlign="Center‬‬ ‫>‪<HeaderStyle HorizontalAlign="Center"></HeaderStyle‬‬

‫‪ HeaderText‬تأخذ‬ ‫كقيمة اسم العامود‪.‬‬ ‫الحظ أنه ليس‬ ‫بالضرورة أن يكون‬

‫‪1‬‬

‫>‪<ItemTemplate‬‬ ‫"‪<asp:Label ID="lblCourseID" runat="server‬‬ ‫>'>‪Text='<%# Bind("CourseID") %‬‬ ‫>‪</asp:Label‬‬ ‫>‪</ItemTemplate‬‬

‫‪2‬‬

‫>‪<EditItemTemplate‬‬ ‫"‪<asp:TextBox ID="txtCourseID" runat="server‬‬ ‫>'>‪Text='<%# Bind("CourseID") %‬‬ ‫>‪</asp:TextBox‬‬ ‫>‪</EditItemTemplate‬‬

‫نفس اسم الحقل‪.‬‬

‫‪3‬‬

‫>‪<FooterTemplate‬‬ ‫> "‪<asp:TextBox ID="txtNewCourseID" runat="server‬‬ ‫>‪</asp:TextBox‬‬ ‫>‪</FooterTemplate‬‬ ‫>‪</asp:TemplateField‬‬

‫القالب ‪:ItemTemplate‬‬ ‫استعملنا في مجال الـ ‪ ItemTemplate‬لعرض قيمة كود الدورة األداة ‪ .Label‬الحظ أن هذه األداة تحصل على‬ ‫القيمة من خالل أسلوب الـ ‪ DataBinding‬وذلك عن طريق استدعاء العملية ‪ Bind‬على طريقة الـ ‪ ASP‬القديم‬ ‫أي بين الرموز >‪ .<%# %‬هذه العملية تأخذ كبارامتر اسم حقل موجود داخل جدول النتيجة وتعطي قيمة هذا‬ ‫الحقل في السطر الحالي للخاصية ‪ Text‬التابعة لألداة ‪ .Label‬هنا أعطينا هذه العملية االسم ‪ CourseID‬لعرض‬ ‫قيمة كود الدورة (أنظر إلى مجموعة األوامر رقم ‪ُ .)1‬تستدعى هذه العملية عند استدعاء العملية ‪DataBind‬‬ ‫المذكورة أعاله من الـ ‪Code Behind‬‬ ‫القالب ‪:EditItemTemplate‬‬ ‫استعملنا في مجال الـ ‪ EditItemTemplate‬لتحرير قيمة كود الدورة األداة ‪ .TextBox‬الحظ أن هذه األداة‬ ‫أيضا تحصل على القيمة من استدعاء العملية ‪ Bind‬بحيث تظهر القيمة المُراد تعديلها داخل صندوق النص (أنظر‬ ‫إلى مجموعة األوامر رقم ‪.)2‬‬

‫‪206‬‬


:FooterTemplate ‫القالب‬ ‫ (أنظر إلى مجموعة‬.TextBox ‫ إلدخال قيمة كود الدورة األداة‬FooterTemplate ‫استعملنا في مجال الـ‬ .)3 ‫األوامر رقم‬ ً ‫يجب اعطاء كل واحدة من األدوات المستعملة هنا اسمًا خا‬ Code ‫صا بها ألننا سوف نستعمل هذا األسم في الـ‬ .‫ ألستخراج القيم والقيام بالمطلوب مثل تخزين القيم في قاعدة البيانات أو حذفها منها وما الى ذلك‬Behind ‫ التغييرات الوحيدة‬.(Title, Date. Category, Hours) ‫ هذا يجب أن نكرره لجميع األعمدة‬aspx ‫كود الـ‬ .‫ واألمر الثاني تغيير أسماء األدوات‬Bind ‫الذي يجب أن نقوم بها هي تغيير اسم الحقل المًمرر للعملية‬

<asp:TemplateField HeaderText="Date" HeaderStyle-HorizontalAlign="Center"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate> <asp:Label ID="lblDate" runat="server" Text='<%# Bind("Date") %>'> </asp:Label> </ItemTemplate>

Date

<EditItemTemplate> <asp:TextBox ID="txtDate" runat="server" Text='<%# Bind("Date") %>'> </asp:TextBox> </EditItemTemplate> <FooterTemplate> <asp:TextBox ID="txtNewDate" runat="server" > </asp:TextBox> </FooterTemplate>

207


</asp:TemplateField> <asp:TemplateField HeaderText="Category" HeaderStyle-HorizontalAlign="Center"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate> <asp:Label ID="lblCategory" runat="server" Text='<%# Bind("Category") %>'> </asp:Label> </ItemTemplate>

Category

<EditItemTemplate> <asp:TextBox ID="txtCategory" runat="server" Text='<%# Bind("Category") %>'> </asp:TextBox> </EditItemTemplate> <FooterTemplate> <asp:TextBox ID="txtNewCategory" runat="server" > </asp:TextBox> </FooterTemplate> </asp:TemplateField>

<asp:TemplateField HeaderText="Title" HeaderStyle-HorizontalAlign="Center"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate> <asp:Label ID="lblTitle" runat="server" Text='<%# Bind("Title") %>'> </asp:Label> </ItemTemplate>

Title

<EditItemTemplate> <asp:TextBox ID="txtTitle" runat="server" Text='<%# Bind("Title") %>'> </asp:TextBox> </EditItemTemplate> <FooterTemplate> <asp:TextBox ID="txtNewTitle" runat="server" > </asp:TextBox> </FooterTemplate>

208


</asp:TemplateField> <asp:TemplateField HeaderText="Hours" HeaderStyle-HorizontalAlign="Center"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate> <asp:Label ID="lblHours" runat="server" Text='<%# Bind("Hours") %>'></asp:Label> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="txtHours" runat="server" Text='<%# Bind("Hours") %>'></asp:TextBox> </EditItemTemplate> <FooterTemplate> <asp:TextBox ID="txtNewHours" runat="server" > </asp:TextBox> </FooterTemplate> </asp:TemplateField>

Hours

<asp:TemplateField HeaderText="Update" HeaderStyle-HorizontalAlign="Center"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate> <asp:LinkButton ID="lnkEdit" runat="server" CommandName="Edit" Text="Edit"> </asp:LinkButton> </ItemTemplate> <EditItemTemplate> <asp:LinkButton ID="lbkUpdate" runat="server" CommandName="Update" Text="Update"> </asp:LinkButton> <asp:LinkButton ID="lnkCancel" runat="server" CommandName="Cancel" Text="Cancel"> </asp:LinkButton> </EditItemTemplate> <FooterTemplate> <asp:LinkButton ID="lnkAdd" runat="server" CommandName="Insert" Text="Insert" > </asp:LinkButton> </FooterTemplate> </asp:TemplateField>

209

Update+Edit+ Cancel+Insert


<asp:TemplateField HeaderText="Delete"> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <ItemTemplate > <asp:LinkButton ID="LinkButtonDelete" Runat="server" OnClientClick="return confirm('Are you sure?');" CommandName="Delete"> Delete </asp:LinkButton> </ItemTemplate> </asp:TemplateField>

Delete

‫( مع المقطع األول للعامود‬Title, Date. Category, Hours( ‫ لألعمدة‬aspx ‫ًإذا لو قارنا كود الـ‬ ً .‫ وأسماء األدوات‬Bind ‫متشابهة ج ًدا باستثناء اسم الحقل ال ُممرر للعملية‬ ‫ لوجدناها‬CourseID .)Delete ‫ و‬Update+Edit+Cancel+Insert ‫بقي أن نشرح المقاطع التابعة للعامودين األخيرين (أي‬ ‫ولنبدأ بعامود األوامر األول‬

LinkButton ‫ فهو عبارة عن األداة‬Edit ‫ أما‬.Insert ‫ و‬Edit ‫الحظ أن الخاليا تحتوي على االرتباطات التشعبية‬ .View Mode ‫ ألنها تظهر في الـ‬ItemTemplate ‫وهي موجودة في القالب‬

<ItemTemplate> <asp:LinkButton ID="lnkEdit" runat="server" CommandName="Edit" Text="Edit"> </asp:LinkButton> </ItemTemplate>

210


‫ وتظهر‬FooterTemplate ‫ ولكنها موجودة في القالب‬LinkButton ‫ فهي أيضا عبارة عن األداة‬Insert ‫وأما‬ .View Mode ‫أيضا في الـ‬

<FooterTemplate> <asp:LinkButton ID="lnkAdd" runat="server" CommandName="Insert" Text="Insert" > </asp:LinkButton> </FooterTemplate>

‫ الصورة التالية توضح هذا الوضع‬.‫ يفتح خاليا ذلك السطر للتحرير‬Edit ‫لقد ذكرت سابقا بأن النقر على االرتباط‬ :‫لسطر واح ٍد أي السطر الذي فُتح للتحرير‬

‫ لذلك نجد في القالب‬Cancel ‫ و‬Update ‫ ظهر أمران هما‬Edit ‫اضافة الى ذلك فان مكان األمر‬ :Cancel ‫ وواحدة للتراجع أي‬Update ‫ واحدة للتحديث أي‬LinkButton ‫ أداتي‬EditItemTemplate <EditItemTemplate> <asp:LinkButton ID="lbkUpdate" runat="server" CommandName="Update" Text="Update"> </asp:LinkButton> <asp:LinkButton ID="lnkCancel" runat="server" CommandName="Cancel" Text="Cancel"> </asp:LinkButton> </EditItemTemplate> ‫ ولذلك وضعنا هذه األداة في‬LinkButton ‫ فانه موجود دائما ويحتوي على األداة‬Delete ‫أما عامود الحذف أي‬ :ItemTemplate ‫مجال الـ‬ <asp:TemplateField HeaderText="Delete">

‫ تأخذ‬OnClientClick ‫الخاصية‬

<HeaderStyle HorizontalAlign="Center"></HeaderStyle>

‫ يتم تنفيذ‬JavaScript ‫كقيمة كود‬

<ItemTemplate > <asp:LinkButton ID="LinkButtonDelete" Runat="server" OnClientClick="return confirm('Are you sure?');" CommandName="Delete">

211

‫ هنا‬.(Client) ‫في حاسوب ال بون‬ ‫ التي تًظهر‬confirm ‫نستدعي الدالة‬ ‫نافذة حوار لتنبيه المستخدم من‬ .‫عملية الحذف‬


‫‪Delete‬‬ ‫>‪</asp:LinkButton‬‬ ‫>‪</ItemTemplate‬‬ ‫>‪</asp:TemplateField‬‬ ‫بهذا نكون قد أنهينا مرحلة التنسيق‪ .‬واآلن سننتقل الى شرح برمجة معالجات األحداث في الـ ‪.Code Behind‬‬ ‫سبق وأن تحدثنا عن الخاصية ‪ .CommandName‬عندما ينقر المستخدم على أداة ‪ ButtonField‬أو على أداة‬ ‫‪ LinkButton‬فان الـ ‪ GridView‬يرسل الحدث ‪ .RowCommand‬ولذلك كان علينا أن نعالج هذا الحدث وأن‬ ‫نصرح بذلك في خصائص الـ ‪ GridView‬ونحدد اسم معالج الحدث كما يلي‪:‬‬ ‫"‪OnRowCommand="GridViewCourses_RowCommand‬‬ ‫قلنا أيضا بأن هذا المعالج يكون مشتركا لجميع األدوات التي تكون من نوع "زر" أي ‪ ButtonField‬أو‬ ‫‪ LinkButton‬الموجودة في خاليا الـ ‪ GridView‬وبأنه ال بد من وجود امكانية لمعرفة األداة التي سنعالج أمرها‬ ‫في معالج الحدث‪ .‬وقلنا أيضا بأن هذه اإلمكانية هي الخاصية ‪ CommandName‬التي تأخذ كقيمة اسما أوحدا‬ ‫لألمر )‪ .(Unique Name‬الحظ أننا أعطينا هذه الخاصية عند جميع األدوات التي استعملناها في العامودين‬ ‫األخيرين أسماء خاصة ومُعرّ فة مسبقا من قبل ميكروسفت )‪ .(Edit, Delete, Insert, Cancel‬إذا نقر‬ ‫المستخدم على واحدة من هذه األدوات يقوم الـ ‪ GridView‬بإرسال الحدث ‪ RowCommand‬وحدثا آخرً ا متعل ًقا‬ ‫باألمر الذي تم النقر عليه‪ .‬الجدول التالي يُل ّخص لكل أمر األحداث التي يُرسلها الـ ‪:GridView‬‬ ‫قيمة الخاص ّية ‪CommandName‬‬ ‫"‪"Cancel‬‬

‫الشرح‬ ‫يمثل هذا األمر عملية "التراجع" عن التغييرات أي ‪ .Undo‬هذا‬ ‫األمر أيضا يُغلق الخاليا ويعيدها إلى وضعها الطبيعي أي يظهر‬ ‫أدوات العرض‪ .‬يُرسل الحدث ‪.RowCancelingEdit‬‬

‫"‪"Delete‬‬

‫يمثل هذا األمر عملية الحذف لسطر معين من أسطر الـ‬ ‫‪ GridView‬ومن قاعدة البيانات‪ .‬يُرسل الحدث‬ ‫‪ RowDeleting‬و ‪.RowDeleted‬‬

‫"‪"Edit‬‬

‫يمثل هذا األمر عملية "فتح" خاليا السطر للتحرير‪ .‬يُرسل الحدث‬ ‫‪.RowEditing‬‬

‫"‪"Update‬‬

‫يمثل هذا األمر عملية إضافة تغييرات السطر إلى الـ ‪GridView‬‬ ‫وقاعدة البيانات‪ .‬يُرسل الحدث ‪ RowUpdating‬والحدث‬ ‫‪.RowUpdated‬‬

‫"‪"Page‬‬

‫يمثل هذا األمر عملية التصفح‪ .‬يقوم أيضً ا بتحديد قيمة الخاصيّة‬

‫‪212‬‬


‫‪ CommandArgument‬وجعلها ‪ First, Last, Next‬أو‬ ‫‪ Prev‬أو رقم صفحة معينة وذلك تبعً ا لما يقوم به المستخدم حين‬ ‫تصفح محتوى الـ ‪ .GridView‬يُرسل الحدث‬ ‫‪ PageIndexChanging‬والحدث‬ ‫‪.PageIndexChanged‬‬ ‫"‪"Select‬‬

‫يمثل هذا األمر عملية تظليل أو اختيار السطر‪ .‬يُرسل الحدث‬ ‫‪ SelectedIndexChanging‬والحدث‬ ‫‪.SelectedIndexChanged‬‬

‫"‪"Sort‬‬

‫يمثل هذا األمر عملية ترتيب محتوى الـ ‪ GridView‬حسب‬ ‫معين‪ .‬يُرسل الحدث ‪ Sorting‬والحدث ‪.Sorted‬‬ ‫عامو ٍد‬ ‫ٍ‬

‫الحظ أن معنى ارسال الـ ‪ GridView‬حدثا معينا بعد تفعيل أمر معين من األوامر الموجودة في الجدول أعاله أنه‬ ‫علينا معالجة ذلك الحدث وبانه ان لم نفعل ذللك فإننا نحصل ‪ at run time‬على استثناء يبن بأن األداة أرسلت حدثا‬ ‫لكنه غير معالج‪ .‬الصورة التالية تبين ذلك الخطأ‪:‬‬

‫الجدول التالي يلخص بعض األحداث التي ُيرسلها الـ ‪ GridView‬والتي تهمنا هنا في المثال‪:‬‬ ‫الحدث‬ ‫‪PageIndexChanged‬‬

‫الشرح‬ ‫يُرسل كلما غير المستخدم رقم الصفحة من خالل النقر على‬ ‫األرقام التي تظهر عادة في أسفل الـ ‪ GridView‬الصورة التالية‬ ‫تبين ذلك‬

‫‪PageIndexChanging‬‬

‫‪.‬‬

‫يُرسل كلما كان المستخدم على وشك تغيير رقم الصفحة‪ .‬أي أن‬ ‫المستخدم نقر على رقم الصفحة لكن العملية لم تنتهي بعد‪.‬‬

‫‪RowCancelingEdit‬‬

‫يُرسل كلما نقر المستخدم على األمر ‪ Cancel‬لكن معالجة األمر‬ ‫لم تنتهي بعد‪ .‬بإمكاننا معالجة هذا الحدث مثال من أجل اعادة بناء‬ ‫الـ ‪ GridView‬واغالق خاليا التحرير كما سيبين ذلك الح ًقا‪.‬‬

‫‪RowCommand‬‬

‫تم شرحه أعاله‪.‬‬

‫‪213‬‬


‫‪RowUpdated‬‬

‫يُرسل كلما انتهت عملية تحديث السطر‪ .‬بإمكاننا معالجة هذا‬ ‫الحدث مثال من أجل ابراز األسطر التي تم تحديثها في الجلسة‬ ‫الحالية من خالل تلوينها بلون يبرز ذلك‪.‬‬

‫‪RowUpdating‬‬

‫يُرسل كلما يطلب المستخدم تحرير سطر ما من خالل النقر على‬ ‫األمر ‪ ،Update‬لكن قبل أن تنتهي العملية‪.‬‬

‫‪RowDeleting‬‬

‫يُرسل كلما يطلب المستخدم حذف سطر ما من خالل النقر على‬ ‫األمر ‪ ،Delete‬لكن قبل أن تنتهي العملية‪.‬‬

‫عليك أن تالحظ الفرق بين األحداث التي ُتخبر بأن أمرا ما على وشك أن يحدث أي ُترس ُل قبل االنتهاء من تنفيذ أمر‬ ‫ما (في هذه الحالة ينتهي اسم الحدث بالنص ‪ )ing‬وبين تلك التي ًترس ًل بعد االنتهاء من تنفيذ أمر ما (في هذه الحالة‬ ‫ينتهي اسم الحدث بالنص ‪.)ed‬‬ ‫الصورة التالية تبين جمع األحداث التي يُرسلها الـ ‪:GridView‬‬

‫‪214‬‬


‫واآلن نأتي الى قسم البرمجة‪ .‬أوال علينا توسيع الفئة ‪ ClassCourses‬بحيث تحتوي على العمليات الالزمة‬ ‫إلضافة وحذف وتعديل سجالت‪ .‬الجدول التالي يحتوي على وصف لهذه العمليات‪:‬‬ ‫‪ClassCourses‬‬ ‫العملية ‪ /‬الصفة‬

‫الشرح‬ ‫عملية بنائية تتلقى قيمًا لجميع الصفات‬

‫‪public ClassCourses(string courseID,‬‬ ‫‪string date, string category, string title,‬‬ ‫)‪string hours‬‬

‫ُتضيف سجال جدي ًدا الى الجدول‪ .‬قيم هذا السجل يجب أن‬

‫()‪public void Insert‬‬

‫تكون مُعطاة للكائن الحالي‪.‬‬ ‫ُتحتلن سجال موجو ًدا في الجدول‪ .‬قيم هذا السجل يجب‬

‫)‪public void Update(string OldCourseID‬‬

‫أن تكون مُعطاة للكائن الحالي‪ .‬العملية تتلقى قيمة المُفتاح‬ ‫القديم لهذا السجل أي قيمة المفتاح كما كانت في الجدول‬ ‫قبل التغيير‪ .‬راجع آلية الحصول على هذه القيمة الح ًقا‬ ‫عند استعمال هذه العملية‪.‬‬ ‫تحذف سجال موجو ًدا في الجدول قيمة المُفتاح الرئيسي‬

‫)(‪public void Delete‬‬

‫(أي رقم الكورس) يجب أن تكون مُعطاة للكائن الحالي‪.‬‬ ‫واليك برمجة هذه العمليات‬ ‫)(‪public void Insert‬‬ ‫{‬ ‫;" ]‪string strSQL = "INSERT INTO [Courses‬‬ ‫;" )]‪strSQL += " ([CourseID], [Date], [Category], [Title], [Hours‬‬ ‫;")}‪strSQL += " VALUES ({0}, #{1}#, '{2}', '{3}', {4‬‬ ‫‪strSQL = string.Format(strSQL,‬‬ ‫;)‪CourseID, Date, Category, Title, Hours‬‬ ‫;)"‪Dbase.ChangeTable(strSQL, "Db.mdb‬‬ ‫}‬ ‫الحظ كيف نقوم بتجهيز القيم التي نريد ادخالها الى قاعدة البيانات بحيث نحيط قيم الحقول النصيّة بالشطائر الفردية‬ ‫'…' وقيمة التاريخ بالرمز‪.#‬‬ ‫العملية الستاتية ‪ string.Format‬تعمل بطريقة شبيهة بكيفية عمل العملية ‪ .Console.WriteLine‬فهي تتلقي‬ ‫ن ً‬ ‫صا فيه رموز رقابة … ‪ {0}, {1},‬لكل قيمة تأتي في قائمة البارمترات ومن ثم تقوم بتعويض كل قيمة في المكان‬

‫‪215‬‬


‫ هذه الطريقة أسهل وأوضح من طريقة الوصل التي‬.‫صا جدي ًدا بعد انتهاء عملية التعويض لكل القيم‬ ً ‫المُعد لها وتعيد ن‬ .‫اعتدنا عليه‬ :‫ختم العملية‬ public static string Format(string format, params object[] args); ‫ نص الرقابة‬:format ‫ قائمة البارامترات‬:args public void Update(string OldCourseID) { string strSQL = "UPDATE [Courses] "; strSQL += " SET [Date]=#{0}#, [Category]='{1}', [Title]='{2}'," ; strSQL += " [Hours]={3}, [CourseID]={4} "; strSQL += " WHERE [CourseID]={5}"; strSQL = string.Format(strSQL, Date, Category, Title, Hours, CourseID, OldCourseID); Dbase.ChangeTable(strSQL, "Db.mdb"); } public void Delete() { string strSQL = "Delete * FROM [Courses] WHERE [CourseID]={0}"; strSQL = string.Format(strSQL, courseID); Dbase.ChangeTable(strSQL, "Db.mdb"); }

216


:Insert ‫ وكيفية معالجته لألمر‬RowCommand ‫واآلن دعونا نلقي نظرة على معالج الحدث‬

‫ لمعالجة أي استثناء يحصل خاصة في‬try/catch ‫ على جميع معالجات األحداث أن تستعمل‬:‫مالحظة‬ ‫قد يُحدث استثناءان‬SQL ‫ ألن العمل مع قاعدة البيانات وتنفيذ أوامر‬ClassCourses ‫عمليات الفئة‬ .‫خالل زمن التشغيل‬ protected void GridViewCourses_RowCommand(object sender, GridViewCommandEventArgs e) { try { if (e.CommandName.Equals("Insert")) // 1 { // Save the data in the DB InsertCourse(); // 2 // Refresh the GridView FillGrid(); // 3 } } catch (Exception ex) { LabelMessage.Text = ex.Message; } } private void InsertCourse() { // Gather the data to be inserted TextBox txtCourseID = (TextBox)GridViewCourses.FooterRow.FindControl("txtNewCourseID"); TextBox txtDate = (TextBox)GridViewCourses.FooterRow.FindControl("txtNewDate"); TextBox txtCategory 2.1 = (TextBox)GridViewCourses.FooterRow.FindControl("txtNewCategory"); TextBox txtTitle = (TextBox)GridViewCourses.FooterRow.FindControl("txtNewTitle"); TextBox txtHours = (TextBox)GridViewCourses.FooterRow.FindControl("txtNewHours");

}

ClassCourses obj = new ClassCourses(txtCourseID.Text, txtDate.Text, txtCategory.Text, txtTitle.Text, txtHours.Text); obj.Insert(); 2.3

217

2.2


‫‪ .1‬البارامتر ‪ e‬يأتي محمّال باسم األمر ‪ .e.CommandName‬ولذلك نفحص ان كان األمر هو ‪Insert‬‬ ‫(األمر رقم ‪)1‬‬ ‫•‬

‫فان كان األمر كذلك نستدعي العملية المساعدة )(‪( InsertCourse‬األمر رقم ‪ )2‬وهي تقوم‬

‫•‬

‫بجمع المعلومات من أدوات السطر األخير أي الـ ‪ FooterRow‬وذلك بمساعدة العملية‬ ‫‪ FindControl‬التي تأخذ كبارامتر اسم أداة موجودة في الـ ‪ GridView‬وتعيد لنا مرجعا (أي‬ ‫‪ )Reference‬من نوع ‪Control‬على هذه األداة اذا وجدتها واآل فانها تعيد ‪ .null‬بما أن األداة‬ ‫المُرجعة من نوع ‪ Control‬ونحن نريدها من نوع ‪ TextBox‬فاننا نقوم بعملية ‪Down‬‬ ‫‪ Casting‬من ‪ Control‬الى ‪( TextBox‬األوامر رقم ‪:)2.1‬‬ ‫‪TextBox txtCourseID‬‬

‫;)"‪= (TextBox)GridViewCourses.FooterRow.FindControl("txtNewCourseID‬‬ ‫ختم العملية ‪FindControl‬‬ ‫;)‪public virtual Control FindControl(string id‬‬ ‫•‬

‫نكرر هذا األمر لجميع األدوات الموجودة في خاليا الـ ‪( FooterRow‬األمر رقم ‪.)2.1‬‬

‫•‬

‫بعد ذلك نقوم بإنشاء كائن من الفئة ‪ ClassCourses‬و ُنمرر له جميع القيم المطلوب ادخالها الى‬ ‫الجدول (األوامر رقم ‪.)2.2‬‬

‫•‬

‫وأخيرً ا نستدعي العملية ‪ Insert‬بواسطة هذا الكائن لتنفيذ أمر الـ ‪ SQL‬وحفظ القيم في قاعدة‬ ‫البيانات (األوامر رقم ‪.)2.3‬‬

‫‪ .2‬وأخيرً ا علينا تحديث (أي ‪ )Refresh‬الـ ‪ GridView‬من خالل استدعاء العملية )(‪( FillGrid‬األمر رقم‬ ‫‪)3‬‬

‫‪218‬‬


FillGrid ‫برمجة العملية‬ ‫ ما نقوم به هو‬.‫ال جديد في برمجة هذه العملية‬ ClassCourses ‫) انشاء كائن من الفئة‬1 ‫ بواسطة هذه الكائن‬GetAll ‫) استدعاء العملية‬2 ‫) نقوم بعملية الربط اذا لم يكن جدول النتيجة فارغا‬3 ‫) خالف ذلك ُنخبر المُستخدم بأننا لم نجد أي كورسات في القاعدة‬4 public void FillGrid() { try { ClassCourses obj = new ClassCourses(); // 1 DataTable Courses = obj.GetAll(); // 2 if (Courses.Rows.Count > 0) // 3 { GridViewCourses.DataSource = Courses; GridViewCourses.DataBind(); } else LabelMessage.Text = "No Courses Found!"; // 4 } catch (Exception ex) { LabelMessage.Text = ex.Message; } }

219


:Edit ‫معالجة األمر‬ protected void GridViewCourses_RowEditing(object sender, GridViewEditEventArgs e) { try { // Set the index of the row to be edited 1 GridViewCourses.EditIndex = e.NewEditIndex; // Refresh the GridView FillGrid();

2

// Extract the old Course ID to use it when updating the row string CourseID = ((TextBox)GridViewCourses.Rows[e.NewEditIndex]. FindControl("txtCourseID")).Text;

3

// Save the extracted old Course ID in ViewState to retrieve it later ViewState["OldCourseID"] = CourseID; } catch (Exception ex) { LabelMessage.Text = ex.Message; } } ‫ ولذلك نعطي رقم‬.e.NewEditIndex ‫ يأتي محمّال برقم السطر الذي يريد المُستخدم تحريره‬e ‫البارامتر‬

‫) حتى يقوم بفتح خاليا السطر للتحرير خالل عملية الـ‬1 ‫ (األمر رقم‬GridView ‫السطر هذا الى الـ‬ .)2 ‫ (األمر رقم‬FillGrid() ‫ التي تتم في العملية‬DataBinding ‫ نقوم باستخراج قيمة المُفتاح الرئيسي لهذا السطر الذي هو رقم الكورس المُراد‬3 ‫في مجموعة األوامر رقم‬ .Update ‫ لنستعمله عند القيام بعملية الـ‬ViewState ‫ ومن ثم نحفظه في‬.‫تعديله‬

220


:Update ‫معالجة األمر‬ protected void GridViewCourses_RowUpdating(object sender, GridViewUpdateEventArgs e) { try { TextBox txtCourseID = (TextBox)GridViewCourses.Rows[e.RowIndex].FindControl("txtCourseID"); TextBox txtDate = (TextBox)GridViewCourses.Rows[e.RowIndex].FindControl("txtDate"); TextBox txtCategory = (TextBox)GridViewCourses.Rows[e.RowIndex].FindControl("txtCategory"); TextBox txtHours = (TextBox)GridViewCourses.Rows[e.RowIndex].FindControl("txtHours"); TextBox txtTitle = (TextBox)GridViewCourses.Rows[e.RowIndex].FindControl("txtTitle"); ClassCourses obj = new ClassCourses(txtCourseID.Text, txtDate.Text, txtCategory.Text, txtTitle.Text, txtHours.Text); string OldCourseID = ViewState["OldCourseID"].ToString(); obj.Update(OldCourseID);

FillGrid();

2

3

4 5

GridViewCourses.EditIndex = -1;

6

} catch (Exception ex) { LabelMessage.Text = ex.Message; } } .e.RowIndex ‫ يأتي محمّال برقم السطر الذي يريد المُستخدم حتلنته‬e ‫البارامتر‬

‫ تقوم بجمع المعلومات من أدوات السطر‬1 ‫مجموعة األوامر رقم‬

‫ و ُنمرر له جميع القيم المطلوب ادخالها الى الجدول‬ClassCourses ‫بعد ذلك نقوم بإنشاء كائن من الفئة‬

.)2 ‫(األمر رقم‬ .)3 ‫ (األمر رقم‬ViewState ‫بعدها نستخرج قيمة المفتاح القديم الذي حفظناه سابقا في‬

‫ وحفظ القيم في قاعدة البيانات‬SQL ‫ بواسطة هذا الكائن لتنفيذ أمر الـ‬Update ‫بعدها نستدعي العملية‬

.)4 ‫(األمر رقم‬

221

1


‫•‬

‫بعدها (األمر رقم ‪ )5‬نعطي الصفة ‪ EditIndex‬التابعة للـ ‪ GridViewCourses‬القيمة ‪ -1‬للتدليل‬ ‫على انتهاء عملية التحرير بحيث يتم عرض الجدول في وضع الـ ‪ .ViewMode‬يحصل هذا خالل‬ ‫استدعاء العملية ‪ .FillGrid‬الحظ أن عملية الـ ‪ DataBinding‬تفحص قيمة الخاصية ‪EditIndex‬‬ ‫على النحو التالي‪:‬‬

‫•‬

‫األمر رقم ‪ 6‬يقوم بعرض محتوى الجدول من جديد لتظهر جميع المعلومات مُحتلنة‪.‬‬

‫‪222‬‬


:Delete ‫معالجة األمر‬ .‫ال شيء خاص هنا فقد تم سابقا شرح جميع التقنيات المُستعملة من قبل هذا المعالج‬ protected void GridViewCourses_RowDeleting(object sender, GridViewDeleteEventArgs e) { try { Label lblCourseID = (Label)GridViewCourses.Rows[e.RowIndex].FindControl("lblCourseID"); if (lblCourseID != null) { ClassCourses obj = new ClassCourses(); obj.CourseID = lblCourseID.Text; obj.Delete(); FillGrid(); } } catch (Exception ex) { LabelMessage.Text = ex.Message; } } :Cancel ‫معالجة األمر‬ protected void GridViewCourses_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e) { try { GridViewCourses.EditIndex = -1; FillGrid(); } catch (Exception ex) { LabelMessage.Text = ex.Message; } } ‫ التي تمثل رقم السطر الذي فُتح للتحرير القيمة‬EditIndex ‫كل ما نفعله هنا هو إعطاء الخاصيّة‬ .)View Mode ‫ التي تؤدي الى اعادة السطر الى وضع العرض (أي‬FillGrid ‫ واستدعاء العملية‬-1

223


‫تمرين رقم ‪1‬‬ ‫أضف الى ‪ GridViewCourses‬عامو ًدا جدي ًدا لألمر ‪ Select‬بحيث اذا نقر المُستخدم عليه يتم تظليل السطر‬ ‫على النحو التالي‪:‬‬

‫كما ُذكر آن ًفا فان عليك أن تعالج الحدث ‪ SelectedIndexChanging‬على النحو التالي‪:‬‬

‫‪protected void GridViewCourses_SelectedIndexChanging(object sender,‬‬ ‫)‪GridViewSelectEventArgs e‬‬ ‫{‬ ‫;‪GridViewCourses.SelectedIndex = e.NewSelectedIndex‬‬ ‫}‬ ‫لنفرض أنك تريد (مثال من خالل النقر على زر آخر) عرض تفاصيل اضافية عن الدورة التي تم اختيارها؟ كيف‬ ‫يمكنك معرفة السطر المُختار؟‬ ‫تمرين رقم ‪2‬‬ ‫معين من حقول األعمدة في الـ ‪ .GridView‬ولذلك نرى عاد ًة أسماء‬ ‫لحقل‬ ‫الترتب (أي ‪ )Sorting‬يكون عادة وف ًقا‬ ‫ٍ‬ ‫ٍ‬ ‫األعمدة كأنها ‪ LinkButtons‬اذا نقر المستخدم على أحدها يتم ترتيب محتوى ذلك الـ ‪ GridView‬بحسب حقل‬ ‫ذلك العامود‪ .‬وكلما نقر عليه تغير اتجاه الترتيب من تصاعدي الى تنازلي والعكس‪ .‬وتظهر أحيانا صورة صغيرة‬ ‫بجانب االسم للداللة على انجاه الترتيب‪ .‬الصورة التالية توضح ذلك‪:‬‬

‫الحظ أن الصورة الصغيرة تبين أن الترتيب اآلن هو تنازلي‪.‬‬ ‫المطلوب إلضافة الترتيب‪:‬‬ ‫‪ .1‬أضف الى ‪ GridViewCourses‬امكانيات ترتيب )‪ (Sorting‬من خالل دمج الكود التالي‪:‬‬

‫‪224‬‬


‫‪ .2‬أعط الخاصية ‪ AllowSorting‬التابعة لـ ‪ GridViewCourses‬القيمة ‪true‬‬ ‫‪ .3‬أضف الى ‪ GridViewCourses‬معالج الحدث التالي‪:‬‬ ‫"‪onsorting="GridViewCourses_Sorting‬‬ ‫‪GridViewCourses_Sorting‬للتفاصيل أنظر الى برمجة معالج الحدث‬ ‫‪ .4‬أضف الى كل ‪ TemplateField‬الخاصية ‪ SortExpression‬وأعطها كقيمة اسم الحقل الموجود في‬ ‫ذلك العامود‪ :‬مثال للعامود األول أكتب "‪SortExpression="CourseID‬‬ ‫‪ .5‬اضافة الصورة لبيان اتجاه الترتيب‬ ‫برمجة معالج الحدث ‪GridViewCourses_Sorting‬‬ ‫لألسف يوجد أخطاء في برمجة الترتيب عند الـ ‪ GridView‬وذلك ألن الخاصيّة ‪ e.SortExpression‬التي‬ ‫تحفظ اتجاه الترتيب تبقى محتفظة بالقيمة ‪ Ascending‬وال تتغير‪ .‬لذلك وجب علينا حفظ هذه القيمة في كائن‬ ‫‪ ViewState‬وقلب االتجاه في كل مرة ينقر فيها المستخدم على رأس العامود لتغيير اتجاه الترتيب‪ .‬كذلك األمر‬ ‫بالنسبة ألسم الحقل الذي نرتب بحسبه فإننا نحفظه أيضا في الخاصيّة (التابعة لنا) ‪.GridViewSortDirection‬‬ ‫بعد ذلك نقوم باستدعاء العملية ‪ SortGridView‬التي تحل محل العملية ‪ FillGrid‬فهي تقو بتعبئة الـ‬ ‫‪ GridView‬مع مراعاة الترتيب المطلوب‪:‬‬ ‫‪protected void GridViewCourses_Sorting(object sender, GridViewSortEventArgs‬‬ ‫)‪e‬‬ ‫{‬ ‫;‪string strSortExpression = e.SortExpression‬‬ ‫;‪ViewState["m_Sortexpresion"] = e.SortExpression‬‬ ‫اذا كان الترتيب تصاعديا فاآلن يريده المستخدم تنازليا ‪//‬‬ ‫)‪if (GridViewSortDirection == SortDirection.Ascending‬‬ ‫{‬ ‫;‪GridViewSortDirection = SortDirection.Descending‬‬ ‫;)"‪SortGridView(strSortExpression, "DESC‬‬ ‫}‬ ‫‪else‬‬ ‫{‬ ‫واال فالترتيب كان تنازليا واآلن يريده المستخدم تصاعديا ‪//‬‬ ‫;‪GridViewSortDirection = SortDirection.Ascending‬‬ ‫;)"‪SortGridView(strSortExpression, "ASC‬‬ ‫}‬ ‫}‬

‫‪225‬‬


SortGridView ‫العملية‬ ‫ فاذا‬.DataBinding ‫ الذي مُرر اليه عند القيام بعملية الـ‬DataTable ‫ بكائن الـ‬GridView ‫أيضا ال يحتظ الـ‬ :null ‫ فاننا نحصل على‬DataTable ‫قمنا بالعملية التالية ألسترجاع أو للحصول على الـ‬ DataTable dt = GridViewCourses.DataSource as DataTable; :ViewState ‫ في كائن‬DataBinding ‫لذلك وجب علينا حفظ هذا الكائن عند عملية الـ‬

GridViewCourses.DataSource = Courses; GVDataSource = Courses; GridViewCourses.DataBind(); ‫ التابعة له من‬Sort ‫ ألنه يمكننا من خالل الخاصيّة‬DataView ‫ الذي هو من نوع‬dv ‫الحظ أننا هنا نستعمل الكائن‬ ً ‫تحديد اسم الحقل الذي نريد أن نرتب بحسبه واتجاه الترتيب أي‬ ‫ هذا الكائن ننشئه ونعطيه من‬.)2 ‫ضا (األمر رقم‬ ‫) كقيمة للخاصية‬dv ‫) ومن ثم نعطيه (أي الكائن‬1 ‫ (األمر رقم‬GVDataSource ‫خالل الباني الكائن‬ DataBind ‫ وأخيرً ا نقوم باستدعاء العملية‬.)3 ‫ (األمر رقم‬GridViewCourses ‫ التابعة لـ‬DataSource :)4 ‫التي بدورها تراعي متطلبات الترتيب (األمر رقم‬ private void SortGridView(string sortExpression, string direction) { if (GVDataSource == null) return; DataView dv = new DataView(GVDataSource); // 1 dv.Sort = sortExpression + " " + direction; // 2 this.GridViewCourses.DataSource = dv; // 3 GridViewCourses.DataBind(); // 4 }

226


‫ والـ‬SortExpression ‫ والـ‬DataSource ‫المقاطع التالية تبين جميع الخاصيات التي عرفناها لحفظ الـ‬ :SortDirection public string SortExpression { get { if (ViewState["m_Sortexpresion"] == null) ViewState["m_Sortexpresion"] = this.GridViewCourses.DataKeyNames[0].ToString(); return ViewState["m_Sortexpresion"].ToString(); } set { ViewState["m_Sortexpresion"] = value; } } public DataTable GVDataSource { get { if (ViewState["m_GVDataSource"] == null) ViewState["m_GVDataSource"] = this.GridViewCourses.DataSource as DataTable; return ViewState["m_GVDataSource"] as DataTable; } set { ViewState["m_GVDataSource"] = value; } } public SortDirection GridViewSortDirection { get { if (ViewState["m_SortDirection"] == null) ViewState["m_SortDirection"] = SortDirection.Ascending; return (SortDirection)ViewState["m_SortDirection"]; } set { ViewState["m_SortDirection"] = value; } }

227

‫خاصيّة لحفظ الـ‬ ‫ في الحالة‬.SortExpression ‫االفتراضية نحفظ بداخله الحقل‬ ‫الذي ُعرّف على أنه مفتاح‬ DataKey ‫األسطر أي‬

‫خاصيّة لحفظ الـ‬ ‫ في الحالة‬.DataSource ‫االفتراضية نأخذ قيمة الـ‬ ‫ التابعة للـ‬DatSource .GridView

‫خاصيّة لحفظ الـ‬ ‫ في الحالة‬.SortDirection ‫االفتراضية يكون الترتيب‬ ‫تصاعديًا‬


‫اضافة الصورة لبيان اتجاه الترتيب‬ ‫السؤال هنا أين سنضيف الصورة وأين سيتم تغييرها من تصاعدي الى تنازلي؟ والجواب على ذلك هو عند االنتهاء‬ ‫ أو‬FillGrid ‫من انشاء سطر العناوين حيث يتم انشاؤه (وانشاء باقي األسطر) في كل مرة يُنادى فيها على العملية‬ ً .RowCreated ‫ الحدث هو‬.‫وخاصة عند النقر على اسم عامود من األعمدة لترتيبه‬ SortGridView ‫العملية‬ :‫ولذلك سنبرمج معالج هذا الحدث كالتالي‬ protected void GridViewCourses_RowCreated(object sender, GridViewRowEventArgs e) { ‫نقوم بإضافة الصورة فقط إذا‬ if (e.Row.RowType == DataControlRowType.Header) ‫كان نوع السطر الذي تم‬ { ‫ واسم‬Header ‫انشاؤه‬ if (String.Empty != SortExpression) ‫الحقل ليس فار ًغا‬ { AddSortImage(e.Row); } } } AddSortImage ‫العملية‬ void AddSortImage(GridViewRow headerRow) { int iCol = GetSortColumnIndex(SortExpression); if (-1 == iCol) { return; } // Create the sorting image based on the sort direction. Image sortImage = new Image(); if (SortDirection.Ascending == GridViewSortDirection) { sortImage.ImageUrl = "images/dwn.gif"; sortImage.AlternateText = "Ascending Order"; } else { sortImage.ImageUrl = "images/up.gif"; sortImage.AlternateText = "Descending Order"; } // Add the image to the appropriate header cell. headerRow.Cells[iCol].Controls.Add(sortImage); }

228


// This is a helper method used to determine the index of the // column being sorted. If no column is being sorted, -1 is returned. int GetSortColumnIndex(String strCol) { foreach (DataControlField field in GridViewCourses.Columns) { if (field.SortExpression == strCol) { return GridViewCourses.Columns.IndexOf(field); } } return -1; } 3 ‫تمرين رقم‬ .‫ بحيث يتم تلوين السطر الذي تم تحديثه باللون األصفر‬RowUpdated ‫أكتب معالجا للحدث‬

Paging 4 ‫تمرين رقم‬ :‫ من خالل دمج الكود التالي‬GridView ‫ الـ‬Paging ‫المطلوب إضافة إمكانيات التصفح أي‬ true ‫ القيمة‬GridViewCourses ‫ التابعة لـ‬AllowPaging ‫ أعط الخاصية‬.1 ‫ هذه القيمة تحدد عدد األسطر‬.5 ‫ القيمة‬GridViewCourses ‫ التابعة لـ‬PageSize ‫ أعط الخاصية‬.2 ‫ ُتضاف اليه‬PageSize ‫ على عدد أسطر أكبر من الـ‬GridView ‫ إذا احتوى الـ‬.‫في الصفحة الواحدة‬ .

‫أرقام الصفحات على شكل ارتباطات تشعبية‬ :‫ معالج الحدث التالي‬GridViewCourses ‫ أضف الى‬.3

onpageindexchanging="GridViewCourses_PageIndexChanging" :‫ األجراء التالي‬Code Behind ‫ أضف الى الـ‬.4

protected void GridViewCourses_PageIndexChanging(object sender, GridViewPageEventArgs e) { // ‫هنا يتم تحديث رقم الصفحة‬ GridViewCourses.PageIndex = e.NewPageIndex; // ‫هنا يتم تعبئة الـجدول مع مراعاة الترتيب‬ if (GridViewSortDirection == SortDirection.Ascending) SortGridView(SortExpression, "asc"); else SortGridView(SortExpression, "desc"); }

229


‫تمرين رقم ‪5‬‬ ‫المطلوب‬ ‫اآلن نريد عرض محتوى الجدول ‪ Users‬بواسطة ‪ GridView‬واعطاء امكانية لترتيب المحتوى بحسب كل‬ ‫عامود من خالل النقر على "رأس العامود" أو ما يسمى بخاليا العناوين )‪ .(Header cells‬األداة‬ ‫‪ BoundField‬توفر أفضل وأسهل امكانية لتعبئة األداة من خالل أسلوب الوصل )‪ (Data Binding‬وأيضً ا‬ ‫الترتيب‪.‬‬ ‫االسم ‪ BoundField‬معناه حقل موصول أو متصل لإلشارة الى أسلوب الـ ‪.Data Binding‬‬ ‫عملية الترتيب تتم من قبل معالج الحدث ‪ Sorting‬بنفس الطريقة والعمليات التي استعملناها في الفعالية رقم ‪2‬‬ ‫أعاله‪.‬‬

‫•‬

‫أعط الخاصية ‪ AllowSorting‬التابعة لـ ‪ GridViewUsers‬القيمة ‪true‬‬

‫•‬

‫مبنى الـ ‪ GridViewUsers‬نحصل عليه من خالل استعمال األداة ‪ BoundField‬لكل حقل من‬ ‫الحقول الثالث الظاهرة في جدول قاعدة البيانات أعاله‪.‬‬

‫•‬

‫أضف الى ‪ GridViewUsers‬معالج الحدث التالي‪:‬‬

‫•‬

‫برمجة هذا المعالج هي نفس برمجة المعالج ‪ GridViewCourses_Sorting‬المشروح أعاله‪.‬‬

‫"‪onsorting="GridViewUsers_Sorting‬‬

‫"‪<asp:GridView ID="GridViewUsers" runat="server‬‬ ‫"‪AutoGenerateColumns="false‬‬ ‫"‪AllowSorting="true‬‬ ‫>"‪onsorting="GridViewUsers_Sorting‬‬

‫‪230‬‬


‫الوصل مع قاعدة البيانات يكون من خالل‬ ‫التي تأخذ كقيمة اسم ‪DataField‬الخاصية‬ ‫الحقل كما يظهر في قاعدة البيانات‬ ‫أما تهيئة الترتيب فتكون من خالل الخاصية‬ ‫التي تأخذ كقيمة أيضًا ‪SortExpression‬‬ ‫اسم الحقل كما يظهر في قاعدة البيانات‬ ‫والذي نريد ترتيب محتوى الجدول بحسبه‬ ‫حين النقر على ذلك العامود‪.‬‬

‫>‪<columns‬‬ ‫"‪<asp:Boundfield HeaderText="User Name‬‬ ‫"‪DataField="UserName‬‬ ‫>‪SortExpression="UserName" /‬‬ ‫"‪<asp:Boundfield HeaderText="Password‬‬ ‫"‪DataField="Password‬‬ ‫>‪SortExpression="Password" /‬‬

‫"‪<asp:Boundfield HeaderText="Is Administrator‬‬ ‫"‪DataField="IsAdmin‬‬ ‫>‪SortExpression="IsAdmin" /‬‬ ‫>‪</columns‬‬ ‫>‪</asp:GridView‬‬

‫النتيجة تكون كالتالي‪:‬‬

‫الحظ أن عملية الوصل نقلت القيمة البوليانية ‪ IsAdmin‬أي التي من نوع ‪ Yes/No‬الى ‪ True‬و ‪ .False‬ال‬ ‫شك بأن هذه القيم هي قيم برمجية يفهمها المبرمجون أكثر من المستخدمين العاديين‪ .‬ولذلك علينا ترجمة هذه القيم‬ ‫الى مثالً نعم وكال وما شابه ذلك‪ .‬والسؤال اآلن أين بإمكاننا أن نفعل ذلك؟ احدى اإلمكانيات هي في معالج الحدث‬ ‫‪ RowDataBound‬الذي يحصل عند االنتهاء من عملية وصل سطر معين من أسطر الـ ‪( GridView‬وهذا‬ ‫يحصل طبعً ا لكل سطر)‪ .‬حينها بإمكاننا أن نفحص القيمة التي تم ادخالها الى الخلية‪ ،‬فان كانت ‪ True‬نكتب بدالً‬ ‫منها ‪ Yes‬وان كانت ‪ False‬نكتب ‪ No‬أو أي شي ٍء آخر‪:‬‬

‫‪231‬‬


protected void GridViewUsers_RowDataBound(object sender, GridViewRowEventArgs e) { //‫نفعل ذلك فقط ألسطر المعلومات وليس لسطر العناوين‬ if (e.Row.RowType == DataControlRowType.DataRow) { // 0 ‫ فرقم العامود األول‬،‫ هو الرقم التسلسلي للعامود‬2 ‫الرقم‬ // ‫ وهكذا‬1 ‫ورقم الثاني‬ string strValue = e.Row.Cells[2].Text; if (strValue.Equals("True")) e.Row.Cells[2].Text = "Yes"; else e.Row.Cells[2].Text = "No"; } } :‫والنتيجة اآلن أصبحت كالتالي‬

5 ‫تمرين رقم‬ .(Templates) ‫( بمساعدة القوالب‬Users) ‫ لتحرير المستخدمين‬UsersEditor ‫المطلوب برمجة صفحة باسم‬ :‫وضع العرض‬

232


‫وضع التحرير‬

‫ وفي الـ‬FooterTemplate ‫ الموجودة في الـ‬CheckBox ‫ األمر الجديد هنا هو األداة‬:‫مالحظة‬ ‫الحقيقة أنه ال يوجد فرق بين استعمال هذه األداة وغيرها سوى أننا هنا نتعامل مع قيم‬.EditItemTemplate :‫ التصميم ممكن أن يكون كالتالي‬.‫بوليانية‬ <asp:TemplateField HeaderText="Is Administrator" SortExpression="IsAdmin"> <EditItemTemplate> <asp:CheckBox ID="cbIsAdmin" runat="server" Checked='<%# Bind("IsAdmin") %>' /> </EditItemTemplate> <ItemTemplate> <asp:Label ID="lblIsAdmin" runat="server" Text='<%# Bind("IsAdmin") %>'></asp:Label> </ItemTemplate> <FooterTemplate> <asp:CheckBox ID="cbNewIsAdmin" runat="server" /> </FooterTemplate> </asp:TemplateField> ‫ كما ُذكر سال ًفا يُحدث مشكلة وهي أن‬RowDataBound ‫الحظت خالل تحضيري لهذ المثال أن استعمال الحدث‬ ‫ ولذلك أقترح استعمال دالة مساعدة باسم‬،Edit Mode ‫ ال تظهر كما يجب في الـ‬CheckBox ‫األداة‬ ‫ تقوم هذه العملية بتمشيط الـ‬.FillGrid() ‫ ننادي عليها في نهاية األجراء‬ChangeIsAdminValue() ‫ الى القيمة التي‬True ‫ و‬False ‫" من‬Is Administrator " ‫ سطرً ا سطرً ا وتغير قيمة الخلية‬GridView .‫نريدها‬

233


void ChangeIsAdminValue() { foreach (GridViewRow row in GridViewUsers.Rows) { Label lblIsAdmin = ((Label)row.FindControl("lblIsAdmin")); if (lblIsAdmin != null) { if (lblIsAdmin.Text.Equals("False")) lblIsAdmin.Text = "No"; else lblIsAdmin.Text = "Yes"; } } } ‫ وال نكون حينها بحاجة الى "ترجمة" القيم‬CheckBox ‫ أداة‬ItemTemplate ‫طب ًعا بإمكاننا أن نضع في الـ‬ :‫ حينها سيظهر الجدول كالتالي‬.ChangeIsAdminValue ‫بواسطة العملية‬

:‫والكود هو‬ <ItemTemplate> <asp:CheckBox Enabled="false" ID="cbITIsAdmin" runat="server" Checked='<%# Bind("IsAdmin") %>' /> </ItemTemplate> ً ‫اعلم أي‬ ‫ ولألمر‬INSERT INTO ‫ الى قاعدة البيانات لألمر‬Yes/No ‫ضا أن تمرير القيم لحقل من نوع‬ .)No ‫ لـ‬false‫ و‬Yes ‫ لـ‬true( false‫ و‬true ‫ يكون من خالل تمرير القيم‬UPDATE

234


‫تمرين رقم ‪6‬‬ ‫الهدف‪ :‬تمشيط الـ ‪ GridView‬سطرً ا سطرً ا‪.‬‬ ‫المطلوب برمجة صفحة باسم ‪ MarksEditor‬لتحرير عالمات الطالب بمساعدة القوالب )‪.(Templates‬‬ ‫التصميم المُقترح‪:‬‬

‫تفاصيل المطلوب‪:‬‬ ‫•‬

‫في معالج الحدث ‪ Page_Load‬يجب تعبئة القائمة المنسدلة بأسماء الدورات كـ ‪DataTextField‬‬ ‫وبكود الدورة كـ ‪( DataValueField‬العملية )(‪.)FillDDL‬‬

‫•‬

‫في معالج الحدث ‪ Page_Load‬وكلما اختار المستخدم دورة أخرى من القائمة المنسدلة يجب تعبئة‬ ‫الجدول بالتفاصيل التي تظهر فيه‪.‬‬

‫•‬

‫عند النقر على الزر‬

‫يجب القيام بتمشيط الجدول سطرً ا سطرً ا وحفظ العالمة في الجدول‬

‫‪ StudentsCourses‬في الحقل ‪ Mark‬للطالب الموجودة تفاصيله في السطر وللدورة التي ندخل‬ ‫عالماتها‪ .‬من أجل الحصول على الـ ‪ StudentID‬و الـ ‪ CourseID‬أقترح استعمال الخاصية‬ ‫‪ DataKeyNames‬التابعة للـ ‪ GrideView‬وذلك على النحو التالي‪:‬‬ ‫"‪ .DataKeyNames="StudentID,CourseID‬الحظ أننا نستعمل هنا حقلين كمفاتيح لكل سطر‪.‬‬ ‫طب ًعا علينا أن نشمل هذه الحقول في االستعالم الذي يعيد محتوى الجدول‪.‬‬

‫‪235‬‬


:StudentsCourses ‫ الى الجدول‬Mark ‫من أجل هذا الغرض قمنا بإضافة حقل جديد باسم‬

:‫العالقات هي كالتالي‬

:FillDDL ‫العملية‬ public void FillDDL () { try { DropDownListCourses.DataTextField = "Title"; DropDownListCourses.DataValueField = "CourseID"; ClassCourses obj = new ClassCourses(); DataTable Courses = obj.GetAll(); if (Courses.Rows.Count > 0) { DropDownListCourses.DataSource = Courses; DropDownListCourses.DataBind(); } else LabelMessage.Text = "No Courses Found!"; } catch (Exception ex) { LabelMessage.Text = ex.Message; } }

236


ClassStudentsCourses ‫الفئة‬ .‫ المشروح أعاله‬Courses ‫ و‬Students ‫ُتمثل هذه الفئة جدول الربط الذي يربط بين الجدولين‬ public class ClassStudentsCourses { private string iD, courseID, studentID; public ClassStudentsCourses() {} public string ID { get { return iD; } set { iD = value; } } public string CourseID { get { return courseID; } set { courseID = value; } } public string StudentID { get { return studentID; } set { studentID = value; } } public static DataTable GetAllByCourseID(string strCourseID) { string strSQL = "SELECT Students.StudentID, Students.FName, "; strSQL += " Students.LName, Courses.Title, "; strSQL += "Courses.CourseID, StudentsCourses.Mark "; strSQL += " FROM Students INNER JOIN "; strSQL += " (Courses INNER JOIN StudentsCourses "; strSQL += " ON Courses.CourseID = StudentsCourses.CourseID) ON "; strSQL += " Students.StudentID = StudentsCourses.StudentID "; strSQL += " WHERE (((Courses.CourseID)=" + strCourseID + "))"; return Dbase.SelectFromTable(strSQL, "Db.mdb"); }

237


public void Insert() { string strSQL; strSQL = "INSERT INTO [ClassStudentsCourses] ([CourseID],[StudentID])"; strSQL += " VALUES ({0}, {1})"; strSQL = string.Format(strSQL, CourseID, StudentID); Dbase.ChangeTable(strSQL, "Db.mdb"); } public void Update(string OldCourseID) { string strSQL; strSQL = "UPDATE [ClassStudentsCourses] "; strSQL += "SET [CourseID]={0}, [StudentID]={1} "; strSQL += " WHERE [ID]={2}"; strSQL = string.Format(strSQL, CourseID, StudentID, ID); Dbase.ChangeTable(strSQL, "Db.mdb"); } public void Delete() { string strSQL = "Delete * FROM [ClassStudentsCourses] WHERE [ID]={0}"; strSQL = string.Format(strSQL, courseID); Dbase.ChangeTable(strSQL, "Db.mdb"); } } :‫ واالستعالم المطلوب لتعبئة الجدول هو اآلتي‬FillGrid ‫العملية‬ private void FillGrid() { string strCourseID = DropDownListCourses.SelectedValue; DataTable dt = ClassStudentsCourses.GetAllByCourseID(strCourseID); if (dt.Rows.Count > 0) { GridViewMarks.DataSource = dt; GridViewMarks.DataBind(); } else ClassGlobals.ShowEmptyGrid(GridViewMarks, dt); }

238


ShowEmptyGrid ‫شرح العملية الستاتية‬ ‫ فار ًغا فيه سطر العناوين وسطر اضافي‬GridView ‫إذا لم نجد طالبًا مسجلين للدورة ال ُمختارة نقوم حينها بإنشاء‬ .No Records Found :‫فيه خلية واحدة مكتوب فيها النص‬ ‫ختم العملية‬ public static void ShowEmptyGrid(GridView gv, DataTable dt) :‫ادعاء الدخول‬ GridView ‫ مؤشر على الـ‬:gv ‫ وهو بالتالي يحتوي على مبنى الجدول أي‬SQL ‫ حصلنا عليه كنتيجة لتنفيذ أمر‬DataTable ‫ مؤشر على الـ‬:dt .‫األعمدة وأنواعها وما الى ذلك‬ ‫ في فئة الخدمة‬ShowEmptyGrid ‫ لذلك حفظنا العملية‬GridView ‫بما أن هذه العملية سنستعملها لكل‬ :‫ التالية‬ClassGlobals public class ClassGlobals { public static void ShowEmptyGrid(GridView gv, DataTable dt) { // ‫هنا ننشئ مصفوفة بطول عدد األعمدة لنحفظ فيها قيمة افتراضية لكل حقل بحسب نوع الحقل‬ object[] values = new object[dt.Columns.Count]; for (int i = 0; i < dt.Columns.Count; i++) { if (dt.Columns[i].DataType.ToString().Equals("System.Int32")) values[i] = default(System.Int32); else if (dt.Columns[i].DataType.ToString().Equals("System.String")) values[i] = default(System.String); else if (dt.Columns[i].DataType.ToString().Equals("System.DateTime")) values[i] = default(System.DateTime); else if (dt.Columns[i].DataType.ToString().Equals("System.Boolean")) values[i] = default(System.Boolean); else values[i] = null; } // ‫نضيف هذه المصفوفة كسطر جديد الى الجدول‬ // dt dt.Rows.Add(values); // ‫هنا نقوم بعملية الوصل‬ // of the GridView gv.DataSource = dt; gv.DataBind();

239


// Get the number of columns in order to set the ColumnsSpan int TotalColumns = gv.Rows[0].Cells.Count; // Clear any content of the grid // Empty the cells of any content gv.Rows[0].Cells.Clear(); // Create a new table cell gv.Rows[0].Cells.Add(new TableCell()); // Set the ColumnSpan to TotalColums gv.Rows[0].Cells[0].ColumnSpan = TotalColumns; // Put the message into the cell gv.Rows[0].Cells[0].Text = "No Records Found"; // Center the message in the cell gv.Rows[0].Cells[0].Attributes.CssStyle["text-align"] = "center"; } }

240


‫تمرين رقم ‪7‬‬ ‫الهدف‪ :‬تمشيط الـ ‪ GridView‬سطرً ا سطرً ا وفحص وضع األدوات (مثال ‪.)CheckBox‬‬ ‫أضف الى الجدول في صفحة آخر األخبار عامو ًدا جدي ًدا لالختيار وثالثة أزرار كالتالي‪:‬‬

‫‪ .1‬النقر على الزر ‪ Check All‬يؤدي الى اختيار جميع األسطر أي يقوم بجعل جميع صناديق االختيار في‬ ‫العامود ‪ Select‬مُختارة أي‬

‫‪.‬‬

‫‪ .2‬النقر على الزر ‪ Clear All‬يؤدي الى الغاء اختيار جميع األسطر أي يقوم بجعل جميع صناديق االختيار‬ ‫في العامود ‪ Select‬غير مُختارة أي‬

‫‪.‬‬

‫‪ .3‬النقر على الزر ‪ Delete‬يؤدي الى حذف جميع األسطر المُختارة بعد سؤال المستخدم بمساعدة الخاصيّة‬ ‫‪ OnClientClick‬التابعة للزر‪:‬‬ ‫";)'?‪OnClientClick="return confirm('Are you sure‬‬

‫‪241‬‬


‫تمرين رقم ‪8‬‬ ‫الهدف‪ :‬استعمال ‪ DropDownList‬داخل الـ ‪.GridView‬‬ ‫نريد تغيير صفحة تحرير المستخدمين بحيث نفرّق بين ثالثة مجموعات من المستخدمين‪:‬‬ ‫•‬

‫مدراء للموقع ‪Admin‬‬

‫•‬

‫معلمين ‪Teacher‬‬

‫•‬

‫طالب ‪Student‬‬

‫من أجل هذا الغرض تم تغيير الجدول ‪ Users‬في قاعدة البيانات ليصبح كالتالي‪:‬‬

‫التصميم ال ُمقترح‬ ‫وضع العرض‪:‬‬

‫‪242‬‬


:‫وضع التحرير‬

‫ ثابتة وليست مُخ ّزنة بجدول مستقل فمن السهل اضافتها الى القائمة‬Admin, Teacher, Student ‫بما أن القيم‬ :‫ كالتالي‬aspx ‫المُنسدلة مباشر ًة في ملف الـ‬ <asp:TemplateField HeaderText="User's Groupe" SortExpression="Groupe"> <ItemTemplate> <asp:Label ID="lblGroupe" runat="server" Text='<%# Bind("Groupe") %>' /> </ItemTemplate> <EditItemTemplate> <asp:DropDownList ID="ddlUserGroupes" runat="server" SelectedValue='<%# Bind("Groupe") %>'> <asp:ListItem Value="Admin" Text="Admin"></asp:ListItem> <asp:ListItem Value="Teacher" Text="Teacher"></asp:ListItem> <asp:ListItem Value="Student" Text="Student"></asp:ListItem> </asp:DropDownList> </EditItemTemplate> <FooterTemplate> <asp:DropDownList ID="ddlNewUserGroupes" runat="server"> <asp:ListItem Value="Admin" Text="Admin"></asp:ListItem> <asp:ListItem Value="Teacher" Text="Teacher"></asp:ListItem> <asp:ListItem Value="Student" Text="Student"></asp:ListItem> </asp:DropDownList> </FooterTemplate> </asp:TemplateField> :‫استخراج القيم من أجل اإلضافة يكون كالتالي‬ DropDownList ddlGroupes = (DropDownList)GridViewUsers.FooterRow.FindControl("ddlNewUserGroupes"); ddlGroupes.SelectedValue ‫بعدها نستعمل الصفة‬ :‫) يكون كالتالي‬UPDATE ‫استخراج القيم من أجل التعديل (أي‬ DropDownList ddlGroupes = (DropDownList)GridViewUsers.Rows[nRow].FindControl("ddlUserGroupes");

243


‫‪ GridView‬والتأكد من صحة االدخال‬ ‫ماذا عن استعمال أدوات التأكد من صحة االدخال ضمن الـ ‪GridView‬؟ طبعا بإمكاننا فعل ذلك‪ ،‬بل هذا أمر شبه‬ ‫مُلزم ومحبذ جدا‪ .‬هنالك بعض النقاط البسيطة التي سيتم شرحها هنا ألنها خاصة باستعمال هذه األدوات ضمن الـ‬ ‫‪ .GridView‬سنشرح القضية من خالل المثال التالي الذي يبين تحرير مكونات قاموس انجليزي عربي‪.‬‬

‫هنالك أداتان سيؤدي الضغط عليها الى تفعيل أدوات التأكد من صحة االدخال وهي‪ Insert :‬و ‪:Update‬‬ ‫فالضغط على ‪ Insert‬في الـ ‪ Footer‬عندما تكون الصناديق فارغة يظهر أن االدخال غير سليم‪:‬‬

‫‪244‬‬


:‫ عندما تكون الصناديق فارغة يظهر أن االدخال غير سليم‬Edit Mode ‫ في الـ‬Update ‫وأيضا الضغط على‬

‫ يجب أن يُفعل‬Edit Mode ‫ في الـ‬Update ‫ فالضغط على‬.‫ولكن يجب أن نحدد أية أدوات يجب أن ُتفعل ومتى‬ ‫فقط األدوات المسؤولة عن الصندوقين الموجودين في السطر الذي يريد المستخدم تحرير محتواه وال يجوز في هذه‬ ‫ كيف يمكننا تحقيق ذلك؟ يتم ذلك بواسطة الخاصية‬.Footer ‫الحالة تفعيل األدوات الموجودة في الـ‬ ‫ تمككنا هذه الخاصية من تقسيم أدوات التحقق من‬.‫ التابعة ألدوات التحقق من صحة االدخال‬ValidationGroup ‫صحة االدخال الى مجموعات وربط هذه األدوات باألداة الصحيحة التي اذا ضغط عليها المستخدم يتم تفعيل‬ :‫ كود التنسيق التالي يبين كيفية فعل ذلك‬.‫المجموعة التي تم ربطها به‬ <body> <form id="form1" runat="server"> <div> <asp:Label ID="LabelMsg" runat="server" Text=""></asp:Label> <br /> <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="ID" onrowediting="GridView1_RowEditing" ShowFooter="true" onrowcancelingedit="GridView1_RowCancelingEdit" onrowupdating="GridView1_RowUpdating" > <Columns> <asp:TemplateField HeaderText="ID" InsertVisible="False" SortExpression="ID"> <EditItemTemplate> <asp:Label ID="LabelEditID" runat="server" Text='<%# Eval("ID") %>'> </asp:Label> </EditItemTemplate> <ItemTemplate> <asp:Label ID="LabelID" runat="server" Text='<%# Bind("ID") %>'> </asp:Label> 245


</ItemTemplate> LinkButtonInsert ‫الضغط على ال ر‬ <FooterTemplate> <asp:LinkButton ‫يؤدي فقط الى تفعيل أدوات التأكد من صحة‬ ID="LinkButtonInsert" .InsertGroup ‫االدخال التابعة للمجموعة‬ ValidationGroup="InsertGroup" OnClick="HandleInsertEvent" :‫الحظ أن ذلك تم من خالل األمر‬ Text="Insert" runat="server"> ValidationGroup="InsertGroup" Insert </asp:LinkButton> </FooterTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Word" SortExpression="Word"> <EditItemTemplate> <asp:TextBox ID="TextBoxEditWord" runat="server" Text='<%# Bind("Word") %>'> </asp:TextBox> <asp:RequiredFieldValidator ID="rfvEditWord" runat="server" ErrorMessage="Word is required" Text="*" ForeColor="Red" Display="Dynamic" ControlToValidate="TextBoxEditWord" > </asp:RequiredFieldValidator> </EditItemTemplate> <ItemTemplate> <asp:Label ID="LabelWord" runat="server" Text='<%# Bind("Word") %>'> </asp:Label> </ItemTemplate> <FooterTemplate> <asp:TextBox ID="TextBoxInsertWord" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="rfvInsertWord" runat="server" ErrorMessage="Word is required" Text="*" ForeColor="Red" Display="Dynamic" ValidationGroup="InsertGroup" ControlToValidate="TextBoxInsertWord" > </asp:RequiredFieldValidator>

246

rfvInsertWord ‫الحظ كيفية ربط األداة‬ ‫ من خالل‬InsertGroup ‫بالمجموعة األمر‬ :‫األمر‬ ValidationGroup="InsertGroup"


</FooterTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Meaning" SortExpression="Meaning"> <EditItemTemplate> <asp:TextBox ID="TextBoxEditMeaning" runat="server" Text='<%# Bind("Meaning") %>'> </asp:TextBox> <asp:RequiredFieldValidator ID="rfvEditMeaning" runat="server" ErrorMessage="Meaning is required" Text="*" ForeColor="Red" Display="Dynamic" ControlToValidate="TextBoxEditMeaning" > </asp:RequiredFieldValidator> </EditItemTemplate> <ItemTemplate> <asp:Label ID="LabelMeaning" runat="server" Text='<%# Bind("Meaning") %>'> </asp:Label> </ItemTemplate> <FooterTemplate> <asp:TextBox ID="TextBoxInsertMeaning" runat="server"> </asp:TextBox> <asp:RequiredFieldValidator ID="rfvInsertMeaning" runat="server" ErrorMessage="Meaning is required" Text="*" ForeColor="Red" Display="Dynamic" ValidationGroup="InsertGroup" ControlToValidate="TextBoxInsertMeaning" > </asp:RequiredFieldValidator> </FooterTemplate> </asp:TemplateField>

247

rfvInsertMeaning ‫الحظ كيفية ربط األداة‬ ‫ من خالل‬InsertGroup ‫بالمجموعة األمر‬ :‫األمر‬ ValidationGroup="InsertGroup"


<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" /> </Columns> </asp:GridView> <asp:ValidationSummary ID="ValidationSummary1" ValidationGroup="InsertGroup" runat="server" /> <asp:ValidationSummary ID="ValidationSummary2" runat="server" /> </div> </form> </body>

‫ هي‬ValidationSummary1 ‫أداة التلخيص‬ ‫المسؤولة عن اظهار نصوص األخطاء التابعة‬ :InsertGroup ‫للمجموعة‬ ValidationGroup="InsertGroup" ‫ فهي‬ValidationSummary2 ‫أما األداة‬ ‫المسؤولة عن اظهار نصوص األخطاء التابعة‬ .‫لباقي أدوات التحقق من صحة االدخال‬

248


‫األداة ‪Repeater‬‬ ‫تعتبر األداة ‪ Repeater‬من األدوات القادرة على عرض المعلومات على شكل جدول‪ .‬تدعم هذه األداة أيضا ربط‬ ‫األدوات بمصادر المعلومات (أي ‪ .)DataBinding‬لكنها ال تدعم آلية لتحرير وإضافة المعلومات كما هو األمر‬ ‫عند األداة ‪.GridView‬‬ ‫تعمل هذه األداة أيضا مع القوالب فهي تدعم القوالب التالية لتمكين المستخدم من تنسيق الجدول وفق ما يريد‪:‬‬ ‫‪HeaderTemplate .1‬‬ ‫يحتوي هذا القالب على األدوات التي يجب أن تظهر في رأس الجدول‪ .‬وظيفة هذه األدوات عرض أسماء‬ ‫أعمدة الجدول بالتنسيق الذي نريد‪.‬‬ ‫‪ItemTemplate .2‬‬ ‫يحتوي هذا القالب على األدوات التي يجب أن تظهر في كل سطر من أسطر المعلومات‪ .‬يتم تكرار إنشاء‬ ‫هذا القالب وما يحتويه بعدد السجالت الموجودة في جدول النتيجة‪ .‬من هنا أتت تسمية هذه األداة‬ ‫‪FooterTemplate .3‬‬ ‫يحتوي هذا القالب على األدوات التي يجب أن تظهر في ذيل الجدول‪.‬‬ ‫‪SeparatorTemplate .4‬‬ ‫بإمكاننا استعمال هذا القالب لنحدد النسق الفاصل بين األسطر‪.‬لذلك فهو يأتي بعد كل قالب‬ ‫‪.ItemTemplate‬‬ ‫‪AlternatingItemTemplate .5‬‬ ‫يمكننا هذا القالب من تنسيق األسطر الزوجية بالنسق الذي نريده‪.‬‬ ‫ال بد من مالحظة أن هذه األداة سوف ال تعرض المعلومات إذا لم نستعمل القوالب وخاصة القالب‬ ‫‪ ItemTemplate‬وذلك بخالف األداة ‪ GridView‬التي لها مبنى افتراضي تستعمله إذا لم يقم المستخدم بأي‬ ‫تنسيق‬ ‫عملية الـ ‪ DataBinding‬لهذه األداة تحصل بمساعدة العملية التالية‪:‬‬ ‫)‪DataBinder.Eval(object Container, string FieldName‬‬

‫‪249‬‬


‫البارامتر األول ‪ Container‬يجب أن يكون دائما ‪ Container.DataItem‬ويعني أننا نصل عنصر معلومات‬ ‫أي ‪.Data Item‬‬ ‫أما البارمتر الثاني ‪ FieldName‬فيجب أن يكون اسم الحقل كما يظهر في جدول النتيجة (أي الجدول المُرجع من‬ ‫قبل االستعالم أي من قبل األمر ‪.SELECT‬‬ ‫‪.‬‬ ‫مثال رقم ‪:1‬‬ ‫معطى الجدول التالي‪:‬‬

‫وفيه المعلومات التالية‪:‬‬

‫نريد تصميم صفحة انترنت لعرض محتوى الجدول كامال بواسطة األداة ‪ Repeater‬وفق النسق التالي‪:‬‬

‫‪250‬‬


251


:‫الكود المُقترح لهذا التنسيق هو التالي‬ <asp:Repeater ID="RepeaterMessages" runat="server"> <HeaderTemplate> <table cellpadding="5" cellspacing="2" border="0"> <tr bgcolor="gray"> <td><b>ID</b></td> <td><b>Title</b></td> <td><b>Body</b></td> <td><b>Date</b></td> </tr> </HeaderTemplate>

‫ استعملنا‬HeaderTemplate ‫في قالب الـ‬ ‫ الحظ أننا هنا نبدأ الجدول‬.‫جدوال بسيطا‬ ‫ونضيف سطر رأس مع أسما األعمدة في‬ ‫ إغالق الجدول سيتم في القالب‬.‫الخاليا‬ Footer Template

<ItemTemplate> <tr> <td><%# DataBinder.Eval(Container.DataItem,"ID") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Title") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Body") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Date") %> </td> </tr> </ItemTemplate>

<AlternatingItemTemplate> <tr bgcolor="#ccccff"> <td><%# DataBinder.Eval(Container.DataItem,"ID") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Title") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Body") %> </td> <td><%# DataBinder.Eval(Container.DataItem,"Date") %> </td> </tr> </AlternatingItemTemplate>

252

‫في قالب الـ‬ ItemTemplate tr ‫ننشئ سطرا أي‬ ‫ خاليا أي لكل‬4 ‫وفيه‬ ‫عامود خلية ونقوم‬ ‫بوصله مع الحقل في‬ ‫جدول النتيجة‬ ‫بمساعدة العملية‬ ‫ آنفة الذكر‬Eval

‫في هذا القالب نفعل‬ ‫تماما ما فعلنا عند‬ ‫القالب السابق‬ ‫باستثناء لون الخلفية‬ ‫لهذا السطر أي الـ‬ bgcolor


<SeparatorTemplate> <tr> <td colspan="4"><hr /></td> </tr> </SeparatorTemplate>

<FooterTemplate> </table> </FooterTemplate>

‫في هذا القالب نضيف‬ hr ‫سطرا أفقيا أي‬ ‫كفاصل بين األسطر‬

‫في هذا القالب نغلق‬ ‫الجدول‬

</asp:Repeater> :GridView ‫ تتم تماما كما فعلنا ذلك من قبل مع األداة‬Code Behind ‫تعبئة األداة في الـ‬

protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) FillRepeater(); } private void FillRepeater() { try { DataTable dt = ClassMessages.GetAll(); RepeaterMessages.DataSource = dt; RepeaterMessages.DataBind(); } catch (Exception ex) { LabelMessage.Text = ex.Message; } }

253


‫تمرين رقم ‪:1‬‬ ‫معطى الجدول التالي‪:‬‬

‫المطلوب تصميم صفحة انترنت لعرض محتوى الجدول بواسطة األداة ‪ Repeater‬وفق النسق التالي‪:‬‬

‫الحظ أن عامود االرتباط يجب أن يحتوي على ارتباط تشعبي أي وسم ‪ .anchor‬نص االرتباط هو قيمة الحقل‬ ‫‪ Name‬والعنوان هو الحقل ‪.Link‬‬

‫‪254‬‬


:2 ‫مثال رقم‬ ‫ الذي يحدث‬ItemCommand ‫في هذا المثال نريد التعرف على الحدث األساسي لدى هذه األداة أال وهو الحدث‬ .‫ أو ما شابه ذلك‬LinkButton ‫كلما طلب المستخدم تنفيذ أمر معين مثال من خالل النقر على‬ ‫ ومعالج الحدث‬LinkButton ‫) باستعمال األداة‬1 ‫سنقوم في هذا المثال بحل السؤال السابق (رقم‬ :ItemCommand :‫كود التنسيق‬

‫الحظ معالج الحدث‬ <asp:Repeater ID="RepeaterLinks" runat="server" onitemcommand="RepeaterLinks_ItemCommand"> <HeaderTemplate> <table cellpadding="5" cellspacing="2" border="0"> <tr bgcolor="gray"> <td><b>ID</b></td> <td><b>Link</b></td> </tr> </HeaderTemplate>

ItemCommand

‫هنا استعملنا الخصائص‬ ‫ و‬CommandName CommandArgument .‫وأعطيناهم القيم الالزمة‬

<ItemTemplate> <tr> <td><%# DataBinder.Eval(Container.DataItem,"ID") %></td> <td> <asp:LinkButton ID="LinkButton1" runat="server" CommandName="View" CommandArgument='<%# DataBinder.Eval(Container.DataItem,"Name") %>' > <%# DataBinder.Eval(Container.DataItem,"Name") %> </asp:LinkButton> </td> </tr> </ItemTemplate> <AlternatingItemTemplate> <tr bgcolor="#ccccff"> <td><%# DataBinder.Eval(Container.DataItem,"ID") %></td> <td> <asp:LinkButton ID="LinkButton1" runat="server" CommandName="View" CommandArgument='<%# DataBinder.Eval(Container.DataItem,"Name") %>' > <%# DataBinder.Eval(Container.DataItem,"Name") %>

255


</asp:LinkButton> </td> </tr> </tr> </AlternatingItemTemplate> <SeparatorTemplate> <tr> <td colspan="2"><hr /></td> </tr> </SeparatorTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater> RepeaterLinks_ItemCommand ‫برمجة معالج الحدث‬ protected void RepeaterLinks_ItemCommand(object source, RepeaterCommandEventArgs e) { if (e.CommandName.Equals("View")) { string strLink; strLink = e.CommandArgument.ToString(); Response.Redirect(strLink); } } :2 ‫تمرين رقم‬ .‫عليكم برمجة الحل المقترح وفحصه بشكل كامل‬

256


‫األداة ‪DataList‬‬ ‫لقد تعرفنا حتى اآلن على األداتين ‪ Repeater‬و ‪ GridView‬وهما أداتان لعرض وتحرير البيانات من خالل‬ ‫تحديد األعمدة أي الـ ‪ .Columns‬في هذا الفصل سنتعرف على األداة ‪ DataList‬التي تمكننا من عرض البيانات‬ ‫سطرا سطرا وبدون أعمدة‪ .‬في كل سطر يوجد عدد معين من الخاليا‪ .‬عدد هذه الخاليا التي في السطر الواحد يُحدد‬ ‫من خالل الخاصية ‪ .RepeateColumns‬جميع حقول السجل الواحد تظهر في خلية واحدة فقط‪ .‬تدعم هذه األداة‬ ‫اضافة أدوات أخرى داخل خالياها بحسب الحاجة الى ذلك‪ .‬إضافة األدوات تتم بنفس الطريقة التي تعلمناها سابقا‬ ‫عند الـ ‪ GridView‬وهي الطريقة التي تعتمد على القوالب‪ .‬هنالك عدة أنواع من القوالب‪ .‬سنعرض هنا بعضها‪:‬‬ ‫•‬

‫‪ :HeaderTemplate‬نصرح بداخله عن األدوات التي ستظهر في رأس الـ ‪.DataList‬‬

‫•‬

‫‪ :ItemTemplate‬نصرح بداخله عن األدوات التي ستظهر في الخلية عندما تظهر الـ ‪ DataList‬في‬ ‫وضع العرض (أي الـ ‪.)View Mode‬‬

‫•‬

‫‪ :EditItemTemplate‬نصرح بداخله عن األدوات التي ستظهر في الخلية عندما تظهر الـ ‪DataList‬‬ ‫في وضع التحرير (أي الـ ‪.)Edit Mode‬‬

‫•‬

‫‪ :FooterTemplate‬نصرح بداخله عن األدوات التي ستظهر في الخلية عندما تظهر الـ ‪DataList‬‬ ‫في وضع اإلضافة (أي الـ ‪ .)Insert Mode‬هذه األدوات تظهر فقط إذا كانت الخاصية‬ ‫‪.true ShowFooter‬‬

‫•‬

‫‪ :SeparatorTemplate‬بإمكاننا استعمال هذا القالب لنحدد النسق الفاصل بين األسطر‪.‬‬

‫•‬

‫‪ :AlternatingItemTemplate‬يمكننا هذا القالب من تنسيق األسطر الزوجية بالنسق الذي نريده‪.‬‬

‫مثال – إدارة المستخدمين‬ ‫اآلن نريد عرض محتوى الجدول ‪ Users‬بواسطة ‪ DataList‬والتعرف من خالل هذا المثال على أهم خصائص‬ ‫هذه األداة‪ .‬سنوفر في هذا المثال زرين لترتيب البيانات حسب ‪ FName‬أو ‪.LName‬‬ ‫محتوى الجدول كما يظهر في قاعدة البيانات‪:‬‬

‫‪Email‬‬

‫‪UserName IsAdmin‬‬

‫‪MyPassword‬‬

‫☒‬

‫‪1‬‬

‫‪1‬‬

‫‪Abu Wael Wael@B.com‬‬

‫☐‬

‫‪2‬‬

‫‪2‬‬

‫‪Ali@com.com‬‬

‫‪Abu Ali‬‬

‫‪Ali‬‬

‫☐‬

‫‪2‬‬

‫‪2‬‬

‫‪B@Othman.net‬‬

‫‪Othman‬‬

‫‪Baher‬‬

‫‪257‬‬

‫‪Lname‬‬

‫‪UserID Fname‬‬ ‫‪Wael‬‬

‫‪6‬‬ ‫‪12‬‬ ‫‪13‬‬


:‫ كالتالي‬DataList ‫ فيها األداة‬UsersWithDataList.aspx ‫نصمم صفحة جديدة باسم‬ <h1>Users With DataList Control</h1> <asp:Label ID=”LabelMsg” runat=”server” Text=""></asp:Label> <asp:DataList id=”DataListUsers” CellSpacing=”1” CellPadding=”3” GridLines=”None” BorderWidth=”1px” BorderColor=”#4A3C8C” runat=”server” onitemcommand=”UsersList_ItemCommand”> <HeaderStyle Font-Bold=”True” ForeColor=”#E7E7FF” BackColor=”#4A3C8C”/> <ItemStyle ForeColor=”navy” BackColor=”#F0F0FF”/> <AlternatingItemStyle ForeColor=”navy” BackColor=”#C0C0FF”/> <HeaderTemplate> <asp:Label ID=”LabelUsers” Text=”Users” runat=”server”/> &nbsp;&nbsp; <asp:Button ID=”Button1” Text=”Sort By Fname” CommandArgument ‫الخاصية‬ Width=”150px” CommandName=”Sort” ‫تحمل اسم الحقل الذي نريد أن نرتب البيانات‬ CommandArgument=”Fname” runat=”server”/> <asp:Button ID=”Button2” Text=”Sort By Lname” ‫بحسبه عندما يضغط المستخدم على ال ر‬ Width=”150px” CommandName=”Sort” CommandArgument=”Lname” runat=”server”/> </HeaderTemplate> <ItemTemplate> <asp:Label ID=”LabelUserID” Text=’<%# “UserID: “+DataBinder.Eval(Container.DataItem,”UserID”)%>’ runat=”server”/> <br> <asp:Label ID=”LabelFName” Text=’<%# “Fname: “+DataBinder.Eval(Container.DataItem,”Fname”)%>’ runat=”server”/> <br> <asp:Label ID=”LabelLName” Text=’<%# “Lname: “+DataBinder.Eval(Container.DataItem,”Lname”)%>’ runat=”server”/> <asp:Label ID=”LabelEMail” Text=’<%# “Email: “+DataBinder.Eval(Container.DataItem, “Email”)%>’ runat=”server”/>

258


<br> <asp:Label ID=”LabelMyPassword” Text=’<%# “MyPassword: “+DataBinder.Eval(Container.DataItem, “MyPassword”)%>’ runat=”server”/> <br> <asp:Label ID=”LabelUserName” Text=’<%# “UserName: “+DataBinder.Eval(Container.DataItem, “UserName”)%>’ runat=”server”/> <br> <asp:Label ID=”LabelIsAdmin” Text=”IsAdmin: “ runat=”server”/> <asp:CheckBox ID=”CheckBoxIsAdmin” Enabled=”false” Checked=’<%# DataBinder.Eval(Container.DataItem, “IsAdmin”)%>’ runat=”server” /> </ItemTemplate> </asp:DataList> :‫أما البرمجة فهي كالتالي‬ ‫ تتلقى كبارامتر إسم الحقل الذي نريد أن نرتب السجالت بحسبه عندما يضغط‬FillDataList ‫الحظ أن العملية‬ DataList ‫المستخدم على األزرار في رأس الـ‬

protected void Page_Load(object sender, EventArgs e) { try { if (Session[“ValidUser”] == null || !(bool)Session[“ValidUser”]) { Response.Redirect(“Login.aspx”); } if (!IsPostBack) { FillDataList(“Fname”); } LabelMsg.Text = ""; }

259


catch (Exception ex) { LabelMsg.Text = ex.Message; } } private void FillDataList(string strFieldName) { try { DataTable dt = ClassUsers.GetAll(strFieldName); // ORDER BY FieldName DataListUsers.DataSource = dt; DataListUsers.DataBind(); } catch (Exception ex) { LabelMessage.Text = ex.Message; } }

protected void UsersList_ItemCommand(object source, DataListCommandEventArgs e) { ‫هذ الخاصية تحمل اسم الحقل الذي‬ try { ‫نريد أن نرتب البيانات بحسبه‬ if (e.CommandName.Equals(“Sort”)) { FillDataList(e.CommandArgument.ToString()); } } catch (Exception ex) { LabelMsg.Text = ex.Message; } } :‫الصورة التالية تظهر نتيجة تشغيل الصفحة‬

260


:‫ لتحديد عدد الخاليا في السطر الواحد‬RepeatColumns ‫واآلن سنستعمل الخاصية‬ <asp:DataList id="DataListUsers" CellSpacing="1" CellPadding="3" GridLines="None" BorderWidth="1px" BorderColor="#4A3C8C" RepeatColumns="2" … > …

261


‫الصورة التالية تظهر نتيجة تشغيل الصفحة بعد إعطاء الخاصية ‪ Repeat‬القيمة ‪ 2‬ليكون في كل سطر خليتان‪:‬‬

‫واآلن نريد إضافة إمكانية لتحرير البيانات بحيث تظهر الصفحة كالتالي‪:‬‬

‫‪262‬‬


‫هذا هو الـ ‪Footer‬‬

‫‪263‬‬


‫ لتحديد األدوات التي ستظهر في الخاليا عندما يضغط المستخدم‬EditItemTemplate ‫طبعا سنستعين بالقالب‬ ‫ وحينها ستظهر الخلية التي نريد‬GridView ‫ بالضبط بنفس الطريقة التي استعملناها عن الـ‬Edit ‫على األمر‬ :‫تحريرها كالتالي‬

<asp:DataList id="DataListUsers" CellSpacing="1" CellPadding="3" GridLines="None" BorderWidth="1px" BorderColor="#4A3C8C" RepeatColumns="2" ShowFooter="true" runat="server" DataKeyField="UserID" OnEditCommand="HandleEditCommand" ‫مجموعة معالجات األحداث‬ OnCancelCommand="HandleCancelCommand" OnUpdateCommand="HandleUpdateCommand" ‫للتحرير‬ OnDeleteCommand="HandleDeleteCommand" onitemcommand="UsersList_ItemCommand" > <HeaderStyle Font-Bold="True" ForeColor="#E7E7FF" BackColor="#4A3C8C"/> <ItemStyle ForeColor="navy" BackColor="#F0F0FF"/> <AlternatingItemStyle ForeColor="navy" BackColor="#C0C0FF"/> <HeaderTemplate> <asp:Label ID="LabelUsers" Text="Users" runat="server"/> &nbsp;&nbsp; <asp:Button ID="Button1" Text="Sort By FName" Width="150px" CommandName="Sort" CommandArgument="FName" runat="server"/> <asp:Button ID="Button2" Text="Sort By LName" Width="150px" CommandName="Sort" CommandArgument="LName" runat="server"/>

264


</HeaderTemplate> <ItemTemplate> <asp:Label ID="LabelUserID" Text='<%# "UserID: " + DataBinder.Eval(Container.DataItem, "UserID")%>' runat="server"/> <br> <asp:Label ID="LabelFName" Text='<%# "FName: " + DataBinder.Eval(Container.DataItem, "FName")%>' runat="server"/> <br> <asp:Label ID="LabelLName" Text='<%# "LName: " + DataBinder.Eval(Container.DataItem, "LName")%>' runat="server"/> <br> <asp:Label ID="LabelEMail" Text='<%# "Email: " + DataBinder.Eval(Container.DataItem, "Email")%>' runat="server"/> <br> <asp:Label ID="LabelMyPassword" Text='<%# "MyPassword: " + DataBinder.Eval(Container.DataItem, "MyPassword")%>' runat="server"/> <br> <asp:Label ID="LabelUserName" Text='<%# "UserName: " + DataBinder.Eval(Container.DataItem, "UserName")%>' runat="server"/> <br> <asp:Label ID="LabelIsAdmin" Text="IsAdmin: " runat="server"/> <asp:CheckBox ID="CheckBoxIsAdmin" Enabled="false" Checked='<%# DataBinder.Eval(Container.DataItem, "IsAdmin")%>' runat="server" />

265


<br /> <asp:LinkButton ID="LinkButton1" CommandName="Edit" Text="Edit" Width="100px" runat="server"/> <asp:LinkButton ID="LinkButton2" CommandName="Delete" OnClientClick="return confirm('Are You Sure?');" Text="Delete" Width="100px" runat="server"/> </ItemTemplate> <EditItemTemplate> FName:&nbsp; <asp:Textbox ID="TextboxFName" Text='<%# DataBinder.Eval(Container.DataItem, "FName")%>' runat="server"/> <br> LName:&nbsp; <asp:Textbox ID="TextboxLName" Text='<%# DataBinder.Eval(Container.DataItem, "LName")%>' runat="server"/> <br /> Email:&nbsp; <asp:Textbox ID="TextboxEMail" Text='<%# DataBinder.Eval(Container.DataItem, "Email")%>' runat="server"/> <br> MyPassword:&nbsp; <asp:Textbox ID="TextboxMyPassword" Text='<%# DataBinder.Eval(Container.DataItem, "MyPassword")%>' runat="server"/> <br> UserName:&nbsp; <asp:Textbox ID="TextboxUserName" Text='<%# DataBinder.Eval(Container.DataItem, "UserName")%>' runat="server"/> <br> IsAdmin:&nbsp; <asp:CheckBox ID="CheckBoxIsAdmin" Enabled="true" Checked='<%# DataBinder.Eval(Container.DataItem, "IsAdmin")%>'

266


runat="server" /> <br /> <asp:LinkButton ID="LinkButton3" CommandName="Update" Text="Update" Width="100px" runat="server"/> <asp:LinkButton ID="LinkButton4" CommandName="Cancel" Text="Cancel" Width="100px" runat="server"/> </EditItemTemplate> <FooterTemplate> <table> <tr> <td colspan="2" style="font-weight: bold"> Insert a new User </td> </tr> <tr> <td style="font-weight: bold"> FName </td> <td> <asp:Textbox ID="TextboxNewFName" runat="server"/> </td> </tr> <tr> <td style="font-weight: bold"> LName </td> <td> <asp:Textbox ID="TextboxNewLName" runat="server"/> </td> </tr> <tr> <td style="font-weight: bold"> EMail </td> <td> <asp:Textbox ID="TextboxNewEMail" runat="server"/> </td> </tr> <tr> <td style="font-weight: bold"> MyPassword </td>

267


<td> <asp:Textbox ID="TextboxNewMyPassword" runat="server"/> </td> </tr> <tr> <td style="font-weight: bold"> UserName </td> <td> <asp:Textbox ID="TextboxNewUserName" runat="server"/> </td>> </tr> <tr> <td style="font-weight: bold"> IsAdmin? </td> <td> <asp:CheckBox ID="CheckBoxNewIsAdmin" Enabled="true" runat="server" /> </td> </tr> <tr> <td colspan="2"> <asp:LinkButton ID="LinkButton3" CommandName="Insert" Text="Insert" Width="100px" runat="server"/> </td> </tr> </table> </FooterTemplate> </asp:DataList>

268


:‫أما البرمجة فهي كالتالي‬ protected void UsersList_ItemCommand(object source, DataListCommandEventArgs e) { try { if (e.CommandName.Equals("Sort")) { FillDataList(e.CommandArgument.ToString()); } else if (e.CommandName.Equals("Insert")) { AddNewUser(); } } catch (Exception ex) { LabelMsg.Text = ex.Message; } } /* EditCommand-Event-Handler*/ protected void HandleEditCommand(object sender, DataListCommandEventArgs e) { try { /* Set the index of the row to be edited */ DataListUsers.EditItemIndex = e.Item.ItemIndex;

/* Fill DataList to switch the mode to edit mode*/ FillDataList("FName"); } catch (Exception ex) { LabelMsg.Text = ex.Message; } }

269


/* CancelCommand-Event-Handler*/ protected void HandleCancelCommand(object sender, DataListCommandEventArgs e) { try { /* Turn off the Edit Mode */ DataListUsers.EditItemIndex = -1; /* Bind DataList to switch the mode back to view mode*/ FillDataList("FName"); } catch (Exception ex) { LabelMsg.Text = ex.Message; } } /* UpdateCommand-Event-Handler*/ protected void HandleUpdateCommand(object sender, DataListCommandEventArgs e) { try { /* Get the UserID of the row to be edited */ int UserID; int Index = e.Item.ItemIndex; UserID = Convert.ToInt32(DataListUsers.DataKeys[Index]); /* Get the data from the edit controls */ TextBox TextboxFName = (TextBox)(e.Item.FindControl("TextboxFName")); TextBox TextboxLName = (TextBox)(e.Item.FindControl("TextboxLName")); TextBox TextboxEMail = (TextBox)(e.Item.FindControl("TextboxEMail")); TextBox TextboxMyPassword = (TextBox)(e.Item.FindControl("TextboxMyPassword")); TextBox TextboxUserName = (TextBox)(e.Item.FindControl("TextboxUserName")); CheckBox CheckBoxIsAdmin = (CheckBox)(e.Item.FindControl("CheckBoxIsAdmin")); ClassUsers obj = new ClassUsers(); obj.FName = TextboxFName.Text; obj.LName = TextboxLName.Text; obj.UserName = TextboxUserName.Text;

270


obj.EMail = TextboxEMail.Text; obj.MyPassword = TextboxMyPassword.Text; obj.CheckBoxIsAdmin = CheckBoxIsAdmin.Checked; obj.UserID = UserID.ToString(); obj.Update(); /* Turn off the Edit Mode */ DataListUsers.EditItemIndex = -1; /* Bind DataList to switch the mode back to view mode*/ FillDataList("FName"); } catch (Exception ex) { LabelMsg.Text = ex.Message; } } /* DeleteCommand-Event-Handler*/ protected void HandleDeleteCommand(object sender, DataListCommandEventArgs e) { try { /* Get the UserID of the row to be edited */ int Index = e.Item.ItemIndex; int UserID = Convert.ToInt32(DataListUsers.DataKeys[Index]); /* Delete the user from the Database */ ClassUsers obj = new ClassUsers(); obj.UserID = UserID.ToString(); obj.Delete(); /* Bind DataList to show that the user is deleted*/ FillDataList("FName"); } catch (Exception ex) { LabelMsg.Text = ex.Message; } }

271


/* AddNewButton Click-Event-Handler*/ protected void AddNewUser() { try { // The footer is a control in DataList and it is the last // control int FooterIndex = DataListUsers.Controls.Count - 1; Control Footer = DataListUsers.Controls[FooterIndex]; /* Get the data from the edit controls of the footer */ TextBox TextboxFName = (TextBox)(Footer.FindControl("TextboxNewFName")); TextBox TextboxLName = (TextBox)(Footer.FindControl("TextboxNewLName")); TextBox TextboxEMail = (TextBox)(Footer.FindControl("TextboxNewEMail")); TextBox TextboxMyPassword = (TextBox)(Footer.FindControl("TextboxNewMyPassword")); TextBox TextboxUserName = (TextBox)(Footer.FindControl("TextboxNewUserName")); CheckBox CheckBoxIsAdmin = (CheckBox)(Footer.FindControl("CheckBoxNewIsAdmin")); ClassUsers obj = new ClassUsers(); obj.FName = TextboxFName.Text; obj.LName = TextboxLName.Text; obj.UserName = TextboxUserName.Text; obj.EMail = TextboxEMail.Text; obj.MyPassword = TextboxMyPassword.Text; obj.CheckBoxIsAdmin = CheckBoxIsAdmin.Checked; obj.Insert(); FillDataList("FName"); } catch (Exception ex) { LabelMsg.Text = ex.Message; } }

272


‫ عرض الصور‬:‫مثال‬ ‫ هي أنها تمكننا‬GridView ‫ األفضلية لهذه األداة مقابل الـ‬.‫ لعرض الصور‬GridView ‫واآلن نريد استعمال الـ‬ .GridView ‫من تحديد عدد الصور في السطر الواحد وهو ما ال يمكننا فعله بواسطة الـ‬

:‫أما البرمجة فهي بسيطة جدا‬ <asp:Label ID="LabelMsg" runat="server" Text="" FontSize="Large"></asp:Label> <asp:Panel ID="pnlImages" runat="server" Height="100%" Width="100%"> <asp:DataList ID="DataListImages" runat="server" DataKeyField="FileName" RepeatColumns="2" RepeatDirection="Horizontal" Width="100%"> <ItemTemplate> <table style="width:90%"> <tr valign="top"> <td> <a href='<%# DataBinder.Eval(Container.DataItem,"FileName") %>'> <img title='<%# Eval("Description") %>' src='<%# DataBinder.Eval (Container.DataItem, "FileName") %>' height="100" width="100" style="border:solid 5px #dcdcdc"></a> </td> <td align="left"> 273


<asp:Label ID="DescriptionLabel" runat="server" Width="150px" Text='<%# Eval("Description") %>'/></td> </tr> </table> </ItemTemplate> </asp:DataList> </asp:Panel> protected void Page_Load(object sender, EventArgs e) { try { if (!IsPostBack) { ShowImages(); } } catch (Exception ex) { } } private void ShowImages() { try { DataTable dt = ClassFiles.GetAll("Image"); if (dt.Rows.Count == 0) { LabelMsg.Text = "No items available"; return; } DataListImages.DataSource = dt; DataListImages.DataBind(); } catch (Exception ex) { LabelMsg.Text = ex.Message; } }

274


‫الملفات‬ ‫‪Files‬‬ ‫تعتبر الملفات الهياكل األساسية لتخزين المعلومات‪ .‬يوجد أنواع عدة من الملفات تبعا لألنواع المختلفة من التطبيقات‪.‬‬ ‫فهنالك على سبيل المثال ملفات األوفيس مثل ملفات الورد واإلكسيل والبوربوينت وما شابه ذلك‪ .‬وملفات الـ ‪PDF‬‬ ‫وغيرها‪ .‬وال يمكن تصور العمل مع الحواسيب بدون استعمال هذه الهياكل لحفظ المعلومات‪ .‬والسؤال هنا هو كيف‬ ‫يمكننا التعامل مع الملفات من خالل المواقع؟ والمقصود بالتعامل‪:‬‬ ‫•‬

‫رفع الملفات )‪ (File Upload‬إلى الخادم وحفظها عليه‬

‫•‬

‫حذف الملفات من الخادم‬

‫•‬

‫عرض الملفات الموجودة على الخادم‬

‫•‬

‫تحميل الملفات )‪ (File Download‬من الخادم‬

‫في هذا الفصل سوف نناقش كل هذه النقاط وإعطاء أمثلة عملية لها‪ .‬حفظ الملفات على الخادم سيتم ضمن منظومة‬ ‫الملفات أي ضمن ما يسمى بالـ ‪.File System‬‬ ‫لتوضيح ذلك نأخذ المثال التالي‪:‬‬ ‫موقع لمدرسة يُم ّكن كل معلم من إنشاء مواقع خاصة بدروسه‪ .‬لكل درس من هذه الدروس توجد أنواع عدة من‬ ‫الملفات‪ ،‬مثل ملفات امتحانات وملفات مواد تدريس وملفات وظائف وما إلى ذلك‪ .‬بالتأكيد نستطيع أن نحفظ كل هذه‬ ‫الملفات ضمن منظومة الملفات لكن وبال أدنى شك فان حفظها داخل قاعدة البيانات أسهل وبالتالي األفضل لرعاية‬ ‫وصيانة برمجيات الموقع‪ .‬سنرى في األمثلة التالية أننا عند حفظ الملفات ضمن منظومة الملفات سنستعين بقاعدة‬ ‫البيانات لحفظ بعض التفاصيل عن الملفات وعن مكان وجودها ضمن منظومة الملفات‪.‬‬

‫‪275‬‬


‫الموقع بصورته النهائية‬

‫حفظ الملفات ضمن منظومة الملفات )الصفحة ‪)FilesEditor.aspx‬‬ ‫نريد برمجة موقع لمساق أو لموضوع معين ندير من خالله عدة أنواع من الملفات‪ :‬مثل ملفات امتحانات وملفات‬ ‫وظائف وملفات متنوعة وملفات صور‪ .‬توجد في الموقع لكل نوع من هذه األنواع إمكانية لعرض الملفات وتحميلها‬ ‫ولرفعها وحذفها‪.‬‬ ‫من أجل رفع الملفات إلى الخادم وحفظها عليه ال بد أوال من اختيارها حيث تكون مخزنة في منظومة الملفات التابعة‬ ‫لحاسوب الزبون‪.‬‬

‫‪276‬‬


‫للقيام بذلك تستعين باألداة ‪FileUpload‬‬ ‫جزء من واجهة األداة‪:‬‬ ‫العملية ‪ -‬الخاصية‬

‫الشرح‬ ‫خاصية بوليانية تكون قيمتها ‪ true‬إذا اختار المستخدم ملفا‬ ‫)حجمه ليس ‪ .(0‬خالف ذلك تكون قيمتها ‪false‬‬ ‫اسم الملف الذي اختاره المستخدم‬ ‫الملف الذي اختاره المستخدم وهو عبارة عن كائن من نوع‬ ‫‪ HttpPostedFile‬لديه مجموعة من الخصائص والعمليات‬ ‫التي ستشرح الحقا عند استعمالها‬ ‫هذه العملية تأخذ كبارامتر المسار الكامل )‪(Full Path‬‬ ‫للمكان الذي نريد حفظ الملف فيه‪ .‬على هذا المسار أن يشمل‬ ‫اسم الملف مع الملحق‪ .‬لنجاح هذه العملية يجب أن يكون لدى‬ ‫الموقع الحق في الوصول إلى المجلد الذي يريد حفظ الملف‬ ‫فيه‪ .‬ماذا لو كان الملف موجودا في نفس المجلد؟‬

‫‪bool HasFile‬‬ ‫‪string FileName‬‬ ‫‪HttpPostedFile PostedFile‬‬

‫)‪void SaveAs(string filename‬‬

‫شرح بعض العمليات المساعدة‪:‬‬ ‫العملية‬

‫الشرح‬ ‫عملية ستاتية بوليانية تأخذ كبارامتر المسار الكامل ‪(Full‬‬ ‫)‪ Path‬لملف ما (يشمل اسم الملف مع الملحق) وتعيد ‪ true‬إذا‬ ‫وجدت أن الملف حقا موجود في المكان الممرر كبارامتر‪ .‬خالف‬ ‫ذلك تعيد العملية ‪ .false‬العملية موجودة ضمن الفئة‪:‬‬ ‫‪System.IO.File‬‬ ‫عملية ستاتية بوليانية تأخذ كبارامتر المسار الكامل ‪(Full‬‬ ‫)‪ Path‬لمجلد )‪ (Directory‬ما وتعيد ‪ true‬إذا وجدت أن‬ ‫المجلد حقا موجود في المكان الممرر كبارامتر‪ .‬خالف ذلك تعيد‬ ‫العملية ‪ .false‬العملية موجودة ضمن الفئة‪:‬‬ ‫‪System.IO.Directory‬‬ ‫عملية ستاتية تأخذ كبارامتر المسار الكامل )‪ (Full Path‬لملف‬ ‫ما (يشمل اسم الملف مع الملحق) ثم تقوم بحذفه‪ .‬ترمي هذه‬ ‫العملية استثناءات مختلفة بحسب الخطأ الحاصل‪ :‬مثل أن يكون‬ ‫الملف مفتوحا من قبل برنامج آخر أو من قبل مستخدم ما‪ ،‬أو أن‬ ‫ال يتم العثور على الملف أو المجلد أو عدم وجود حق للموقع في‬ ‫الوصول إلى المجلد أو الملف وما إلى ذلك‪ .‬العملية موجودة‬ ‫ضمن الفئة‪:‬‬ ‫‪System.IO.File‬‬

‫‪277‬‬

‫)‪bool Exists(string path‬‬

‫)‪bool Exists(string path‬‬

‫)‪void Delete(string path‬‬


‫الصفحة ‪ FilesEditor‬مسئولة عن رفع الملفات وحفظها‪ .‬الصفحة تعرض الملفات الموجودة بواسطة‬ ‫‪.GridView‬‬ ‫المظهر األولي لصفحة تحرير ملفات االمتحانات‪:‬‬

‫الملفات س ُتحفظ في مجلد خاص باسم ‪ Docs‬موجود ضمن مجلدات الموقع‪.‬‬

‫ذكرت آنفا بأننا سنستعين بقاعدة البيانات لحفظ بعض التفاصيل عن الملفات وعن مكان وجودها ضمن منظومة‬ ‫الملفات‪ .‬هذا سيسهل علينا عملية عرضها بواسطة ‪ GridView‬عن طريق وصل المعلومات )‪.(DataBinding‬‬ ‫مبنى الجدول ‪ Files‬في قاعدة البيانات‪:‬‬

‫‪278‬‬


279


:‫كود التنسيق‬ <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> <asp:Label ID="Label1" runat="server" Text="Please select the files type"></asp:Label> <br /> <asp:DropDownList ID="DropDownListFileType" runat="server" AutoPostBack="True" onselectedindexchanged="DropDownListFileType_SelectedIndexChanged"> <asp:ListItem>Exam</asp:ListItem> <asp:ListItem>Homework</asp:ListItem> <asp:ListItem>Misc</asp:ListItem> </asp:DropDownList> <br /> <br /> <asp:Label ID="Label2" runat="server" Text="Description"></asp:Label> <br /> <asp:TextBox ID="TextBoxDescription" Width="500px" runat="server"></asp:TextBox> <br /> <asp:Label ID="LabelMsg" runat="server" Text=""></asp:Label> <br /> <asp:FileUpload ID="FileUpload1" Width="500px" runat="server" /> &nbsp; <asp:Button ID="ButtonUpload" runat="server" Text="Upload" onclick="ButtonUpload_Click" /> <hr /> <asp:GridView ID="GridViewFiles" runat="server" CellPadding="4" AutoGenerateColumns="false" ForeColor="#333333" GridLines="None" Caption="Available Files" ondatabound="GridViewFiles_DataBound" onrowdatabound="GridViewFiles_RowDataBound" onrowcommand="GridViewFiles_RowCommand" onrowdeleting="GridViewFiles_RowDeleting"> <RowStyle BackColor="#E3EAEB" /> <FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White"/> <PagerStyle BackColor="#666666" ForeColor="White" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#C5BBAF" Font-Bold="True" ForeColor="#333333" /> <HeaderStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White"/> <EditRowStyle BackColor="#7C6F57" /> <AlternatingRowStyle BackColor="White" /> <Columns>

280


<asp:TemplateField HeaderText="ID"> <ItemTemplate> <asp:Label ID="LabelID" runat="server" Text='<%#Bind("ID") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="FileName"> <ItemTemplate> <asp:Label ID="LabelFileName" runat="server" Text='<%#Bind("FileName") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="FileSize"> <ItemTemplate> <asp:Label ID="LabelFileSize" runat="server" Text='<%#Bind("FileSize") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Description"> <ItemTemplate> <asp:Label ID="LabelDescription" runat="server" Text='<%#Bind("Description") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="UploadDate"> <ItemTemplate> <asp:Label ID="LabelUploadDate" runat="server" Text='<%#Bind("UploadDate") %>'></asp:Label> </ItemTemplate> </asp:TemplateField> ‫ التي‬HyperLink ‫هنا نستعمل األداة‬ <asp:TemplateField HeaderText="Download"> <ItemTemplate> <asp:HyperLink ID="HyperLinkFileName" NavigateUrl='<%#Bind("FileName") %>' Text="Download" runat="server"> </asp:HyperLink> </ItemTemplate> </asp:TemplateField>

‫تقوم بعملية عرض نافذة الحوار‬ ‫الخاصة بتحميل الملفات وحفظها‬ (File Download Dialog) ‫بمجرد أن ينقر المستخدم على هذ‬ NavigateUrl ‫ الخاصية‬.‫األداة‬ ‫تأخذ كقيمة اسم الملف مع المجلد‬ ‫المخ ن فيه الملف أي‬ .Docs/FileName

<asp:TemplateField HeaderText="Delete"> <ItemTemplate> <asp:LinkButton ID="LinkButtonDelete" CausesValidation="False" CommandName="Delete" CausesValidation="false" OnClientClick='return confirm("Are you sure?")' runat="server">Delete</asp:LinkButton>

281


</ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </asp:Content>

282


:‫البرمجة‬ protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { FillGrid(); } } private void FillGrid() { string strType; strType = DropDownListFileType.SelectedValue.ToString(); DataTable dt = ClassFiles.GetAllByType(strType); GridViewFiles.DataSource = dt; GridViewFiles.DataBind(); } protected void ButtonUpload_Click(object sender, EventArgs e) { // Request.PhysicalApplicationPath ‫تعيد لنا مكان وجود الموقع على الخادم‬ // C:\Inetpub\wwwroot\FilesHandling\ ‫مثال‬ string strRealPath = Request.PhysicalApplicationPath; // C:\Inetpub\wwwroot\FilesHandling\Docs ‫الملفات مخزنة في المجلد‬ strRealPath += "Docs\\"; // ‫هل اختار المستخدم ملفا؟‬ if (FileUpload1.HasFile) { // ‫هل المجلد الذي نريد حفظ الملفات فيه موجود؟‬ if (!System.IO.Directory.Exists(strRealPath)) { LabelMsg.Text = "Docs directory does not exist"; } // ‫هل حجم الملف يزيد عن الحجم الذي نسمح به؟‬ else if (FileUpload1.PostedFile.ContentLength < 100000000) { // ‫هل يوجد ملف بهذا االسم في مجلد حفظ الملفات؟‬ if (System.IO.File.Exists(strRealPath + FileUpload1.FileName)) { LabelMsg.Text = "A file with this name already exists"; } else { // ‫ ملف امتحان أم وظيفة أم متنوع؟‬:‫استخراج نوع الملف‬ string strType = DropDownListFileType.SelectedValue.ToString(); // ‫نحفظ الملف في المجلد ال ُمعد لذلك‬

283


FileUpload1.SaveAs(strRealPath + FileUpload1.FileName); // ‫نحفظ تفاصيل الملف في قاعدة البيانات‬ ClassFiles obj = new ClassFiles(); obj.FileName = "Docs\\" + FileUpload1.FileName; obj.FileSize = FileUpload1.PostedFile.ContentLength; obj.Description = TextBoxDescription.Text; obj.UploadDate = DateTime.Now.ToString(); obj.Type = strType; obj.Insert(); LabelMsg.Text = "Successfully Uploaded "; } } else { LabelMsg.Text = "The file size must be <= to 100 MB "; } } else { LabelMsg.Text = "Please select a file first"; } FillGrid(); } protected void GridViewFiles_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { string strValue = DataBinder.Eval(e.Row.DataItem, "FileName").ToString(); int nIndex = strValue.IndexOf("\\"); if (nIndex != -1) { strValue = strValue.Substring(nIndex + 1); e.Row.Cells[1].Text = strValue; } } }

284

‫هذا المعالج يُستدعى بعد أن تتم‬ ‫عملية وصل السطر أي نقل‬ ‫المعلومات من كائن الـ‬ ‫ إلى خاليا‬DataTable ‫ نحن نخزن في قاعدة‬.‫السطر‬ ‫البيانات مع اسم الملف اسم‬ ‫المجلد الذي نحفظ فيه الملفات‬ ‫ هنا ال‬.Docs/FileName ‫أي‬ ‫نريد أن يظهر اسم المجلد لذلك‬ ‫نستخرج القيمة من الخلية‬ ‫بواسطة العملية‬ ‫ ونحذف‬DataBinder.Eval ‫ الحظ في‬.‫منها اسم المجلد‬ ‫الجدول أعاله أن األسماء تظهر‬ .‫بدون اسم المجلد‬


private void DeleteFile(string strID, string strFileName) { try { // 1: Delete the file record from the data base ClassFiles obj = new ClassFiles(strID); obj.Delete(); // 2: Delete the physical file from the file system string strRealPath = Request.PhysicalApplicationPath; System.IO.File.Delete(strRealPath + strFileName); } catch(Exception ex) { LabelMsg.Text = ex.Message; } } protected void GridViewFiles_RowDeleting(object sender, GridViewDeleteEventArgs e) { string strID = GridViewFiles.DataKeys[e.RowIndex].Values[0].ToString(); ClassFiles obj = new ClassFiles(strID); obj.GetFile(); string strFileName = obj.FileName; DeleteFile(strID, strFileName); FillGrid(); } protected void GridViewFiles_RowCommand(object sender, GridViewCommandEventArgs e) { } protected void DropDownListFileType_SelectedIndexChanged(object sender, EventArgs e) { FillGrid(); }

285


‫تمارين‬ ‫‪ .1‬برمج المثالين المذكورين أعاله بشكل كامل‬ ‫‪ .2‬غير معالج الحدث ‪ GridViewFiles_RowDataBound‬بحيث يُظهر حجم المف‬ ‫▪‬ ‫▪‬ ‫▪‬

‫بوحدات ‪ Bytes‬اذا كان الحجم أقل من ‪.1KB‬‬ ‫بوحدات ‪ KB‬اذا كان الحجم أكثر من ‪ 1KB‬وأقل أو يساوي ‪.1MB‬‬ ‫بوحدات ‪ MB‬خالف ذلك‪.‬‬

‫الحظ أن ‪1KB = 1024 Bytes‬‬ ‫التحكم بعدد المنازل بعد الفاصلة ممكن من خالل االستعانة بالعملية ‪ string.Format‬على النحو التالي‪:‬‬ ‫عدد األصفار بعد النقطة يجب أن يكون بعدد‬ ‫المنازل المطلوبة‬ ‫;)‪strValue = string.Format("{0:0.000}", dFileSizeInMBytes‬‬ ‫‪ .3‬أضف إلى الـ ‪ GridView‬إمكانية لتحرير خصائص الملفات‬ ‫‪ .4‬أضف إلى الـ ‪ TextBoxDescription‬إمكانية للتأكد من صحة اإلدخال (أي ‪Data Validation‬‬ ‫‪)Control‬‬ ‫‪ .5‬أضف ‪ try/catch‬لجميع العمليات وأظهر األخطاء بمساعدة ‪Label Control‬‬ ‫‪ .6‬أضف صفحة لعرض وتحميل الملفات (أي بدون إمكانية الرفع والحذف)‬

‫‪286‬‬


‫ملفات الصور‬ ‫حفظ الصورة ضمن منظومة الملفات‬ ‫) الصفحة ‪)ImagesEditor.aspx‬‬

‫بما أن الصور عبارة عن ملفات فكل ما قيل عن رفع الملفات وحفظها ضمن منظومة الملفات صحيح بالنسبة‬ ‫للصور‪ .‬ولذلك بإمكاننا استعمال نفس الجدول لتخزين الصور وحينها نعطي الحقل ‪ Type‬القيمة ‪ Image‬للداللة‬ ‫على أن الملف هو ملف صورة‪ .‬هذا يساعدنا حين كتابة استعالم ‪ SQL‬للحصول على كل الصور‪.‬‬ ‫التغيير الوحيد هو إضافة عامود جديد في الـ ‪ GridView‬قادر على عرض الصورة‪ .‬لذلك سنستعمل األداة‬ ‫‪ asp:Image‬بحيث نعطي الخاصية ‪ ImageUrl‬التابعة لها المسار المؤدي إلى ملف الصورة من خالل العملية‬ ‫‪ .Eval‬الحظ أنه بإمكان المستخدم النقر بالزر األيمن على االرتباط ‪ Download‬واختيار ‪Save Target As‬‬ ‫لتحميل الصورة بحجمها األصلي‪.‬‬ ‫>"‪<asp:TemplateField HeaderText="Image‬‬ ‫>‪<ItemTemplate‬‬ ‫"‪<asp:Image ImageUrl='<%#Eval("FileName") %>' runat="server‬‬ ‫> "‪Width="100px" Height="100px‬‬ ‫>‪</asp:Image‬‬ ‫>‪</ItemTemplate‬‬ ‫>‪</asp:TemplateField‬‬

‫‪287‬‬


‫ إلى الصور‬Tooltip ‫إضافة الـ‬

protected void GridViewImages_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { string strDescription = DataBinder.Eval(e.Row.DataItem,"Description").ToString(); // ‫نضيف الشرح كعنوان الى خصائص الخلية‬ e.Row.Cells[1].Attributes.Add("title", strDescription); } }

288


‫تمارين‬ ‫‪ .1‬برمج المثالين المذكورين أعاله (عن ملفات الصور) بشكل كامل‬ ‫‪ .2‬أضف إلى الـ ‪ GridView‬الموجود في إحدى الصفحات لحفظ الملفات عامو ًدا جدي ًدا تظهر فيه صورة‬ ‫صغيرة (بحجم ‪ )50X50‬حسب نوع الملف‪ .‬أي في السطر الذي فيه ملف من نوع ‪ Word‬تظهر صورة‬ ‫الورد‬

‫والذي فيه ملف من نوع ‪ PowerPoint‬تظهر الصورة‬

‫هذه الصور إلى الخاليا؟‬

‫‪289‬‬

‫وهكذا‪ .‬أين يجب أن تتم إضافة‬


‫لغة ‪XML‬‬ ‫لغة ‪ )Extensible Markup Language( XML‬هي لغة ترميز مثلها مثل لغة ‪ .HTML‬اال ان لغة ‪XML‬‬ ‫ُتمكن المستخدم من تعريف واستعمال الوسوم التي يريدها بنفسه خالفا للغة‪ HTML‬التي تحتوي على مجموعة‬ ‫وسوم مُعرفة مسبقا وال يمكن تغييرها‪ .‬أي يستطيع ان يوسع مجموعة الوسوم الموجودة باللغة ومن هنا جاءت‬ ‫ً‬ ‫قيمة لوصف‬ ‫التسمية ‪( Extensible‬أي قابلة للتوسيع واإلضافة)‪ .‬إمكانية التوسيع هذه تجعل من اللغة أدا ًة‬ ‫البيانات‪ .‬لذلك تعتبر لغة ‪ XML‬ايضا اللغة المتفق عليها واالكثر تقبال لوصف ولنقل المعلومات بين البرامج وخاصة‬ ‫بين المواقع بل ولحفظ المعلومات أي ممكن اعتبارها بدرجة معينة بديال لقواعد البيانات‪.‬‬ ‫يخزن كود الـ ‪ XML‬داخل ملفات نصيّة عاديّة مع الملحق ‪ , XML‬وبامكان مستكشف االنترنت (أي الـ ‪Internet‬‬ ‫‪ )Explorer‬ان يعرض محتوى هذه الملفات بشكل رائع كما سنرى ذلك الحقا‪.‬‬ ‫مثال‪ -:‬نريد وصف وتخزين نقاط ثنائية االبعاد في ملف ‪. XML‬‬ ‫معلوم ان لكل نقطة من هذه النقاط احداثيتين وهما احداثي ‪ x‬واحداثي‪.y‬‬ ‫وعليه بإمكاننا ان نخزن ذلك كما يلي‪:‬‬ ‫>?"‪<?xml version="1.0" encoding="utf-8‬‬ ‫>‪<Points‬‬ ‫>‪<Point‬‬ ‫هذا السطر هر سطر التصريح ‪(XML‬‬ ‫>‪<x>5</x‬‬ ‫>‪<y>8</y‬‬ ‫)‪ Declaration‬عن مواصفات نسخة ‪XML‬‬ ‫>‪</Point‬‬ ‫ال ُمستخدمة‪ .‬النسخة هنا هي نسخة رقم ‪ .1.0‬وأيضًا‬ ‫>‪<Point‬‬ ‫>‪<x>0</x‬‬ ‫نوع تشفير النصوص أو المحتوى هو ‪ utf-8‬الذي‬ ‫>‪<y>5</y‬‬ ‫>‪</Point‬‬ ‫يُعتبر ‪ Unicode‬ويشمل فيما يشمل اللغة العربية‬ ‫>‪<Point‬‬ ‫>‪<x>-2</x‬‬ ‫والعبرية‪ .‬الحظ أن السطر يبدأ بعالمة السؤال بعد‬ ‫>‪<y>2</y‬‬ ‫اشارة < وينتهي أيضا عالمة السؤال واالشارة >‪ .‬هذ‬ ‫>‪</Point‬‬ ‫>‪<Point‬‬ ‫المعلومات مفيدة بالنسبة للمحلل اللغوي أي الـ ‪Parser‬‬ ‫>‪<x>3</x‬‬ ‫>‪<y>2</y‬‬ ‫كما سيأتي معنا الحقًا‪.‬‬ ‫>‪</Point‬‬ ‫>‪</Points‬‬ ‫ًاذا نرى ان في هذا الملف قد خزنت النقاط )‪ ,(5,8) (0,5) (-2,2) (3,2‬الحظ ايضا اننا نستعمل نفس طريقة الـ‬ ‫‪ . HTML‬أي أنه يوجد للوسوم بداية ونهاية وبين وسم البداية ووسم النهاية نكتب مكوّ نات ذلك الجزء من المعلومات‬

‫‪290‬‬


‫وهكذا‪ .‬بإمكاننا أي ً‬ ‫ضا أن نعطي للوسوم خصائص وأن نعطي لهذه الخصائص قيمًا‪ .‬وعليه فبإمكاننا أن نصف النقاط‬ ‫ثنائية األبعاد من المثال المذكور أعاله كالتالي‪:‬‬ ‫>‪<Points‬‬ ‫>‪<Point x="5" y="8" /‬‬ ‫>‪<Point x="0" y="5" /‬‬ ‫>‪<Point x="2" y="-2" /‬‬ ‫>‪<Point x="3" y="2" /‬‬ ‫>‪</Points‬‬ ‫مثال‪ -:‬نريد وصف وتخزين آخر األخبار ‪.‬‬ ‫لكل خبر توجد المواصفات التالية‪:‬‬ ‫•‬

‫رقم الخبر‬

‫•‬

‫عنوان الخبر‬

‫•‬

‫تاريخ الخبر‬

‫•‬

‫تفاصيل الخبر‬

‫وعليه بإمكاننا ان نخزن ذلك كما يلي‪:‬‬ ‫>‪<LatestNews‬‬ ‫>"‪<Item ID="100‬‬ ‫>‪<Title>XML News</Title‬‬ ‫>‪<Details>Extensible Markup Language</Details‬‬ ‫>‪</Item‬‬ ‫>"‪<Item ID="101‬‬ ‫>‪<Title>HTML News</Title‬‬ ‫>‪<Details>HyperText Markup Language</Details‬‬ ‫>‪</Item‬‬ ‫>"‪<Item ID="102‬‬ ‫>‪<Title>C# News</Title‬‬ ‫>‪<Details>C# For Sharp Minds</Details‬‬ ‫>‪</Item‬‬ ‫> ‪</ LatestNews‬‬ ‫مثال‪ -:‬نريد وصف وتخزين واجهة فئة معينة (أي مجموعة العمليات الـ ‪ public‬التابعة للفئة)‪ .‬لكل عملية توجد‬ ‫المواصفات التالية‪:‬‬ ‫•‬

‫نوع القيمة المُرجعة‬

‫•‬

‫اسم العملية‬

‫•‬

‫قائمة البارامترات‪ .‬وهي عبارة عن سلسلة من األزواج تفصل بينها الفاصلة‪ .‬كل زوج مكون من نوع‬ ‫واسم‪.‬‬ ‫‪291‬‬


:‫وعليه بإمكاننا ان نخزن ذلك كما يلي‬

<Methods> <Method ReturnType="int" Name="Max"> <parameters> <Pararameter> <Type>int</Type> <Name>X</Name> </Pararameter> <Pararameter> <Type>int</Type> <Name>Y</Name> </Pararameter> </parameters> </Method> <Method ReturnType="float" Name="Average"> <parameters> <Pararameter> <Type>float</Type> <Name>X</Name> </Pararameter> <Pararameter> <Type>float</Type> <Name>Y</Name> </Pararameter> </parameters> </Method> </Methods>

:‫ًاذا نرى في هذا الملف العمليتين التاليتين‬

int Max(int x, int y) float Average(float x, float y) :Date ‫ مثال الفئة‬.‫بل بإمكاننا أن ُنخزن وصف فئة بشكل كامل‬ <?xml version="1.0" encoding="utf-8"?> <Classes> <Class Name="Date"> <Properties> <Property> <Access>private</Access> <Type>int</Type> <Name>Day</Name> </Property> <Property> <Access>private</Access>

292


<Type>int</Type> <Name>Month</Name> </Property> <Property> <Access>private</Access> <Type>int</Type> <Name>Year</Name> </Property> </Properties> <Constructors> <Constructor> <Parameters /> </Constructor> <Constructor> <Parameters> <Parameter> <Type>int</Type> <Name>Day</Name> </Parameter> <Parameter> <Type>int</Type> <Name>Month</Name> </Parameter> <Parameter> <Type>int</Type> <Name>Year</Name> </Parameter> </Parameters> </Constructor> </Constructors> <Methods> <Method ReturnType="void" Name="SetDay"> <parameters> <Pararameter> <Type>int</Type> <Name>Day</Name> </Pararameter> </parameters> </Method> <Method ReturnType="int" Name="GetDay"> <parameters> <Pararameter /> </parameters> </Method> </Methods> </Class> </Classes>

293


‫ مثال‬.‫ عند نهاية االسم‬/ ‫الحظ أنه إذا لم تكن هنالك قيمة بين وسم البداية ووسم النهاية نكتب حينها اسم الوسم فقط مع‬ :Default Constructor ‫الـ‬ <Constructor> <Parameters /> </Constructor> :GetDay() ‫أو مثال العملية‬ <Method ReturnType="int" Name="GetDay"> <parameters> <Pararameter /> </parameters> </Method> ً ‫كان بإمكاننا أن نكتب أي‬ :‫ضا‬ <Method ReturnType="int" Name="GetDay"> <parameters /> </Method>

294


‫عرض الملف بمساعدة مستكشف االنترنت‬ ‫إذا عرضنا ملف ‪ XML‬بمساعدة مستكشف االنترنت (أي الـ ‪ )Internet Explorer‬فانه ال ينفذ شيئا من األوامر‬ ‫(خال ًفا للغة ‪ )HTML‬وانما يقوم أوال بفحص مبنى الملف ومن ثم عرضه مع اعطاء امكانيات الستعراض محتويات‬ ‫الوسوم‪ .‬هذه االمكانيات تكون عادة متوفرة مع األداة ‪ Tree Control‬ألن مبنى هذه الملفات يكون هرميًا ومتداخالً‬ ‫كمبنى األشجار‪ .‬فلدينا جذر ‪ Root Node / Element‬هو الوسم >‪ <Methods‬وله أبناء ‪Child Nodes /‬‬ ‫‪ Elements‬وهو الوسم >‪ <Method‬وهذا الوسم له أبناء >‪ <Parameters‬وهكذا‪.‬‬ ‫مثال‪:‬‬

‫إذا نقرنا بال ر‬ ‫األيسر للفارة على‬ ‫اشارة ‪ +‬حينها نرى‬ ‫محتوى تلك العقدة‬

‫الحظ أن تفاصيل البرامترات غير معروضة وانها ُتعرض اذا نقرنا بالزر األيسر للفارة على اشارة ‪ +‬حينها نرى‬ ‫محتوى تلك القعدة (أي الـ ‪ )Node‬أو ذلك الوسم‪ .‬في الوضع االفتراضي ُتعرض محتويات جميع ال ُقد أي الوسوم‬ ‫بشكل كامل وبإمكاننا أن نفتح ونغلق من نشاء منها‪:‬‬ ‫ٍ‬

‫اذا نقرنا بال ر‬ ‫األيسر للفارة على‬ ‫اشارة ‪ -‬حينها تُغلق‬ ‫تلك القعدة ويختفي‬ ‫المحتوى‬

‫‪295‬‬


‫المبنى الهرمي‬ :XML ‫المثال التالي يُوضح المبنى الهرمي لملفات‬ http://www.w3schools.com/xml/xml_tree.asp :‫المصدر‬

:‫الصورة أعاله ُتلخص محتوى الملف التالي‬ <?xml version="1.0" encoding="utf-8"?> <bookstore> <book category="COOKING"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="CHILDREN"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="WEB"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore> <book> ‫ للوسم‬.‫< موجودون بداخله‬book> ‫< جميع أوالده وهم العناصر‬bookstore> ‫الجذر هنا هو الوسم‬ .<title>, <author>, <year>, <price> ‫أربعة أبناء هم‬ 296


‫فحص مبنى الملف‬ ‫ ُتسمى عملية فحص المبنى‬.‫ يقوم أوال بفحص مبنى الملف ومن ثم عرضه‬Internet Explorer ‫قلنا بأن الـ‬ ‫ وهو‬Parser ‫ ويُسمى البرنامج الذي يقوم بهذه المهمة بالـ‬.‫ أي تحليل لغوي بناء على قواعد معطاة‬Parsing ‫( ألن الخطوة األولى في عملية ترجمة البرامج تكون مرحلة التحليل‬Compiler ‫مركب أساسي في أي مترجم (أي‬ .‫اللغوي الذي يُبلغ المبرمج بوجود أخطاء قواعد في البرنامج (في حال وجودها طبعا) ويعطيه فرصة لتصحيحها‬ ‫( الذي‬Microsoft XML Parser) MSXML ‫ لهذا الغرض البرنامج‬Internet Explorer ‫يستخدم الـ‬ XML ‫ فئات للتعامل مع ملفات‬C# ‫ وتوفر أيضا لغة‬.Windows ‫ ضمن نظام التشغيل‬Microsoft ‫توفره شركة‬ .‫كما سنرى الح ًقا‬ ‫مثال لخطئ في المبنى‬ :‫< األول لم يتم اغالقه كما يجب‬Parameter> ‫الملف التالي يحتوي على خطئ وهو أن الوسم‬ <?xml version="1.0" encoding="utf-8"?> <Methods> <Method ReturnType="int" Name="Max"> <parameters> <Pararameter> <Type>int</Type> <Name>X</Name> <Pararameter> <Type>int</Type> <Name>Y</Name> </Pararameter> </parameters> </Method> </Methods> :‫ عند محاولة فتح هذا الملف الرسالة التالية‬Internet Explorer ‫لذلك يظهر الـ‬

The XML page cannot be displayed Cannot view XML input using XSL style sheet. Please correct the error and then click the Refresh button, or try again later.

End tag 'parameters' does not match the start tag 'Pararameter'. Error processing resource 'file:///H:/book/XML/Methods.xml...

</parameters>

------^

297


‫بعض قواعد لغة ‪XML‬‬ ‫•‬

‫يجب أن يكون لكل وسم بداية ونهاية‪ .‬اذا لم تكن للوسم قيمة يُكتب >‪.<TagName /‬‬

‫•‬

‫لغة ‪ XML‬حساسة لحالة الحرف‪ .‬المثال التالي خطئ ألن ‪Message ≠ message‬‬ ‫>‪<Message>Hi, How are you</message‬‬

‫•‬

‫فتح الوسوم واغالقها يجب أن يكون مرتبًا حسب القاعدة‪ :‬آخر وسم فُتح يُغلق أوالً وهكذا‪.‬‬

‫•‬

‫جذر‪.‬‬ ‫على الملف أن يحتوي على ٍ‬

‫•‬

‫قواعد تسمية الوسوم كقواعد تسمية المتغيرات بلغة ‪ C#‬وغيرها من لغات البرمجة‪.‬‬

‫•‬

‫بإمكاننا إعطاء خصائص )‪ (Attributes‬للوسوم كما هو الحال لدى وسوم لغة ‪ُ .HTML‬تكتب‬ ‫الخصائص في وسم البداية ولكن يجب وضع قيم الخصائص بين شطائر مزدوجة‪.‬‬

‫من المُفضل عدم استعمال الخصائص بكثرة بدالً من الوسوم وذلك ألن‬ ‫•‬

‫الوسوم بإمكانها أن تأخذ أكثر من قيمة بخالف الخصائص فكل خاصية تأخذ قيمة واحدة فقط‬

‫•‬

‫الوسوم لديها مبنى هرمي (أي شجرة) بخالف الخصائص‬

‫مثال‪ :‬بإمكاننا وصف المعلومات بعدة طرق‪:‬‬ ‫>? "‪<?xml version="1.0" encoding="utf-8‬‬ ‫>"‪<Comment date="12/02/2010‬‬ ‫>‪<Receiver>Sami</Receiver‬‬ ‫>‪<Sender>Ali</Sender‬‬ ‫>‪<Title>Reminder</Title‬‬ ‫>‪<Body>This is just a reminder</Body‬‬ ‫>‪</Comment‬‬ ‫أو بدون خصائص‪:‬‬ ‫>‪<Comment‬‬ ‫>‪<Date>12/02/2010</Date‬‬ ‫>‪<Receiver>Sami</Receiver‬‬ ‫>‪<Sender>Ali</Sender‬‬ ‫>‪<Title>Reminder</Title‬‬ ‫>‪<Body>This is just a reminder</Body‬‬ ‫>‪</Comment‬‬ ‫أو من خالل استغالل المبنى الهرمي‪:‬‬ ‫>‪<Comment‬‬ ‫>‪<date‬‬ ‫>‪<day>12</day‬‬ ‫>‪<month>02</month‬‬ ‫>‪<year>2010</year‬‬ ‫>‪</date‬‬ ‫>‪<Receiver>Sami</Receiver‬‬ ‫>‪<Sender>Ali</Sender‬‬

‫‪298‬‬


‫>‪<Title>Reminder</Title‬‬ ‫>‪<Body>This is just a reminder</Body‬‬ ‫>‪</Comment‬‬ ‫أما األسلوب التالي فهو غير مُفضّل على اإلطالق‪:‬‬ ‫"‪<Comment day="12" month="02" year="2010‬‬ ‫"‪Receiver="Sami" Sender="Ali" Title="Reminder‬‬ ‫>"‪Body="This is just a reminder‬‬ ‫>‪</Comment‬‬ ‫التأكد من صحة المبنى وصحة المعلومات )‪(Data Validation with XML Schema‬‬ ‫قلنا بأن ‪ُ XML‬تستعمل لوصف المعلومات‪ .‬وعرفنا أي ً‬ ‫ضا بأن للغة قواعد أشد من تلك التي للغة ‪ .HTML‬والسؤال‬ ‫اآلن هو هل هنالك امكانية أللزام المستخدم بكتابة وسوم معينة وقيمًا من مجاالت محددة فقط كما هو األمر في‬ ‫لغات البرمجة؟ وكمثال على ذلك نسأل ماذا لو كتب المستخدم التاريخ ناقصً ا كما يلي‪:‬‬ ‫>‪<date‬‬ ‫>‪<day>12</day‬‬ ‫>‪<month>02</month‬‬ ‫>‪</date‬‬ ‫الحظ أن وسم السنة ناقص!‬ ‫أو مثال ماذا لو كتب المستخدم قيمًا غير ممكنة كما يلي‪:‬‬ ‫>‪<date‬‬ ‫>‪<day>xyz</day‬‬ ‫>‪<month>02</month‬‬ ‫>‪<year>2010</year‬‬ ‫>‪</date‬‬ ‫الحظ أن قيمة الوسم ‪ day‬غير ممكنة!‬ ‫للقيام بمثل هذه الفحوصات نستعين بما يُسمى سكيما والتي تعني منهجية أي مجموعة قواعد للمبنى والمعنى (أي‬ ‫‪ .‬تمكننا السكيما من األمور اآلتية‪XML:‬المحتوى)‪ .‬تصف السكيما مبنى ملفات الـ‬ ‫•‬

‫تحديد الوسوم التي يجوز لها أن تظهر في الملف‬

‫•‬

‫تحديد الخصائص التي يجوز لها أن تظهر في الملف‬

‫•‬

‫تحديد المبنى الهرمي للوسوم أي الوسم وأوالده‬

‫•‬

‫ً‬ ‫جائزا لوسم ما أن يكون فار ًغا أم ال‬ ‫تحديد ما اذا كان‬

‫•‬

‫تحديد أنماط الوسوم والخصائص )‪(Data types for elements and attributes‬‬

‫•‬

‫تحديد قيم افتراضية للوسوم‬

‫•‬

‫‪...‬‬

‫تستعمل السكيما نفس قواعد الكتابة التي تستعملها لغة ‪ XML‬ولذلك لسنا بحاجة الى دراسة لغة اضافية للقيام بكتابة‬ ‫ف ما‪.‬‬ ‫سكيما لمل ٍ‬

‫‪299‬‬


:‫ نريد كتابة سكيما للملف التالي‬:‫مثال‬ <Comment> <Receiver>Sami</Receiver> <Sender>Ali</Sender> <Title>Reminder</Title> <Body>This is just a reminder</Body> </Comment> :‫السكيما المطلوبة للمثال المذكور أعاله تكون كالتالي‬ <?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="Comment"> <xs:complexType> <xs:sequence> <xs:element name="Receiver" type="xs:string"/> <xs:element name="Sender" type="xs:string"/> <xs:element name="Title" type="xs:string"/> <xs:element name="Body" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element>

Comment ‫العنصر أو الوسم‬ ‫عبارة عن نوع مر ّكب‬ ‫ ألنه ُمر ّكب‬ComplexType ‫ من عدة‬sequence ‫من سلسلة‬ ‫ كل واحد من‬.‫عناصر أو وسوم‬ string ‫هذ الوسوم هو من نوع‬ ‫وهو عبارة عن نوع أساسي بسيط‬ ‫ غير ُمر ّكب‬Simple Type

</xs:schema> XML ‫ ) ويُشار اليها في ملف الـ‬Comment.xsd ‫ (هنا‬xsd ‫ُتخ ّزن السكيما داخل ملف خاص مع األمتداد‬ :‫كالتالي‬ <?xml version="1.0"?> <Comment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Comment.xsd"> <Receiver>Sami</Receiver> <Sender>Ali</Sender> <Title>Reminder</Title> <Body>This is just a reminder</Body> </Comment> Web ‫ بواسطة الـ‬XML ‫ وفتحنا ملف الـ‬XML ‫واآلن اذا حفظنا ملف السكيما في نفس المجلد الذي فيه ملف الـ‬ ‫ فمثال الملف‬.‫ يقوم وبشكل تلقائي بفحص مبنى الملف وف ًقا للسكيما‬Web Developer ‫ فان الـ‬Developer ‫ غير مُع ّرف في السكيما‬Titlee ‫التالي فيه خطأ وهو أن الوسم‬

<?xml version="1.0"?>

300


‫"‪<Comment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance‬‬ ‫>"‪xsi:noNamespaceSchemaLocation="Comment.xsd‬‬ ‫>‪<Receiver>Sami</Receiver‬‬ ‫>‪<Sender>Ali</Sender‬‬ ‫>‪<Titlee>Reminder</Titlee‬‬ ‫>‪<Body>This is just a reminder</Body‬‬ ‫>‪</Comment‬‬ ‫الحظ هنا أن الـ ‪ Web Developer‬يضع ً‬ ‫خطا تحت الوسم الغير مُع ّرف وعند وضع الماوس فوق هذا الوسم‬ ‫تظهر رسالة تبين تفاصيل الخطأ‪:‬‬

‫مثال آخر‪ :‬الملف التالي ال يحتوي عاى الوسم ‪:Body‬‬

‫واألهم من ذلك كله أنه عندما نفتح هذه الملفات بواسطة عمليات خاصة لفتح وقراءة ملفات ‪ XML‬فان المحلل سوف‬ ‫يقطع عملية القراءة اذا خالف محتوى الملف السكيما‪.‬‬

‫‪301‬‬


‫ للقيام‬DOT NET FRAMEWORK ‫البرنامج التالي يبين لنا كيفية استعمال بعض الفئات الموجودة ضمن الـ‬ ‫ الموجودة‬Comment.xsd ‫ الذي يجب أن يكون محررا وفق السكيما‬Comment.xml ‫بقراءة محتوى الملف‬ :‫في نفس المجلد‬ public class XMLValidator { public static void Main(string[] args) { try { XmlTextReader r = new XmlTextReader("D:\\Book\\XML\\Comment.xml"); XmlValidatingReader v = new XmlValidatingReader(r); v.ValidationType = ValidationType.Schema; while (v.Read()) { if (v.NodeType == XmlNodeType.Element) Console.Write(v.Name + ": "); else if (v.NodeType == XmlNodeType.Text) Console.Write(v.Value + " "); else Console.WriteLine(); } v.Close(); } catch (Exception ex) { Console.WriteLine("Document is invalid"); }

‫ تمكننا‬XmlTextReader ‫الفئة‬ ‫من إنشاء كائن نمرر إلى باني‬ XmlValidatingReader ‫الفئة‬ ‫التي بدورها تمكننا من قراءة‬ ‫محتوى الملف عن طريق العملية‬ .Read

‫يوجد لدى الفئة‬ XmlValidatingReader ValidationType ‫الخاصية‬ ‫التي نستطيع من خاللها إبالغ‬ ‫ بأن ينفذ الفحص بنا ًء‬v ‫الكائن‬ ‫على السكيما المذكورة في ملف الـ‬ .XML

}} ‫ استثناء من نوع‬Read ‫في حال حصول أي خطأ متعلق بالسكيما ترمي العملية‬ ‫ فعندما وصلت‬.Comment.xml ‫ وهذا ما حصل بالفعل مع الملف‬.XmlSchemaValidationException :‫ ولم يكن موجو ًدا حصل األستثناء كما ثظهر التافذة التالية‬Body ‫القراءة الى السطر المتوقع أن يكون فيه العنصر‬

302


‫أما اذا كان الملف مواف ًقا للسكيما فان عملية القراءة تتم بدون أية أخطاء كما تبين ذلك النافذة التالية‪:‬‬

‫‪303‬‬


XML Databinding ‫ ولذل كان من الطبيعة أن يُقبل كقيمة‬.‫ يُعتبر مصدرً ا للمعلومات‬XML ‫كما بينت األمثلة السابقة فان أي ملف‬ ‫ المتوفرة لدى األدوات التي تدعم منهج ربط األدوات بمصادر المعلومات (أي‬DataSource ‫للخاصية‬ .)Databinding :Comments.xml ‫ التي تأخذ محتواها من الملف‬GridView ‫المثال التالي يستعمل األداة‬ <h1>Comments</h1> <asp:GridView ID="GridViewComments" runat="server" BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical"> <FooterStyle BackColor="#CCCCCC" /> <PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" /> <AlternatingRowStyle BackColor="#CCCCCC" /> </asp:GridView> </center> protected void Page_Load(object sender, EventArgs e) { DataSet ds; string strPath = Server.MapPath("Comments.xml"); ds = new DataSet(); //Read the contents of the XML file into the DataTable ds.ReadXml(strPath); GridViewComments.DataSource = ds.Tables[0].DefaultView; GridViewComments.DataBind(); } :‫والنتيجة تكون كالتالي‬

304


‫الملف ‪web.config‬‬ ‫ال شك بأنك الحظت وجود ملف بأسم ‪ web.config‬في كل مواقع الـ ‪ ASP.NET‬التي قمت بانشائها حتى اآلن‪.‬‬ ‫اذا فتحت اآلن هذا الملف ستجد ملف ‪ .XML‬اسم هذا الملف يدل على وظيفته‪ .‬معنى كلمة ‪Configuration‬‬ ‫التكوين‪ .‬والمقصود هنا أن هذا الملف يحتوى على خيارات التكوين العام للموقع ‪ .‬يتم إنشاؤه بشكل تلقائي عند إنشاء‬ ‫موقع جديد ‪.‬‬ ‫أمثلة على كيفية استعمال الملف‬ ‫‪ .1‬نص االتصال‬ ‫كما مر معنا سابقا عند الحديث عن الفئة ‪ Dbase‬توصلنا الى قاعدة البيانات من خالل العملية التالية‬ ‫)‪private static OleDbConnection MakeConnection(string dbFile‬‬ ‫{‬ ‫;)(‪OleDbConnection c = new OleDbConnection‬‬ ‫= ‪c.ConnectionString‬‬ ‫‪"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" +‬‬ ‫;)‪HttpContext.Current.Server.MapPath("~/App_Data/" + dbFile‬‬ ‫;)(‪c.Open‬‬ ‫;‪return c‬‬ ‫}‬ ‫كما هو واضح تفترض هذه العملية وجود المجلد ‪ App_Data‬ضمن مجلدات الموقع وبداخاه ملف قاعدة‬ ‫البيانات‪ .‬لكن ماذا لو تغير اسم ملف قاعدة البيانات أو تغير مكان وجوده؟ أو ماذا لو أردنا االنتقال مثال من‬ ‫‪ MS Access‬الى ‪MS SQL Server‬؟ طب ًعا هناك فرق بين نص ارتباط لـ ‪ MS Access‬ولـ ‪MS‬‬ ‫‪ .SQL Server‬في مثل هذه الحاالت سيكون لزامًا علينا أن نغير هذه القطعة من الكود وبالتالي يجب ترجمة‬ ‫وفحص الموقع من جديد‪ .‬البديل لذلك هو حفظ نص االرتباط داخل ملف الـ ‪ web.config‬حيث أن تغيير‬ ‫النص لن يلزم اعادة ترجمة وفحص الموقع‪ .‬نضيف نص االرتباط داخل الوسم أو القسم المُسمى‬ ‫‪ connectionStrings‬بواسطة األمر أو العنصر ‪.add‬‬ ‫ما علينا أن نفعله هو اضافة األسطر التالية الى الملف ‪:web.config‬‬

‫‪305‬‬


<configuration> <connectionStrings> <add name="DBConStr" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\DB.mdb;" providerName="System.Data.OleDb" /> </connectionStrings> … </configuration> ‫السطر‬

‫الشرح‬

<configuration>

web.config ‫بهذا العنصر يبدأ كل ملف‬

<connectionStrings>

‫بهذا العنصر نبدأ سلسة جديدة من نصوص‬ ‫ هذا يعني أنه بامكاننا أن نحفظ أكثر‬.‫االتصال‬ .‫لكل واحد اسمه وخصائصه‬.‫من نص اتصال‬

<add name="DBConStr" connectionString= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\DB.mdb;" providerName="System.Data.OleDb" />

‫ يمكننا من اضافة نص اتصال‬add ‫األمر‬ ‫) ومحتوى‬name ‫جديد له اسم خاص (قيمة‬ ‫ الخاصيّة‬.)connectionString ‫(قيمة‬ ‫ تبين أن قاعدة البيانات‬ProviderName

‫ معرّ ف مسب ّقا ويشير إلى المجلد‬DataDirectory ‫المتغير‬ App_Data </connectionStrings>

.‫بهذا العنصر ننهي سلسلة نصوص االتصال‬

</configuration>

web.config ‫بهذا العنصر ينتهي كل ملف‬

.‫هي أكسس‬

‫ من الملف‬DBConStr ‫ بحيث تقرأ نص االتصال المسمّى‬MakeConnection ‫واآلن نعيد صياغة العملية‬ Dot Net ‫ الموجودة ضمن فئات الـ‬ConfigurationManager ‫ القراءة تتم بواسطة الفئة‬.web.config ‫ الذي يُستعمل هنا كما ُتستعمل‬ConnectionString )Collection( ‫ لدى هذه الفئة التجميع‬.Framework .‫المصفوفات‬ private static OleDbConnection MakeConnection(string dbFile) { OleDbConnection c = new OleDbConnection(); c.ConnectionString = ConfigurationManager.ConnectionStrings["DBConStr"].ConnectionString; c.Open(); return c; }

306


‫هنالك امكانية ثانية لتخزين نص االتصال في الملف ‪ web.config‬وهي طريقة المفتاح والقيمة (‪Key-‬‬ ‫‪ .)Value Pairs‬والمقصود أننا نضيف عناصر جديدة الى القسم (‪ )Section‬المُسمّى ‪( appSettings‬أي‬ ‫‪ Application Settings‬والتي تعني خيارات التطبيق أو الموقع)‪ .‬تتم االضافة بواسطة العنصر ‪ .add‬لدى‬ ‫هذا األمر خاصيّتان هما ‪ Key‬و ‪ Key .Value‬تأخذ كقيمة اسم نص االتصال‪ .‬أما ‪ Value‬فتأخذ كقيمة‬ ‫تفاصيله‪ .‬تتم االضافة بواسطة العنصر ‪ add‬كالتالي‪:‬‬ ‫>‪<appSettings‬‬ ‫‪<add‬‬ ‫"‪key="DBConStr‬‬ ‫\|‪value="Provider=Microsoft.Jet.OLEDB.4.0;DataSource=|DataDirectory‬‬ ‫>‪DB.mdb;"/‬‬ ‫>‪</appSettings‬‬ ‫واآلن تقرأ العملية ‪ MakeConnection‬نص االتصال كالتالي‪:‬‬ ‫)‪private static OleDbConnection MakeConnection(string dbFile‬‬ ‫{‬ ‫;)(‪OleDbConnection c = new OleDbConnection‬‬ ‫= ‪c.ConnectionString = c.ConnectionString‬‬ ‫;]"‪System.Configuration.ConfigurationSettings.AppSettings["DBConStr‬‬ ‫;)(‪c.Open‬‬ ‫;‪return c‬‬ ‫}‬ ‫‪ .2‬حجم الملفات‬ ‫هل استعملت مرة األداة ‪ asp:FileUpload‬وحاولت رفع ملفات يزيد حجمها عن ‪4MB‬؟ في مثل هذه الحال‬ ‫سيحصل خطأ ولن يكون بامكان األداة تخزين أية معلومات عن الملف‪ .‬ما الحل ًاذا؟‬ ‫تأخذ هذه األداة الحجم األقصى للملفات المُراد رفعها بواسطتها من الملف ‪ web.config‬وبالتحديد من العنصر‬ ‫‪ httpRunTime‬الذي يمكننا من تحديد بعض الخيارات التي يحتاجها البروتوكول ‪ http‬عند عمل الموقع‬ ‫(أي ‪ .)At Run Time‬الحجم االفتراضي الذي تعمل به هذه األداة هو ‪ .4MB‬ويتم تغيير هذا الحجم بواسطة‬ ‫اضافة السطر التالي الى القسم ‪ System.web‬في الملف ‪:web.config‬‬ ‫>‪"/‬الحجم األقصى بالكيلوبايت"=‪<httpRuntime maxRequestLength‬‬ ‫مثال‪ :‬السطر التالي يحدد الحجم األقصى ليصبح ‪:5MB‬‬ ‫>‪<httpRuntime maxRequestLength="5000"/‬‬

‫‪307‬‬


‫لمزيد من المعلومات حول العنصر ‪:httpRunTime‬‬ ‫‪http://msdn.microsoft.com/en-us/library/e1f13641.aspx‬‬ ‫‪Enable Debugging .3‬‬ ‫>‪<compilation defaultLanguage="C#" debug="true" /‬‬ ‫‪ .4‬خيارات األمان‬ ‫زائر للموقع أن يتصفحها وصفحات ال‬ ‫ال يخلو موقع ديناميكي من نوعين من الصفحات‪ :‬صفحات يجوز لكل‬ ‫ٍ‬ ‫يجوز اال ّ لمن "يعرفه" الموقع‪ُ .‬تسمّى عملية التعرف على الزائر من خالل سؤاله عن كلمة المرور وكلمة السر‬ ‫بالمصادقة (‪ .)Authentication‬ولكن بعد دخوله الى الموقع تبقى هنالك أجزاء ال يجوز له أن يصل اليها‬ ‫كصفحات االدارة مثال‪ُ . .‬تسمّى عملية تحديد الصفحات التي يجوز للزائر أن يصل اليها بعملية التخويل‬ ‫(‪.)Authorization‬‬

‫‪Forms Authentication‬‬ ‫ُتعتبر طريقة المصادقة المستندة إلى النماذج األكثر استعماالً في مواقع الـ ‪ .ASP.NET‬تعتمد هذه الطريق‬ ‫على أسلوب الكعكات (أي ‪ )Cookies‬لمتابعة حركة الزائر في الموقع‪ .‬الدخول الى الموقع يكون كما يوحي‬ ‫بذلك اسم الطريقة من خالل نموذج الـدخول (أي ‪ .)Login Form‬اذا تمت عملية المصادقة بنجاح يتم انشاء‬ ‫‪ Cookie‬خاصة بهذا المسثخدم وتستعمل في كل مرة يحاول المستخدم فيها الوصول الى احدى الصفحات‬ ‫المحمية‪ .‬فاذا لم يكن قد دخل عن طريق المصادقة يُحوّ ُل مباشرة الى الصفحة التي تحتوي على نموذج الدخول‬ ‫(أي الـ ‪.)Login Form Page‬‬ ‫تستعمل هذه الطريقة الملف ‪ web.config‬لتخزين المعلومات عن المستخدمين بالطريقة التالية‪:‬‬ ‫>‪<configuration‬‬ ‫>‪<system.web‬‬ ‫>"‪<authentication mode="Forms‬‬ ‫"‪<forms name="AuthCookie" path="/" loginUrl="login.aspx" protection="All‬‬ ‫>"‪timeout="30‬‬ ‫>"‪<credentials passwordFormat="Clear‬‬ ‫>‪<user name="khalil" password="test"/‬‬ ‫>‪<user name="admin" password="123"/‬‬ ‫>‪</credentials‬‬ ‫>‪</forms‬‬ ‫>‪</authentication‬‬ ‫>‪<authorization‬‬ ‫>‪<deny users="?"/‬‬ ‫>‪</authorization‬‬ ‫>‪</system.web‬‬ ‫>‪</configuration‬‬ ‫هنالك ‪ 3‬عناصر أساسية ُتستعمل لحفظ البيانات اللالزمة وهي ( ‪authentication, credentials,‬‬ ‫‪ .)authorization‬لكل واحد من هذه العناصر مجموعة خصائص مشروحة في الجداول التالية‪:‬‬

‫‪308‬‬


‫‪:Authentication‬‬ ‫الخاصية‬

‫الشرح‬ ‫هذا هو اسم الكعكة‬

‫‪Name‬‬

‫المسار الذي ُتحفظ عنده الكعكات‬

‫‪Path‬‬

‫الصفحة التي يُحوّ ُل إليها الزائرون الذين يدخلوا عن طريق المصادقة‬ ‫حماية الكعكة‪ .‬القيمة االفتراضية هي ‪ All‬وتعني حماية كاملة أي مشفرة‬

‫‪LoginUrl‬‬ ‫‪Protection‬‬

‫عمر الكعكة بالدقائق‬

‫‪Timeout‬‬

‫‪:Credentials‬‬ ‫الخاصية‬

‫الشرح‬ ‫طريقة تشفير كلمة المرور‪ .‬تأخذ واحدة من ‪ 3‬قيم هي‪:‬‬

‫‪passwordFormat‬‬

‫‪ :Clear‬أي بدون تشفير‬ ‫‪ :SHA1, MD5‬تشفير بواسطة ألغوريثمات الـ ‪.Hashing‬‬ ‫هنا يتم تخزين كلمة المرور واسم المُستخدم‬

‫‪User‬‬

‫‪:Authorization‬‬ ‫الخاصية‬

‫الشرح‬ ‫هنا تتم عملية السماح للمستخدمين ومنعهم‬

‫‪Deny | Allow‬‬

‫للمزيد عن كيفية تنفيذ المصادقة المستندة إلى النماذج‪:‬‬ ‫‪http://support.microsoft.com/kb/301240/ar‬‬ ‫للمزيد عن كيفية تنفيذ مصادقة ‪ Windows‬والتخويل في ‪:ASP.NET‬‬ ‫‪http://support.microsoft.com/kb/323176/ar‬‬

‫‪309‬‬


‫خدمات الوب‬ ‫‪Web Services‬‬ ‫نالحظ عند تصفح بعض المواقع أنها توفر خدمات لمعرفة مثال درجات الحرارة في مناطق أو حتى دول معينة أو‬ ‫معرفة أسعار العمالت المختلفة أو حسابات تحويل العمالت وف ًقا ألسعارها الحالية أو معرفة نتائج ألعاب رياضية‬ ‫معينة مثل كرة القدم وما شابه ذلك‪ .‬والسؤال المطروح اآلن هو كيف يتم ادخال هذه القيم التي يتمتع تغييرها‬ ‫بديناميكية عالية ً‬ ‫جدا‪ .‬فمثال قيم العمالت تتتغير ربما كل ساعة أو حتى كل دقيقة كونها خاضعة لألحداث العالمية‬ ‫ووضع األسواق العالمية وما الى ذلك‪ .‬هل يُعقل اذن أن يجلس انسان ما أمام الحاسوب ليلقن الموقع بهذه المعلومات‬ ‫في هذه الفترات المتقاربة ج ًدا؟ ربما كان هذا هو الحال ح ًقا قبل أن يتم تصميم ما يُسمى بخدمات الوب المختلفة التي‬ ‫ُتم ّكن المواقع أنفسها من طلب هذه المعلومات من حاسوب الخادم الذي يوفر هذه الخدمة أو هذه المعلومات في أية‬ ‫لحظة دون وجود أية حاجة ألنسان ما أن يتدخل ليعين على القيام بهذه المهام‪ .‬اذن فمعظم المواقع التي توفر هذه‬ ‫الخدمات انما توفرها بمساعدة خدمات الوب‪ .‬ولذلك سنبحث في هذا الفصل المواضيع التالية‬ ‫•‬

‫ما هي خدمات الوب‬

‫•‬

‫كيف يمكن استخدام خدمة وب معينة متوفرة في الوب‬

‫•‬

‫كيف يمكن تصميم وكتابة خدمة وب معينة وتوفيرها لباقي المواقع في الوب‬

‫ما هي خدمات الوب‬ ‫ُتعتبر خدمات الوب م َُر ِّكبات برمجية (أي ‪ )Software Components‬توفر عمليات )‪ُ (Methods‬ت َنفذ على‬ ‫حاسوب الخادم وعن بعد )‪ (Remote Procedure Call‬بعد أن يطلب ذلك حاسوب الزبون‪ .‬هذه العمليات‬ ‫ليست متوفرة بشكل مستقل وانما تكون موجودة ضمن فئات (‪ )Classes‬معينة كجز ٍء من واجهة تلك الفئات (أي‬ ‫ً‬ ‫عامة (أي ‪)public‬‬ ‫‪ .)Class Interfaces‬معروف أن واجهة الفئة هي مجموعة العمليات التي تكون مشاعً ا أو‬ ‫ولذلك تكون هذه العمليات التي ُتسمى أي ً‬ ‫ضا عمليات وب )‪ (Web Methods‬أيضً ا ‪ .public‬تتم عملية االتصال‬ ‫وتبادل المعلومات بين الخادم والزبون عن طريق بروتوكوالت الوب )‪ (Web Protocols‬المختلفة باالستعانة‬ ‫بلغة ‪ XML‬الي تفهمها جميع برمجيات الوب بغض النظر عن نوع الحاسوب أو نظام التشغيل كما سيوضح ذلك‬ ‫الح ًقا‪ .‬بكلمات بسيطة‪ ،‬خدمة الوب عبارة عن فئة نضيف وصف واجهتها الى مشروعنا ونستخدمها كما نستخدم أية‬ ‫فئة أُخرى‪ ،‬أي ننشئ كائ ًنا من نوع هذه الفئة ثم نستدعي بمساعدة ذلك الكائن العمليات التي توفرها الفئة مع الفرق‬ ‫الوحيد بأن تنفيذ هذه العمليات يتم في حاسوب الخادم وتبادل المعلومات يتم بمساعدة لغة ‪ XML‬والبروتوكوالت‬ ‫المختلفة‪ .‬مع أنه بإمكان المبرمج أن يغض النظر عن هذه التفاصيل وأن يتعامل مع الفئة بعد دمج وصفها بالمشروع‬ ‫كأنها فئة محلية عادية‪ .‬لنأخذ اآلن مثاالً على كيفية استعمال خدمة وب لمعرفة حالة الطقس في بلد ما‪.‬‬

‫‪310‬‬


‫استخدام خدمة وب معينة متوفرة في الوب‬ ‫توفر بعض المواقع مثل ‪ www.webservicex.net‬ومثل ‪ www.xmethods.com‬معلومات تفصيلية عن‬ ‫خدمات الوب المختلفة‪ .‬ولذلك سأشرح هنا كيفية استعمال خدمة الوب ‪ GlobalWeather‬المذكورة في الموقع‬ ‫األول )‪ (webservicex‬لمعرفة حالة الطقس لبعض المدن في البالد‪.‬‬ ‫الخطوات الالزمة الستعمال الخدمة هي كالتالي‪:‬‬ ‫•‬

‫علينا أن نضيف وصف واجهة فئة الخدمة الى الموقع من خالل‬

‫•‬

‫النقر على الزر األيمن للفارة على اسم الموقع في الـ ‪ Solution Explorer‬واختيار األمر ‪Add Web‬‬ ‫‪ Reference‬من القائمة التي تظهر‬

‫‪311‬‬


‫•‬

‫نكتب العنوان االنترنتي للخدمة كما يظهر في الموقع ‪ webservicex‬تحت العنوان ‪ Endpoint‬في‬ ‫الصندوق المسمى ‪ URL‬ثم ننقر على الزر ‪:Go‬‬

‫‪312‬‬


‫بعدها تظهر نافذة الحوار التالية‪:‬‬ ‫اسم الفئة‬ ‫أو الخدمة‬

‫هذه هي العمليات التي توفرها لنا هذه‬ ‫الخدمة‪:‬‬ ‫• ‪GetCitiesByCountry‬‬ ‫• ‪GetWeather‬‬ ‫سوف نستخدم هاتين العمليتين الحقًا‪.‬‬

‫بعدها ننقر على الزر ‪Add Reference‬‬

‫وبهذا نكون قد أنهينا اضافة واجهة الفئة الى الموقع‪ .‬ولذلك نرى أن ملفات الموقع قد زادت كما يظهر في الصورة‬ ‫التالية‪:‬‬

‫‪313‬‬


‫نالحظ أن الذي أضيف هو ما يسمى بفضاء األسماء ‪ net.webservicex.www‬وفيه الفئة ‪.globalweather‬‬ ‫كما قلنا ساب ًقا فان الخدمة عبارة عن فئة موجودة عل حاسوب خادم باسم معين‪ .‬والسؤال المطروح اآلن هو ماذا‬ ‫سيحصل لو وجدت في حاسب خادم آخر فئة بنفس االسم؟ كيف سيتعامل الزبون (أي مبرمج الموقع) مع هذه الفئات؟‬ ‫لحل هذه اإلشكالية وُ جد ما يُسمى بفضاء األسماء‪ ،‬بحيث نستعمل كجز ٍء من اسم الفئة العنوان الالنترنتي لمكان‬ ‫وجودها‪ ،‬ومعلوم أن هذا العنوان ال يمكن أن يتكرر في عالم الوب أب ًدا‪ .‬ولذلك سيظهر اسم الفئة حين استعمالها كأنه‬ ‫عنوان انترنتي عادي‪.‬‬ ‫لفهم وظيفة ومحتوى الملفين ‪ globalweather.disco‬و ‪ globalweather.wsdl‬ال بد من شرح بعض‬ ‫المصطلحات األساسية عن خدمات الوب‬

‫البروتوكول ‪Simple Object Access Protocol-SOAP‬‬

‫‪'SOAP provides a simple and lightweight mechanism for exchanging structured‬‬ ‫‪and typed information between peers in a decentralized, distributed environment‬‬ ‫'‪using XML.‬‬ ‫‪From The SOAP specification: http://www.w3.org/TR/soap12-part1/‬‬

‫البروتوكول ‪ SOAP‬عبارة عن بروتوكول اتصاالت يعتمد على لغة ‪ XML‬لتبادل المعلومات بين التطبيقات‬ ‫وخدمات الوب المختلفة‪ .‬يستخدم هذا البرتوكول أيضا الستدعاء العمليات التابعة لخدمات الوب ولنقل المعلومات‬ ‫المُرجعة من قبلها إلى الزبون‪.‬‬ ‫يتكون البروتوكول ‪ SOAP‬من األجزاء التالية‬ ‫‪ .1‬المغلف (‪ )Envelope‬الذي يصف محتوى رسالة البروتوكول ومعلومات عن الجهة التي عليها أن تعالج هذه‬ ‫الرسالة وأيضا يحدد هل هذه الرسالة ملزمة أم اختيارية‬ ‫‪ .2‬مجموعة قواعد تحدد كيفية القيام بعملية الـ ‪ Serialization‬ألنماط المعلومات (‪ )Data Types‬المختلفة‬ ‫التابعة لخدمات الوب ويحدد أيضا كيفية استخدام هذا األنماط‪.‬‬ ‫‪ .3‬وصف كيفية استدعاء عمليات خدمات الوب عن بعد‪.‬‬ ‫العنصر األساسي في رسالة البروتوكول ‪ SOAP‬تكون دائما الوسم ‪ Envelope‬لمنع أي تعارض في أسماء‬ ‫الوسوم يحدد فضاء لألسماء خاص بالبروتوكول ‪.‬‬

‫‪314‬‬


‫تحتوي رسالة البروتوكول ‪ SOAP‬أيضا على الوسم ‪ body‬الذي يحتوي على المعلومات الذي يجب نقلها‪ .‬من‬ ‫اجل استدعاء عملية ما‪ ،‬يُكتب هنا اسم العملية‪ .‬بارامترات العملية تضاف كعناصر أوالد )‪.(Child Elements‬‬ ‫رسالة الرد أي الـ ‪ Response‬تكون أيضا مبنية بنفس الشكل‪ .‬الكلمات ‪ Response‬و‪ Result‬تضاف إلى اسم‬ ‫العملية التي تم استدعاؤها والمعلومات المرجعة من قبلها تضاف كعناصر أوالد‪ .‬يسمح هذا البروتوكول أيضا بنقل‬ ‫)‪ (Passing‬وإرجاع )‪ (Return‬كائنات مركبة لفئات معينة‪.‬‬ ‫المُميز لهذا البرتوكول سهولته ومبناه البسيط بحيث يتم نقل الكائنات من خالل نقل قيم خصائصها وهو ما يُسمى‬ ‫بعملية الـ ‪ .Serialization‬المستقبل )‪ (Receiver‬لهذه المعلومات التي هي عبارة عن كود ‪ XML‬يوم باستعادة‬ ‫الكائن من هذا الكود وهما يسمى بعملية الـ ‪ .DeSerialization‬البروتوكول غير متعلق بنظام تشغيل معين وال‬ ‫بلغة برمجة معينة‪.‬‬ ‫يستعمل ‪ SOAP‬البروتوكول ‪ HTTP‬لتنفيذ عمليات نقل الرسائل‪.‬‬ ‫في المقاطع التالية يمكن رؤية هذه المباني للرسائل بشكل واضح وذلك (على سبيل المثال فقط) للعملية‬ ‫‪ GetCitiesByCountry‬التابعة للخدمة ‪ .GlobalWeather‬يظهر هذا الوصف عندما تطلب تنفيذ العملية في‬ ‫الموقع ‪:www.webservicex.net‬‬

‫‪POST /globalweather.asmx HTTP/1.1‬‬ ‫‪Host: www.webservicex.net‬‬ ‫‪Content-Type: text/xml; charset=utf-8‬‬ ‫‪Content-Length: length‬‬ ‫"‪SOAPAction: "http://www.webserviceX.NET/GetCitiesByCountry‬‬

‫>?"‪<?xml version="1.0" encoding="utf-8‬‬ ‫"‪<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance‬‬ ‫"‪xmlns:xsd="http://www.w3.org/2001/XMLSchema‬‬ ‫>"‪xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/‬‬ ‫>‪<soap:Body‬‬ ‫>"‪<GetCitiesByCountry xmlns="http://www.webserviceX.NET‬‬ ‫>‪<CountryName>string</CountryName‬‬ ‫وصف لرأس الدالة وأنها تأخذ‬ ‫صا كبارامتر‬ ‫ن ً‬

‫>‪</GetCitiesByCountry‬‬ ‫>‪</soap:Body‬‬ ‫>‪</soap:Envelope‬‬

‫‪315‬‬


HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetCitiesByCountryResponse xmlns="http://www.webserviceX.NET"> <GetCitiesByCountryResult>string</GetCitiesByCountryResult> </GetCitiesByCountryResponse> </soap:Body> </soap:Envelope>

‫وصف لنمط القيمة ال ُمرجعة من قبل‬ Response ‫ الحظ الكلمات‬.‫العملية‬ Result ‫و‬

:XML ‫ تظهر النتيجة على شكل كود‬Syria ‫بعد طلب تنفيذ العملية مثال للجمهورية السورية‬ <?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://www.webserviceX.NET"> <NewDataSet> <Table> <Country>Syrian Arab Republic</Country> <City>Aleppo International Airport</City> </Table> <Table> <Country>Syrian Arab Republic</Country> <City>Damascus Int. Airport</City> </Table> <Table> <Country>Syrian Arab Republic</Country> <City>Deir Ezzor</City> </Table> <Table> <Country>Syrian Arab Republic</Country> <City>Kamishli</City> </Table> <Table> <Country>Syrian Arab Republic</Country> <City>Lattakia</City>

316


</Table> <Table> <Country>Syrian Arab Republic</Country> <City>Palmyra</City> </Table> </NewDataSet> </string> WSDL - Web Service Description Language “Web Services Description Language (WSDL) is an XML format for describing Web services. WSDL enables one to separate the description of the abstract functionality offered by a service from concrete details of a service description such as "how" and "where" that functionality is offered”. ‫ هذا الوصف يحتوي على معلومات عن مكان‬.XML ‫ عبارة عن لغة لوصف خدمات الوب تعتمد على لغة‬WSDL ‫وجود الخدمة وعن العمليات التي توفرها وأيضا يحتوي على وصف لألنماط التي تحتاجها الخدمة والتي تعمل بها‬ ‫ هذا الوصف يُمكن البرامج أي المواقع‬.‫ وأيضا يحتوي الوصف على بروتوكوالت االتصال المستخدمة‬.‫العمليات‬ .‫( أي كائنات نائبة تمكنها من استخدام الخدمة‬Proxy Objects) ‫التي تستعمل الخدمة من إنشاء كائنات بروكسي‬ (Derived ‫( والتي هي عبارة عن فئة مشتقة‬Proxy Class) ‫هذه الكائنات يتم إنشاؤها من فئة بروكسي‬ .Dot NET Framework ‫ التابعة للـ‬SoapHttpClientProtocol ‫ من فئة الفئة‬Class) ‫ كل هذا يحصل الن العمليات (وأهمها العملية‬.‫هذه الفئة المشتقة تنوب عن فئة خدمة الوب وهو ما يسمى بالبروكسي‬ ‫ ولذلك ال بد من االشتقاق حتى‬.protected ‫) التي تستخدم الستدعاء خدمات الوب معرفة محمية أي‬Invoke .‫يسمح لنا باستعمالها‬ ‫ التي أضيفت إلى الموقع تم اشتقاقها من الفئة‬GlobalWeather ‫الحظ أن الفئة‬ :SoapHttpClientProtocol public class GlobalWeather : SoapHttpClientProtocol ‫ التي ُتستخدم الستدعاء العمليات التي توفرها‬Invoke ‫ تحتوي على العملية‬SoapHttpClientProtocol ‫الفئة‬ :‫ كما يظهر فيما يلي‬protected ‫ محمية‬Invoke ‫ العملية‬.‫الخدمة‬ namespace System.Web.Services.Protocols { //Summary: // Specifies the class client that proxies derive from when using // SOAP. public class SoapHttpClientProtocol : HttpWebClientProtocol { public SoapHttpClientProtocol(); protected object[] Invoke(string methodName, object[] parameters); }}

317


‫ طب ًعا المبرمج ال يهتم بكل هذه التفاصيل ألن الـ‬.‫ولذلك علينا أن نشتق منها حتى يُسمح لنا باستخدام هذه العملية‬ .‫ يقوم بانجاز كل هذه االعدادت بدون تدخل من قبل المبرمج‬Web Developer ‫ كيفية التعامل مع هذه الخدمة بمعني أنه يحتوي على وصف للدوال الموجودة بها أي التي‬wsdl ‫إذن يوضح ملف الـ‬ .)Web Service Interface( ‫توفرها وهو ما يُسمى بواجهة الفئة أو الخدمة‬ .XML ‫إذا فتحت هذا الملف وعاينت محتواه ستجد أمامك كود‬ globalweather.wsdl ‫ في الملف‬GlobalWeather ‫هذا الوصف نجده بالنسبة للخدمة‬ Discovery – DISCO ‫ (اكتشاف) عبارة عن تقنية لتمكين األشخاص والتطبيقات المختلفة من التعرف على خدمات الوب‬DISCO ‫ التي تحتوي على معلومات‬disco ‫ مع الملحق‬XML‫ تستعمل هذه التقنية ملفات‬.‫ أي تمكنهم من اكتشافها‬،‫الموجودة‬ disco ‫ عندما يبحث شخص أو تطبيق ما عن خدمة ويب معينة يتم تمشيط ملفات الـ‬.‫ التابع للخدمات‬wsdl ‫عن الـ‬ .‫واكتشاف الخدمات الموجودة‬ :‫ كما يلي‬GlobalWeather.disco ‫ يظهر في الملف‬disco ‫محتوى الـ‬ <?xml version="1.0" encoding="utf-8"?> <discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/disco/"> <contractRef ref="http://www.webservicex.net/globalweather.asmx?wsdl" docRef="http://www.webservicex.net/globalweather.asmx" xmlns="http://schemas.xmlsoap.org/disco/scl/" /> <soap address="http://www.webservicex.net/globalweather.asmx" xmlns:q1="http://www.webserviceX.NET" binding="q1:GlobalWeatherSoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" /> <soap address="http://www.webservicex.net/globalweather.asmx" xmlns:q2="http://www.webserviceX.NET" binding="q2:GlobalWeatherSoap12" xmlns="http://schemas.xmlsoap.org/disco/soap/" /> </discovery>

318


‫العنصر األساسي في هذا الملف هو ‪ .contractRef‬الخاصية ‪ ref‬التابعة لهذا العنصر تشير إلى الـ ‪wsdl‬‬ ‫والخاصية ‪ docref‬تشير إلى الخدمة نفسها‪ .‬في العنصر‪ soap‬تخزن معلومات عن كيفية استخدام البروتوكول‬ ‫‪ SOAP‬الستدعاء خدمة الوب‪.‬‬

‫‪UDDI – Universal, Description, Discovery and Integration‬‬ ‫عبارة عن ‪ Directory Service‬أي خدمة سجل عام يُمكن الشركات والمعنيين من نشر خدمات الوب والبحث‬ ‫عنها‪ .‬ففي هذا السجل يتم تخزين المعلومات الالزمة (أي الـ ‪ )WSDL‬عن الخدمات المختلفة حتى يتمكن الباحث‬ ‫عنها من الوصول إليها‪ .‬هذا السجل يستعمل أي ً‬ ‫ضا ‪ SOAP‬لالتصال‪.‬‬ ‫الصورتان التاليتان تلخصان العمليات التي تتم منذ لحظة البحث عن الخدمة إلى لحظة تبادل المعلومات معها‪:‬‬

‫‪319‬‬


320


‫استعمال الخدمة‬ ‫رأينا بأن الخدمة توفر لنا دالتين هما‪:‬‬ ‫‪ )1‬الدالة ‪ GetCitiesByCountry‬تعيد لنا أسماء جميع المدن التي يمكن معرفة درجات الحرارة فيها من قبل‬ ‫الخدمة بالنسبة للدولة الممرة كبارامتر‬ ‫;)‪public string GetCitiesByCountry(string CountryName‬‬ ‫‪ )2‬الدالة ‪ GetWeather‬تعيد لنا تفاصيل حالة الطقس للمدينة ‪ CityName‬الموجودة في الدولة‬ ‫‪CountryName‬‬ ‫;)‪public string GetWeather(string CityName, string CountryName‬‬ ‫النص الذي تعيده هذه الدوال عبارة عن كود ‪ XML‬يصف النتيجة المُرجعة من قبلها‪:‬‬ ‫مثال تعيد الدالة األولى النص التالي لدولة اسرائيل‪:‬‬

‫والدالة الثانية تعيد النص التالي للمكان … ‪Ben-Gurion International Airport, Israel‬‬

‫‪321‬‬


322


‫ جميع المدن في اسرائيل التي يمكن معرفة درجات الحرارة فيها‬GridView ‫الصفحة التالية تعرض لنا بواسطة‬

‫ يحصل على تفاصيل الطقس للمدينة الموجودة في نفس‬Show Weather ‫اذا نقر المستخدم على االرتباط‬ :‫السطر‬

:‫كود التنسيق‬ <asp:GridView ID="GridViewCities" runat="server" onrowcommand="GridViewCities_RowCommand" Caption="Cities Available" BackColor="LightGoldenrodYellow" BorderColor="Tan" BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None"> <Columns> ‫الحظ أننا نقوم بعملية الوصل‬ <asp:TemplateField> ‫كالمعتاد مع أن مصدر‬ <ItemTemplate> ‫المعلومات أي الـ‬ <asp:LinkButton ID="LabelGetWeather" ‫ عبارة عن‬DataSource CommandName="GetWeather" XML ‫ملف‬ CommandArgument='<%#Bind("City") %>' runat="server">Get Weather</asp:LinkButton> </ItemTemplate> </asp:TemplateField> </Columns> <FooterStyle BackColor="Tan" /> <PagerStyle BackColor="PaleGoldenrod" ForeColor="DarkSlateBlue" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" /> <HeaderStyle BackColor="Tan" Font-Bold="True" />

323


<AlternatingRowStyle BackColor="PaleGoldenrod" /> </asp:GridView> <br /> <asp:GridView ID="GridViewWeather" runat="server" BackColor="LightGoldenrodYellow" BorderColor="Tan" BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None"> <FooterStyle BackColor="Tan" /> <PagerStyle BackColor="PaleGoldenrod" ForeColor="DarkSlateBlue" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" /> <HeaderStyle BackColor="Tan" Font-Bold="True" /> <AlternatingRowStyle BackColor="PaleGoldenrod" /> </asp:GridView><br /> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </center> :‫البرمجة‬ protected void Page_Load(object sender, EventArgs e) { try { Label1.Text = ""; if (!IsPostBack) { //‫ الحظ أننا نستعمل فضاء األسماء الكامل‬.‫انشاء كائن من الخدمة‬ net.webservicex.www.GlobalWeather ws; ws = new net.webservicex.www.GlobalWeather(); // ‫استدعاء الدالة بشكل عادي مع أنها في الحقيقة تنفذ على الخادم الذي‬ // ‫يحتوي على الخدمة‬ string str = ws.GetCitiesByCountry("israel"); // XML ‫النص المُعاد من قبل العملية هو نص بنسق‬ // ‫لذلك نقوم بانشاء ملف مساعد نكتب فيه النص ومن ثم نقوم بعملية ربط‬ // ‫الجدول بالملف‬

324


StreamWriter fp; string strFileName = Server.MapPath(".\\App_Data\\Weather.xml"); str = str.Replace("encoding=\"utf-16\"", ""); fp = File.CreateText(strFileName); fp.WriteLine(str); fp.Close();

DataSet ds1 = new DataSet(); // ReadXml ‫العملية‬ // ds1 ‫تنقل محتوى الملف الى الكائن‬ ds1.ReadXml(strFileName); GridViewCities.DataSource = ds1; GridViewCities.DataBind(); } } catch (Exception ex) { Label1.Text = ex.Message; } }

325


protected void GridViewCities_RowCommand(object sender, GridViewCommandEventArgs e) { if (e.CommandName.Equals("GetWeather")) { GridViewWeather.Visible = true; net.webservicex.www.GlobalWeather ws; ws = new net.webservicex.www.GlobalWeather();

StreamWriter fp; string strFileName = Server.MapPath(".\\App_Data\\Weather.xml");

DataSet ds2 = new DataSet(); string str = ws.GetWeather(e.CommandArgument.ToString(), "Israel"); str = str.Replace("encoding=\"utf-16\"", ""); if (!str.Equals("Data Not Found")) { fp = File.CreateText(strFileName); fp.WriteLine(str); fp.Close(); ds2.ReadXml(strFileName); GridViewWeather.DataSource = ds2; GridViewWeather.DataBind(); } else { GridViewWeather.Visible = false; Label1.Text = str; } } }

326


‫تمارين‬ ‫‪ .1‬استعمل الخدمة ‪ GlobalWeather‬كما تم استعمالها أعاله وأضف إلى الصفحة األداة‬ ‫‪ DropDownList‬التي تمكنك من اختيار الدولة التي تريد معرفة حالة الطقس في مدنها‪ .‬عند اختيار‬ ‫الدولة يجب تعبئة الـ ‪ GridView‬بأسماء المدن في الدولة التي تم اختيارها‪.‬‬ ‫‪ .2‬صمم موق ًعا لتبديل العمالت وفق الخدمة ‪ Currency Convertor‬من الموقع‬

‫‪http://www.webservicex.net/ws/WSDetails.aspx?CATID=2&WSID=10‬‬ ‫‪WSDL Schema Location‬‬ ‫‪http://www.webservicex.net/CurrencyConvertor.asmx?WSDL‬‬

‫‪327‬‬


‫تصميم وكتابة خدمة وب‬ ‫سنقوم اآلن بتصميم وكتابة خدمة وب جديدة باسم ‪ TVChannels‬توفر لمُستخدميها برامج بعض المحطات‬ ‫الفضائية‪.‬‬ ‫الخطوات‬ ‫قم بإنشاء صفحة وب جديدة بالطريقة المعروفة وأعطها االسم ‪WSTVPrograms‬‬ ‫قم بإضافة صفحة جديدة إلى الموقع من نوع ‪ Web Service‬من خالل النقر على الزر األيمن للفارة فوق اسم‬ ‫الموقع في الـ ‪ Solution Explorer‬واختيار‪:Add New Item‬‬

‫‪328‬‬


.‫ هذا الملحق يدل على أن الملف هو ملف خدمة وب‬.aspx ‫ وليس‬asmx ‫الحظ أن ملحق الملف الجديد هو‬ ‫محتوى الملف عبارة عن سطر واحد فيه معلومات عن الخدمة كاسم الفئة التابعة لها واسم ملف البرمجة‬ WebService ‫ وطبعا الموجه‬TVPrograms.cs

<%@ WebService Language="C#" CodeBehind="~/App_Code/TVPrograms.cs" Class="TVPrograms" %> ‫ المُعد لملفات البرمجة الغير‬App_Code ‫ عادي يُحفظ عاد ًة في المجلد الخاص‬C# ‫أما ملف البرمجة فانه ملف‬ :‫ وغيرها‬Dbase ‫تابعة لصفحات النماذج وإنما تحتوي على ملفات الفئات المساعدة مثل‬ ‫فضاء األسماء هذا يحتوي على‬ ‫الفئات المساعدة في برمجة‬ ‫الخدمة مثل الفئة‬ ‫ التي نشتق‬WebService ‫منها فئة الوب التي نبنيها هنا‬

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services;

/// <summary> /// Summary description for TVPrograms /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. // [System.Web.Script.Services.ScriptService] public class TVPrograms : System.Web.Services.WebService { public TVPrograms () // ‫الباني االفتراضي‬ { }

‫كل عملية نريد أن نوفرها ل ُمستخدم الفئة‬ ‫نكتب قبل رأسها النص‬ ‫ هذه‬.‫[ بين القوسين‬WebMethod] ‫ هي فقط مثال‬HelloWorld ‫العملية‬ ‫ كمثال‬Web Developer ‫يضيفه الـ‬

[WebMethod] public string HelloWorld() { return "Hello World"; } }

329


‫الخدمة تستعمل قاعدة بيانات لحفظ البرامج للمحطات الفضائية‪.‬‬ ‫الجداول‪:‬‬

‫والعالقات بين هاذين الجدولين‪:‬‬

‫الدوال التي توفرها الخدمة‬ ‫توفر الخدمة دالتين‬ ‫)(‪DataTable GetChannels‬‬ ‫تعيد هذه العملية قائمة تحتوي على تفاصيل الفضائيات التي توفر الخدمة برامجها‪.‬‬

‫)‪DataTable GetProgramByChannelName(string strChannel‬‬

‫‪330‬‬


‫تعيد هذه العملية قائمة تحتوي على برامج الفضائية الممر اسمها للعملية لكل أيام األسبوع مرتبة حسب وقت البث‬ ‫واليوم األسبوعي‪.‬‬ ‫الحظ أنه يجب إضافة الفئة ‪ Dbase‬إلى الموقع‬ ‫برمجة الدوال‬ ‫الحقيقة أنه ال جديد بالنسبة لبرمجه هاتين الدالتين باستثناء وجوب إعطاء الخاصية ‪ TableName‬التابعة للكائن‬ ‫‪ً dt‬‬ ‫قيمة أي اسمًا حتى يكون باإلمكان القيام بعملية تحويل قيم الكائن إلى كود ‪ XML‬وهو ما يُسمى بعملية الـ‬ ‫‪ Serialization‬وأيضً ا الطريقة العكسية أي تحويل كود الـ ‪ XML‬إلى قيم للكائن وهو ما يُسمى بعملية الـ‬ ‫‪ Deserialization‬وذلك عند استعمال الخدمة من قبل الزبون‪:‬‬ ‫]‪[WebMethod‬‬ ‫)(‪public DataTable GetChannels‬‬ ‫{‬ ‫;)(‪DataTable dt = ClassChannels.GetAll‬‬ ‫;”‪dt.TableName = “Channels‬‬ ‫هنا تتم وبشكل خفي عملية تحويل الكائن الى كود ‪return dt;// XML‬‬ ‫أي عملية الـ ‪// Serialization‬‬ ‫}‬ ‫]‪[WebMethod‬‬ ‫)‪public DataTable GetProgramByChannelName(string strChannel‬‬ ‫{‬ ‫;)‪ClassChannels obj = new ClassChannels(strChannel‬‬ ‫;)(‪DataTable dt = obj.GetProgramByChannelName‬‬ ‫;”‪dt.TableName = “Channels‬‬ ‫;‪return dt‬‬ ‫}‬ ‫الصورة التالية توضح عمليتي الـ ‪ Serialization‬و ‪DeSerialization‬‬

‫‪331‬‬


‫فحص الدالة يكون بتشغيل الموقع واستخدام النماذج التي توفرها بيئة التطوير لكل دالة‪:‬‬

‫النقر على أي ارتباط تشعبي من االرتباطين يتيح للمستخدم استدعاء العملية التي يريد‪ .‬مثال إذا نقرنا على االرتباط‬ ‫‪ GetChannels‬يظهر لنا النموذج التالي‪:‬‬

‫‪332‬‬


:XML ‫ يستدعي الدالة ويعيد النتيجة ككود‬Invoke ‫واآلن النقر على الزر‬ <?xml version="1.0" encoding="utf-8" ?> <DataTable xmlns="http://tempuri.org/"> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Channels" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="Channels"> <xs:complexType> <xs:sequence> <xs:element name="ChannelID" type="xs:int" minOccurs="0" /> <xs:element name="Name" type="xs:string" minOccurs="0" /> <xs:element name="Link" type="xs:string" minOccurs="0" /> <xs:element name="Location" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema>

333

‫هذه هي السكيما‬ ‫التي تصف مبنى‬ ‫وأنماط السجالت‬


<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <DocumentElement xmlns=""> <Channels diffgr:id="Channels1" msdata:rowOrder="0"> <ChannelID>1</ChannelID> <Name>‫<الجزيرة‬/Name> <Link>http://www.Aljazeera.net</Link> <Location>‫<قطر‬/Location> </Channels> <Channels diffgr:id="Channels2" msdata:rowOrder="1"> <ChannelID>2</ChannelID> <Name>BBC ‫<العربية‬/Name> <Link>http://www.bbc.co.uk/arabic</Link> ‫هذه هي السجالت‬ <Location>‫<لندن‬/Location> </Channels> <Channels diffgr:id="Channels3" msdata:rowOrder="2"> <ChannelID>3</ChannelID> <Name>‫<الحوار‬/Name> <Link>http://www.alhiwar.tv</Link> <Location>‫<لندن‬/Location> </Channels> </DocumentElement> </diffgr:diffgram> </DataTable>

‫أما إذا نقرنا على الدالة الثانية فإننا نحصل على النموذج التالي الذي يمكننا من تمرير بارا متر للدالة وهو اسم‬ :‫الفضائية‬

334


‫واآلن النقر على الزر ‪ Invoke‬يستدعي الدالة ويعيد النتيجة ككود ‪:XML‬‬

‫‪335‬‬


‫فعاليات‬ ‫برمج الخدمة ‪TVPrograms‬‬

‫‪336‬‬


‫استعمال الخدمة من قبل زبون‬ ‫سنبني موق ًعا باسم ‪ TVChannelsTester‬الستخدام الخدمة وعرض البرامج بواسطة ‪GridView‬‬ ‫الخطوات الالزمة الستعمال الخدمة هي كالتالي‪:‬‬ ‫أوال وقبل كل شيء يجب تحويل الخدمة إلى تطبيق انترنت وذلك بواسطة الـ ‪ .IIS‬للقيام بذلك ندخل إلى الـ‬ ‫‪ Control Panel‬ومن ثم ندخل إلى الـ ‪Administrative Tools‬‬

‫بعد ذلك ندخل إلى الـ ‪ IIS‬أي إلى الـ ‪Internet Information Services‬‬

‫‪337‬‬


‫ونبحث عن الخدمة في الجهة اليسرى من النافذة‬

‫‪338‬‬


‫تم ننقر على الزر األيمن للفارة على اسم الخدمة ونختار األمر ‪Properties‬‬

‫‪339‬‬


‫هنا نضغط على الزر ‪Create‬‬

‫‪340‬‬


‫ونترك االسم كما هو ونغلق النافذة من خالل النقر على الزر ‪OK‬‬ ‫اآلن نضيف وصف واجهة فئة الخدمة إلى الموقع من خالل النقر على الزر األيمن للفارة على اسم الموقع في الـ‬ ‫‪ Solution Explorer‬واختيار األمر ‪ Add Web Reference‬من القائمة التي تظهر‬ ‫ثم ننقر على االرتباط ‪ Web Services On the local machine‬للبحث عن جميع الخدمات الموجودة على‬ ‫الحاسوب المحلي أي الـ ‪:Localhost‬‬

‫‪341‬‬


‫بعدها ستظهر نافذة وفيها قائمة بأسماء وتفاصيل جميع الخدمات الموجودة على الحاسوب‪:‬‬

‫‪342‬‬


‫ثم ننقر على اسم الخدمة لتظهر النافذة التي تعرض لنا قائمة بأسماء الدوال التابعة للخدمة‪:‬‬

‫‪343‬‬


‫بعد ذلك ننقر على الزر ‪ Add Reference‬لننهي بذلك عملية إضافة واجهة الخدمة إلى الموقع‪ .‬الحظ اآلن أنه‬ ‫تمت إضافة ملفات جديدة إلى الموقع في الـ ‪Solution Explorer‬‬

‫‪344‬‬


‫استعمال الخدمة‬ ‫نريد أن نعرض بواسطة ‪ GridView‬جميع الفضائيات المدعومة من قبل الخدمة على النمط التالي‪:‬‬

‫النقر على االرتباط في العامود ‪ Channel‬يأخذنا إلى موقع الفضائية‪ .‬أما النقر على االرتباط في العامود ‪Show‬‬ ‫‪ Program‬فان ذلك يعرض لنا برنامج الفضائية ألسبوع كامل‬

‫‪345‬‬


‫البرمجة‬ ‫ فعلينا‬.GlobalWeather ‫ال يوجد شيء خاص وجديد هنا فالبرمجة هي نفسها كما قمنا بها عند استعمال الخدمة‬ ‫ للحصول على قائمة‬GetChannels ‫ والقيام باستدعاء العملية‬TVPrograms ‫أن نقوم بإنشاء كائن من الفئة‬ .GridViewChannels ‫الفضائيات ومن ثم عرضها في الجدول‬

protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { localhost.TVPrograms ws = new localhost.TVPrograms(); GridViewChannels.DataSource = ws.GetChannels(); GridViewChannels.DataBind(); } } ‫ فان ذلك يعرض لنا برنامج الفضائية ألسبوع كامل‬Show Program ‫بعد ذلك وكلما نقر المستخدم على االرتباط‬ ‫ للحصول على قائمة البرامج ومن ثم عرضها‬GetProgramByChannelName ‫من خالل استدعاء العملية‬ .GridViewProgram ‫في الجدول‬ protected void GridViewChannels_RowCommand(object sender, GridViewCommandEventArgs e) { if (e.CommandName.Equals("ShowProgram")) { localhost.TVPrograms ws = new localhost.TVPrograms(); string str = e.CommandArgument.ToString(); GridViewProgram.DataSource = ws.GetProgramByChannelName(str)); GridViewProgram.DataBind(); } }

346


‫األمر ‪Update Web/Service References‬‬ ‫ماذا لو تغيرت واجهة الفئة التابعة للخدمة التي نستعملها؟ مثال ماذا على الموقع الذي يستخدم الخدمة أن يفعل إذا‬ ‫أضفنا إلى الفئة عمليات جديدة؟‬ ‫حينها سيكون على مبرمج الموقع الذي يستخدم الخدمة أن نختار األمر ‪Update Web/Service‬‬ ‫‪ References‬من القائمة التي تظهر عندما ننقر على الزر األيمن للفارة على اسم الموقع في الـ ‪Solution‬‬ ‫‪ .Explorer‬هذا األمر يقوم بتحديث الواجهة ومراعاة التغييرات التي حصلت في الخدمة‪.‬‬

‫فعاليات‬ ‫‪ .1‬استعمل الخدمة ‪ TVPrograms‬كما تم استعمالها أعاله‬ ‫‪ .2‬أضف إلى الخدمة ‪ TVPrograms‬دالة جديدة باسم ‪ GetProgramByWeekDay‬تعيد برنامج‬ ‫فضائية ما ليوم ما من أيام األسبوع (األحد‪ ،‬االثنين‪:)... ،‬‬

‫)‪DataTable GetProgramByWeekDay(string strChannel, string strWeekDay‬‬ ‫ثم قم بتصميم صفحة في الموقع الذي يستخدم الخدمة الستخدام هذه الدالة الجديدة‪.‬‬ ‫‪347‬‬

Profile for abushanab_khalil

ASP.NET With C#  

أسس البرمجة بلغة ASP.NET بمساعدة لغة C#

ASP.NET With C#  

أسس البرمجة بلغة ASP.NET بمساعدة لغة C#

Advertisement