أسس البرمجة بلغة VB.NET
Basics of VB.NET
تأليف األستاذ خليل أبو شنب جديدة-المكر 2015 abushanab_khalil@yahoo.com
1
الفهرس مقدمة 4.................................................................................................................. البرمجة بتسويق األحداث 6.......................................................................................... األدوات 8................................................................................................ Controls األداة 9...................................................................................................... Form األداة 17 .............................................................................................. TextBox األداة 19 ...................................................................................................Label األداة 20 ................................................................................................. Button قواعد لغة البرمجة 21 ............................................................................... VB.NET تعريف المتغيرات 21 ............................................................................................. العمليات الحسابية 23 ............................................................................................. عمليات المقارنة المنطقية 23 .................................................................................... عمليات الربط المنطقية 24 ....................................................................................... األمر 24 ......................................................................................................... If حلقة 25 ....................................................................................................... For حلقة 26 ................................................................................................... While المصفوفات األحادية 26 .......................................................................................... المصفوفات الثنائية 28 ............................................................................................ اإلجراءات 29 ...................................................................................................... الدوال 30 ........................................................................................................... الفئات31 ............................................................................................................ البواني 32 .......................................................................................................... الخصائص 33 .................................................................................... Properties معالجات األحداث 34 ................................................................................................. عمليات التأكد من صحة االدخال أو المعلومات 41 ............................................................. تمارين 44 .............................................................................................................. األداة 51 ............................................................................................. ComboBox األداة 57 ..................................................................................... DateTimePicker تمارين 64 .............................................................................................................. األداة 65 ............................................................................................. PictureBox األداة 68 .................................................................................................. ListBox األدوات RadioButtonو 76 .................................................................. CheckBox العملية 85 .............................................................................. MessageBox.Show األداة 91 ................................................................................................ ListView 97 ......................................................................................... MDI Applications األداة 97 ...........................................................................................MenuStrip تمارين 104 ............................................................................................................ 2
االستثناءات 107 ...................................................................................................... 112 ............................................................................................................ SQL األمر 118 ........................................................................................... SELECT تحديد وترتيب النتائج 123 ....................................................................................... ترتيب النتائج 130 ................................................................................................. استخراج معلومات من أكثر من جدول 131 .................................................................. األمر 145 ................................................................................ SELECT INTO اإلدخال149 ........................................................................................................ التحديث 152 ....................................................................................................... الحذف 154 ......................................................................................................... الدوال 156 ......................................................................................................... إنشاء مجموعات في جدول النتيجة 161 ....................................................................... 164 .................................................................................................. ADO.NET 174 ............................................................................... 3-Levels Architecture 179 .............................................................................................. Data Controls األداة 207 .................................................................................. DataGridView تمارين228 ......................................................................................................... األداة 239 ......................................................................................... TabControl الملفات 242 ........................................................................................................... األداة 252 ...................................................................................... RichTextBox تمارين 254 ............................................................................................................
3
مقدمة VB.NETعبارة عن منظومة متقدمة جدا توفرها شركة مايكروسوفت لتطوير تطبيقات النوافذ ) .(Windows Applicationsهذه المنظومة تستند إلى البرمجة موجهة الكائنات (Object ) Oriented Programmingوالبرمجة بتسويق األحداث ).(Events Driven Programming وتدعم VB.NETالتواصل مع قواعد البيانات بأساليب مختلفة من خالل توفير إطار ) (Frameworkكامل من الفئات التي تُسهل عملية التواصل مع قواعد البيانات .الكتاب يبدأ بالتعرف على األدوات األساسية وكيفية استعمالها .ثم يشرح مبدأ البرمجة بتسويق األحداث من خالل أمثلة بسيطة تُعتبر مكونات أساسية في معظم التطبيقات .بعدها نتعرف على كيفية انشاء تطبيقات متعددة النماذج ) (MDI Applicationsونستعمل القوائم لتوفير امكانية للتنقل بين النماذج ال ُمتعددة. ثم نتعلم لغة SQLللتواصل مع قواعد البيانات العالئقية .ثم نشرح تقنية ADO.NETالتي توفر لنا مجموعة فئات للتعامل مع قواعد البيانات العالئقية من داخل تطبيق الـ VB.NETوفق المعمارية ثالثية الطبقات .(3 Levels Architectureثم نتعرف على أهم األدوات التي تتخصص بعرض البيانات وتوفير إمكانيات سهلة لتحريرها وهي األداة .DataGridViewوأخيرا نشرح بشكل ُمبسط كيفية التعامل مع الملفات النصيّة .كل موضوع يم شرحه من خالل أمثلة عملية ومناسبة. يفترض الكتاب أن لدى القارئ معرفة تفصيلية ُمسبقة في المواضيع التالية: • أسس علم الحاسوب بلغة برمجة موجهة كائنات مثل لغة :C#أي أنه يفترض معرفة المواضيع التالية oالمبنى العام للبرنامج oالمتغيرات وأنماطها المختلفة مثل int, float, double, char, string, bool oأوامر الطباعة واالستقبال oأمر ifالبسيط والمركب oالحلقات whileو for oالعمليات الستاتية· oالمصفوفات أحادية وثنائية األبعاد· oالنصوص أي الفئة stringوواجهتها • أسس البرمجة موجهة الكائنات .أي أنه يفترض معرفة المواضيع التالية 4
oالفئات Classes - oالكائنات – Objects oالمؤشرات References - static o oالوراثة Inheritance -وتعدد األشكال Polymorphism - oالواجهات Interfaces - • أسس قواعد البيانات العالئقية – Relational Data Basesمثل MS Access كلي أمل بأن يخدم هذا الكتاب كل من أراد معرفة أسس هذا المنهج المتميز من مناهج برمجة تطبيقات النوافذ.
5
البرمجة بتسويق األحداث Events-Driven Programming البرمجة بتسويق األحداث هي إحدى مناهج البرمجة ) (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عبارة عن نافذة
6
رئيسية تظهر فيها عادة القائمة الرئيسية التي تحتوي على العمليات الرئيسة التي يوفرها البرنامج. وعادة ما تظهر أيضا نوافذ الحوار المختلفة التي تحتوي على ما يسمى باألدوات (Tools, ) Controlsمثل األزرار وصناديق النصوص والقوائم المنسدلة وما شابهها .مثال على هذا النوع من التطبيقات جميع برامج أوفس ) (MS Office Applicationsمثل MS Word, MS .Excel, MS PowerPointأيضا مواقع االنترنت الديناميكية أي التي تحتوي على نماذج ) (Formsوأدوات تعتبر أمثلة على برامج مرئية ) .(Visual Programsتُسمى هذه النوافذ أيضا واجهة المستخدم الرسومية ) .(GUI = Graphical User Interfaceولذلك يُستعمل أحيانا المصطلح GUI Programmingبدال من المصطلح .Visual Programming
مراحل برمجة تطبيقات النوافذ • مرحلة التصميم ) (Design Phaseحيث يتم تصميم النماذج المكونة للتطبيق. • مرحلة كتابة معالجات األحداث ) (Events Handler Programming Phaseوتسمى أيضا Coding Phaseأي مرحلة البرمجة. مهمة مرحلة التصميم تصميم النماذج المختلفة واألدوات التابعة لها .لذلك سنبدأ شرحنا بالحديث عن هذه األدوات.
7
األدوات Controls تُعتبر األدوات المكونات األساسية للنماذج .فاذا ألقينا نظرة على أي نموذج ،نجده مكونا من عدة أنواع من األدوات .تُقسم VB.NETاألدوات من الناحية الوظيفية الى عدة أقسام نجدها في الـ Toolboxوهي:
نذكر هنا بعضها: المجموعة Common
أمثلة
الشرح
… TextBox, Label, Button,
األدوات األساسية
Controls Containers
أدوات لتجميع أو احتواء أدوات أخرى
& Menus
القوائم الرئيسية وأشرطة األدوات
… GroupBox, Panel, TabControl,
… MenuStrip, ToolStrip,
Toolbars Data
… DataGridView,
األدوات المتخصصة بعرض المعلومات على شكل جداول
Dialogs
نوافذ حوار مختلفة :لفتح الملفات
ColorDialog, OpenFileDialog, … SaveFileDialog,
والختيار ألوان ...
8
Reporting
أدوات تدعم انتاج تقارير عن البيانات
ReportViewer
المختلفة. في هذا الفصل سوف نشرح بعضا من هذه األدوات وهي األكثر شيوعًا واستعماال في النماذج .لكن قبل أن نبدأ بالتفاصيل ال بد من التنويه الى أن األدوات عبارة عن كائنات أي Objectsالمعروفة لنا من البرمجة موجهة الكائنات ) .(Object Oriented Programmingهناك كانت للكائنات مجموعة من الخصائص والبواني والعمليات .هنا تُضاف إليهم مجموعة من األحداث (أي )Events تضيف الى الكائن جانبا تفاعليًا مع ال ُمستخدم وتمكنهم من معرفة ما يطلبه المستخدم وتلبية هذه الطلبات .أسهل طريقة إلضافة األداة الى النموذج تكون عن طريق سحبها من الـ Toolbox ووضعها في المكان المناسب في النموذج .بعد ذلك بإمكاننا اعطاء القيم المناسبة للخصائص التي نريد وكتابة معالجات لألحداث التي تهمنا .تظهر نافذة الخصائص التابعة لألداة من خالل الضغط على الزر األيمن للفارة وهي موضوعة على األداة واختيار العنصر Propertiesمنها وذلك في عرض الـ Designللنموذج .نبين ذلك بالنسبة لألدوات التالية:
األداة Form تتكون تطبيقات النوافذ على األقل من نموذج واحد .أي من أداة من نوع .Formهذه األداة معروفة لنا من خالل تعاملنا شبه اليومي مع تطبيقات النوافذ المختلفة .فاذا قمنا بإنشاء مشروع جديد من نوع Windows Forms Applicationنحصل على مشروع فيه نموذج واحد يُعتبر النموذج الرئيسي .أي الذي يظهر مباشرة عند تشغيل التطبيق:
9
كما قلنا فان لكل أداة مجموعة من الخصائص واألحداث الخاصة بها .تظهر نافذة الخصائص التابعة لألداة من خالل الضغط على الزر األيمن للفارة وهي موضوعة على األداة واختيار العنصر Propertiesمنها وذلك في عرض الـ Designللنموذج
بعد اختيار العنصر Propertiesتظهر نافذة الخصائص التالية:
نجد في هذه النافذة عددا كبيرا من الخصائص سنتعرف خالل أمثلة الكتاب على معظمها .لكن يهمنا هنا اثنتان Name :و .Textأما Nameفهو اسم النموذج أي اسم الكائن .وهو عبارة عن نص يميز األداة ويجب أن تتوفر فيه الشروط التي يجب أن تتوفر في كل ال ُمعرّفات ) (Identifiersوهي: 10
• ال يجوز أن يبدأ برقم • ال يجوز أن يحتوي على فراغات • ال يجوز أن يحتوي على رموز محجوزة مثل … +,-, /, %, ;, • ال يجوز أن يكون اس ًما محجو ًزا مثل … class, for, while, من ال ُمفضل اعطاء الكائن اس ًما معبرًا عن وظيفتة وجعل االسم يبدأ بنوع الكائن .مثل FormMain :Text وظيفة هذه الخا ّ صة ،اعطاء عنوان للنموذج يظهر في أعلى النموذج .مسموح للعنوان أن يكون بأي لغة نريد .هنا أعطيت النموذج العنوان My First Windows Form
:BackColor وظيفة هذه الخاصّ ة ،تحديد لون خلفية النموذج .يتم اختيار اللون من القائمة التي تظهر عند النقر على القائمة ال ُمنسدلة التي تظهر الى يمين اسم الخاصية في نافذة الخصائص:
11
الصورة التالية تظهر النموذج بعد تغيير لون الخلفية:
12
منظومة الملفات يُحفظ كل نموذج من النماذج المكونة للتطبيق في 3ملفات: • ملف للتنسيق أي لمبنى النموذج FormName.Designer.vb • وملف للبرمجة FormName.vb • وملف للمصادر. FormName.res FormNameهنا هو اسم النموذج كما تم تحديده من قبل المبرمج. يمكننا رؤية اسم الملف في النافذة Solution Explorerالتي نفتحها من خالل النقر على األمر Solution Explorerفي القائمة Viewالتي تظهر ضمن القائمة الرئيسية:
13
14
أما الملفات نفسها فيمكننا رؤيتها في المجلد الذي يحوي المشروع. تم تخزين المشروع كالتالي
داخل هذا المجلد يوجد مجلد آخر وملف الحل وهو يحمل اسم المشروع هنا VB.NET_Book_Samplesوالملحق slnليصبح االسم الكامل
15
.VB.NET_Book_Samples.slnالنقر على هذا الملف نقرة Double Clickبواسطة الزر األيسر للفارة يؤدي الى فتح المشروع بواسطة Visual Studio
إذا فتحنا المجلد VB.NET_Book_Samplesنرى المحتويات التالية
الجدول التالي يشرح وظيفة المجلدات والملفات التي تظهر في الصورة أعاله .الحظ أننا سنعود للحديث عن بعض هذه المكونات الحقا وبشكل مفصل أكثر:
16
اسم المجلد أو الملف bin My Project obj
App.config FormMain.Designer.vb FormMain.res
FormMain.vb VB.NET_Book_Samples.vbproj
الشرح في هذا المجلد يتم حفظ ملفات التشغيل أي الـ Exe Files في هذا المجلد يتم حفظ ملفات متعلقة بالتنسيقات العامة للمشروع أي Configuration information about the project في هذا المجلد يتم حفظ ملفات متعلقة بنسخة المشروع 32 :أو 64 bit في هذا المجلد يتم حفظ معلومات متعلقة بالتنسيقات العامة للمشروع هذا هو ملف التنسيق التابع للنموذج FormMain هذا هو ملف المصادر التابعة للنموذج .FormMainوالمقصود بالمصادر :الصور واأليقونات والنصوص وكذلك ملفات الصوت والفيديو أو أي نوع آخر من الملفات. في هذا المجلد يتم حفظ الكود مثل معالجات األحداث والفئات وما الى ذلك. في هذا المجلد يتم أيضا حفظ معلومات متعلقة بالتنسيقات العامة للمشروع
األداة TextBox وتُسمى أيضا "صندوق نص" تُمكن المستخدم من ادخال األنواع األساسية من المعلومات مثل األعداد والنصوص. بعد اختيار العنصر Propertiesتظهر نافذة الخصائص التالية:
17
ال بد من مالحظة أن محتوى نافذة الخصائص تتغير بتغير األداة التي تم اختيارها وتحديدها ضا بأن معظم الخصائص ُمشتركة لمعظم األدوات أي أنها متوفرة بنفس األسماء عند بالنموذج .وأي ً معظم األدوات .بالنسبة لألداة TextBoxفان أهم خاصيتين حاليًا هما: Name اسم األداة .وهي عبارة عن نص يميز األداة يجب أن تتوفر فيه الشروط التي يجب أن تتوفر في كل ال ُمعرّفات ) (Identifiersالتي ُذكرت أعاله .هنا أعطيت األداة االسم TextBoxXValue Text وظيفة هذه الخاصّة ،حفظ القيمة الموجودة في األداة .وهي قابلة للقراءة والكتابة. • القراءة تعني أننا نستطيع الحصول على القيمة الموجودة في األداة. مثال XValue = TextBoxXValue.Text • والمقصود بالكتابة أي أننا نستطيع وضع قيمة في األداة. مثال "TextBoxXValue.Text = "100 الجدول التالي يُلخص بعض الخصائص االضافية لألداة :TextBox الخاصيّة Visible
الشرح :trueإلظهار األداة :falseإلخفائها
Enabled
:trueيمكن للمستخدم أن يغير محتوى األداة :falseال يمكن للمستخدم أن يغير محتوى األداة يمكن الحصول على نفس النتيجة من خالل الخاصيّة ReadOnly
Width
عرض األداة
Height
ارتفاع األداة
MaxLengthالعدد األكبر للرموز التي يُمكن ادخالها ForeColor Font
لون النص أي المحتوى اسم الخط
18
الحظ أن معظم هذه الخصائص متوفرة عند معظم األدوات .سيتم شرح باقي الخصائص الحقًا ألنها تتطلب معرفة اضافية.
األداة Label تُمكننا هذه األداة من اظهار معلومات للمستخدم للقراءة فقط وتُستخدم إلظهار عناوين ألدوات أخرى أو إلظهار رسائل معينة لل ُمستخدم .مثال بإمكاننا وضع النص Please enter Value of Xالذي يظهر في المثال السابق الى يسار صندوق النص :TextBoxXValue الصورة التالية تُظهر مجموعة الخصائص التابعة لألداة Labelويمكننا مالحظة ما قيل سابقًا من أن معظم الخصائص التي تم شرحها سابقًا متوفرة عند هذه األداة:
19
األداة Button وتُسمى أيضًا Command Buttonبمعنى زر أمر أي أنها تُستعمل لتوفير امكانية لل ُمستخدم لطلب تنفيذ أوامر مثل .Login, Open, Close, Cancel, Add, Calculate,… :الحظ أن جميع هذه الكلمات مكتوبة بصيغة األمر. نضيف هذه األداة الى مثالنا ليصبح كالتالي:
واآلن سننتقل الى الجزء الثاني مما تملكه هذه األدوات وهي مجموعة األحداث .لكن قبلها سنتحدث عن قواعد لغة البرمجة VB.NETومن ثم البرمجة بتسويق األحداث.
20
VB.NET قواعد لغة البرمجة :VB.NET الجدول التالي يلخص األنواع األساسية التي توفرها لغة المجال Min
النوع
Max
True, False 0
Boolean 255
Byte
UTF-16 character codes from 0 to 65,535
Char
1/1/0001, 12:00:00 A.M.
31/12/9999, 11:59:59 P.M.
Date
-7.9228 x 10^28
7.9228 x 10^28
Decimal
-1.80 x 10^308
1.80 x 10^308
Double
–2,147,483,648
2,147,483,647
Integer
-9,223,372,036,854,775,808 9,223,372,036,854,775,807
Long
–128
127
SByte
–32,768
32,767
Short
–3.402823538
3.402823538
Single
Unicode characters 0 ≥ Len ≥ 2 31 قادر على تخزين نص بطول
String
0
4,294,967,295
UInteger
0
18,446,744,073,709,551,615
ULong
0
65,535
UShort
تعريف المتغيرات القاعدة العامة لتعريف المتغيرات Dim VariableName As DataType = InitialValue
21
• :Dimكلمة محجوزة يجب أن يبدأ فيها أمر التعريف .وظيفة هذا األمر تعريف وحجز ذاكرة لمتغير واحد أو لمجموعة من المتغيرات من نوع واحد معين. • :VariableNameاسم المتغير – يجب أن يحقق الشروط التي ُذكرت آنفا • :Asكلمة محجوزة تأتي قبل النوع مباشرة • :DataTypeالنوع • :InitialValueقيمة أولية للمتغير اعطاء القيمة األولية ليس ملزما وانما هو اختياري.
أمثلة األمر
الشرح تم تعريف متغير باسم Xمن نوع عدد صحيح أي Integer تم تعريف متغير باسم fمن نوع عدد حقيقي أي float
Dim X As Integer Dim f As Single
تم تعريف متغير باسم Counterمن نوع عدد صحيح واعطاؤه القيمة األولية
Dim Counter As Integer = 0
0 يمكننا تعريف أكثر من متغير من نفس النوع في أمر واحد
Dim x, y, z As Integer
مالحظة :لغة VB.NETليست حساسة لحالة الحرف .فال فرق عندها بين متغير اسمه Xوآخر اسمه .x
22
العمليات الحسابية العملية
الشرح الجمع
+
الطرح
-
الضرب
*
قسمة مع باق
/
قسمة بدون باق أي .Divالعملية تعمل فقط مع األنواع الرقمية:
\
Byte, Short, Integer, Long باقي القسمة .العملية تعمل فقط مع األنواع الرقمية:
Mod
Byte, Short, Integer, Long القوة. أمثلة
^ Dim a As Integer = 2 Dim n As Integer = 3 Dim Res As Double Res = a^n
النتيجة تكون دائما من نوع .Doubleهنا قيمة Resتكون 8.0
عمليات المقارنة المنطقية العملية
الشرح هل أكبر؟
>
هل أكبر أو يساوي؟ هل أصغر؟
=> <
هل أصغر أو يساوي؟ هل يساوي؟
=< =
هل ال يساوي؟
><
الحظ أن عملية حفظ قيمة ما في متغير هي أيضا العملية =
23
عمليات الربط المنطقية العملية
الشرح وأيضا
And Or
True يكفي أن يكون أحد الطرفين:أو
Not
النفي
Xor
True فقط إذا كان أحد الطرفين:أو
If األمر المبنى العام If Expression Then Statements End If .False واماTrue تعبير منطقي قيمته النهائية اما:Espression True مجموعة أوامر يجب أن تُنفذ اذا كانت قيمة التعبير:Statements Else معIf أمر If Expression Then Statements Else Statements End If ElseIf معIf أمر If Expression Then Statements ElseIf Expression Then Statements ElseIf Expression Then Statements Else Statements End If
24
الحلقات حلقة For المبنى العام For VariableName = Expression To Expression Statements Next الحظ أن Toتشمل القيمة التي تأتي بعدها. هكذا يتم رفع عداد الحلقة بعد كل دورة فقط بواحد أما إذا أردنا رفع عداد الحلقة بعد كل دورة بأية قيمة نريد نكتب القيمة بعد الكلمة :Step For VariableName = Expression To Expression Step Expression Statements Next مثال المقطع التالي يجمع األعداد الزوجية من 0ولغاية :100 Dim i As Integer Dim Sum As Integer = 0 For i = 0 To 100 Step 2 Sum += i Next ممكن الخروج من الحلقة مبكرا عن طريق األمر .Exit For طبعا لدينا أيضا امكانية لكتابة حلقات متداخلة: Dim i, j, k As Integer = 0 For i = 1 To 10 For j = 1 To 10 For k = 1 To 10 ' ... Next Next Next
25
حلقة While المبنى العام While Expression Statements End While :Expressionتعبير منطقي يمثل شرط الحلقة. مثال المقطع التالي يجمع منازل العدد الصحيح Num Dim Num As Integer = 36259 Dim Sum As Integer = 0 While Num <> 0 Sum += Num Mod 10 Num = Num \ 10 End While ممكن الخروج من الحلقة مبكرا عن طريق األمر .Exit While طبعا لدينا أيضا امكانية لكتابة حلقات Whileمتداخلة كما سيأتي الحقا في األمثلة.
المصفوفات األحادية تعرف مصفوفة أحادية Dim ArrayName(ArrayLength-1) As Type مثال األمر التالي يُعرف مصفوفة أحادية ألعداد صحيحة باسم Aوبطول 5 Dim A(4) As Integer أي أن 4هي رقم الخلية األخيرة في المصفوفة .أرقام الخاليا هي: 0, 1, 2, 3, 4 يمكننا ادخال قيم أولية الى المصفوفة وقت تعريفها: مثال }"Dim A( ) As String = {"First", "Second", "Third", "Fourth", "Fifth
26
الوصول الى الخاليا يكون وفق القاعدة التالية: )ArrayName(CellIndex مثال المقطع التالي يحفظ قيمة الخلية رقم 2التابعة لمصفوفة النصوص Aفي المتغير Str Dim Str As String )Str = A(2 المقطع التالي يجد أكبر قيمة في المصفوفة :Arr
}Dim Arr() As Integer = {2, -1, 0, 10, 1, 8 Dim i As Integer )Dim MaxValue As Integer = Arr(0 For i = 1 To Arr.Length - 1 If MaxValue < Arr(i) Then )MaxValue = Arr(i End If Next الحظ أن طول المصفوفة موجود في الخاصية .Length بإمكاننا أيضا انشاء مصفوفات حجمها يُعرف خالل زمن التشغيل .االنشاء يتم بواسطة األمر .New المقطع التالي مثال يقوم بإنشاء مصفوفة أحادية بحجم 5وادخال قيم اليها: Dim Size As Integer = 5 Dim B() As Integer }{ )B = New Integer(Size-1 B(0) = 2 B(1) = 4 B(2) = 6 B(4) = 8
27
المصفوفات الثنائية تعريف مصفوفة ثنائية Dim ArrayName(NumRows-1,NumCols-1) As Type مثال األمر التالي يُعرف مصفوفة ثنائية ألعداد صحيحة باسم Mوبطول 5X5 Dim M(4,4) As Integer يمكننا ادخال قيم أولية الى المصفوفة وقت تعريفها .المقطع التالي يُعرف ويعبئ مصفوفة ثنائية بحجم 3X2 }}Dim Matrix(,) As Integer = {{1, 2}, {3, 4}, {5, 6 امكانية أخرى Dim Matrix(2,1) As Integer Matrix(0, 0) = 1 Matrix(0, 1) = 2 Matrix(1, 0) = 3 Matrix(1, 1) = 4 Matrix(2, 0) = 5 Matrix(2, 1) = 6 المقطع التالي يجمع قيم المصفوفة الثنائية :Matrix
}}Dim Matrix(,) As Integer = {{1, 2}, {3, 4}, {5, 6 Dim i, j As Integer Dim Sum As Integer = 0 )For i = 0 To Matrix.GetUpperBound(0 )For j = 0 To Matrix.GetUpperBound(1 )Sum += Matrix(i, j Next Next العملية GetUpperBoundتتلقى البعد ( 0للبعد األول أي لألسطر و 1للبعد الثاني أي لألعمدة).
28
اإلجراءات المبنى العام لإلجراءات: ) Accessibility Sub RoutineName ( ParamatersList Statements End Sub • :Accessibilityمستوى الحماية كما هو معروف من البرمجة موجهة الكائنات .ممكن أن تكون واحدة من األنواع التاليةPublic, Private, Protected,… : • :Subكلمة محجوزة تفيد أننا نكتب اجراء .وهي مأخوذة من الكلمة Subroutine • :RoutineNameاسم اإلجراء • :ParamatersListقائمة البارمترات. • :End Subنهاية اإلجراء. مبنى قائمة البارمترات القائمة عبارة عن سلسلة من األجزاء التي تفصل بينها الفاصلة .لكل بارامتر يوجد جزء مبني كالتالي: PassingWay ParameterName As ParameterType • :PassingWayنوع تمرير البارمترات وهي اما :ByVal oبحسبها يتم تمرير Valuesأي قيم فقط .هذا النوع من البارمترات يحمل قيما من المستدعي الى اإلجراء فقط .القيم تبقى عند المستدعي بعد انتهاء االستدعاء كما كانت قبله أي ال تتغير. :ByRef oبحسبها يتم تمرير Referencesأي مؤشرات .هذا النوع من البارمترات يحمل قيما من المستدعي الى اإلجراء والعكس .أي أن القيم قد تتغير عند المستدعي بعد انتهاء االستدعاء. • :ParameterNameاسم البارمتر. • :ParameterTypeنوع البارمتر.
29
الخروج من االجراء مبكرا يكون بواسطة األمر .Exit Sub مثال االجراء التالي يعرض العملية المشهورة في علم الحاسوب وهو يتلقى عددين صحيحين aو bثم يقوم بتبديل قيمهم: )Public Sub Swap(ByRef a As Integer, ByRef b As Integer Dim Temp As Integer Temp = a a=b b = Temp End Sub
الدوال المبنى العام للدوال: Accessibility Function FunctionName ( ParamatersList ) As RetType Statements End Function • :Accessibilityمستوى الحماية كما هو مشروح عند اإلجراءات • :Functionكلمة محجوزة تفيد أننا نكتب دالة. • :FunctionNameاسم الدالة • :ParamatersListقائمة البارمترات كما هي مشروحة عند اإلجراءات • :RetTypeنوع القيمة ال ُمرجعة من قبل الدالة .الدالة تعيد القيمة بمساعدة األمرة .Return • :End Functionنهاية الدالة. الخروج من الدالة مبكرا يكون بواسطة األمر .Exit Sub مثال الدالة التالية تتلقى عددا طبيعيا Numوتعيد مجموع منازله: Public Function GetSumOfDigits(ByVal Num As Integer) As Integer Dim Sum As Integer = 0 While Num <> 0 Sum += Num Mod 10 Num = Num \ 10 End While Return Sum End Function
30
مثال :False خالف ذلك تعيد. إذا كان أولياTrue وتعيدNum الدالة التالية تتلقى عددا طبيعيا Public Function IsPrime(ByVal Num As Integer) As Boolean Dim i As Integer For i = 2 To (Num - 1) If Num Mod i = 0 Then Return False End If Next Return True End Function
الفئات سأشرح هنا الفئات بشكل عام ومن خالل أمثلة فقط ألنها ال تختلف كثيرا عن التفاصيل الموجودة في C# كتاب اسس البرمجة موجهة الكائنات بلغة :المبنى العام للفئات public Class ClassName { // تعريف مجموعة من الخصائص أو الصفات // برمجة مجموعة من العمليات }
مثال Public Class Employee Public EmployeeNumber As Integer Public FamilyName As String Public GivenName As String Public DateOfBirth As Date Public Salary As Decimal Public Function Format( ) As String Return GivenName & " " & FamilyName End Function End Class
31
Employee المقطع التالي يستعمل الفئة Dim emp As New Employee( ) emp.EmployeeNumber = 10 emp.FamilyName = "Abu Sami" emp.GivenName = "Sami" emp.DateOfBirth = #1/28/1965# emp.Salary = 11000
emp امكانية أخرى إلنشاء الكائن Dim emp As Employee = New Employee() وهنا تم استعمال الباني االفتراضي الذي وفره.New الحظ أن انشاء الكائنات يكون بواسطة األمر .لنا المترجم كما هو معروف من اسس البرمجة موجهة الكائنات
البواني األمثلة.New ال تأخذ نفس اسم الفئة وانما اسمها يكون الكلمة المحجوزةVB.NET البواني بلغة :التالية تبين ذلك Public Sub New() 'Default Constructor End Sub Public Sub New(ByVal EmployeeNumber As Integer) Me.EmployeeNumber = EmployeeNumber End Sub Public Sub New(ByVal EmployeeNumber As Integer, ByVal FamilyName As String, ByVal GivenName As String) Me.EmployeeNumber = EmployeeNumber Me.FamilyName = FamilyName Me.GivenName = GivenName End Sub
32
في لغاتthis التي تقابل الكلمة المحجوزةMe الحظ أننا نرمز الى الكائن الحالي بالكلمة المحجوزة .C# أخرى مثل
Properties الخصائص تعريف الخصائص يكون على النحو التالي Public Property EmployeeNumber() As String Get Return m_EmployeeNumber End Get Set(ByVal value As String) m_EmployeeNumber = value End Set End Property
33
معالجات األحداث سنبدأ اآلن بشرح جانب البرمجة من خالل األمثلة. مثال المطلوب تصميم وبرمجة آلة حاسبة بسيطة قادرة على جمع وضرب وطرح عددين حقيقيين. التصميم المقترح هو التالي:
يظهر النموذج بأن لدينا 9أدوات أو .Controlsهذه األدوات هي: األداة
االسم أو الـ ID
Label
LabelResult
Label
LabelXValue
Label
LabelYValue
الشرح تستعمل هذه األداة إلظهار نصوص ثابتة كعناوين ألدوات أخرى .هنا سنستعملها إلظهار نتيجة الحساب. هنا استعملناها لتوجيه المستخدم بأن يدخل في صندوق النص الذي إلى يمين األداة القيمة األولى ال ُمراد جمعها. توجه المستخدم بأن يدخل في صندوق النص الذي إلى يمين األداة القيمة الثانية ال ُمراد جمعها.
34
TextBox
TextBoxValue1
TextBox
TextBoxValue2
Button
ButtonAdd
Button
ButtonSub
Button
ButtonMul
Button
ButtonClose
صندوق النص الذي سيستوعب القيمة األولى صندوق النص الذي سيستوعب القيمة الثانية الزر الذي سيكون على المستخدم أن ينقره نقرة واحدة بالزر األيسر للفارة وذلك بعد أن يُزود القيم الالزمة لتنفيذ عملية الجمع وإظهار النتيجة. الزر الذي سيكون على المستخدم أن ينقره نقرة واحدة بالزر األيسر للفارة وذلك بعد أن يُزود القيم الالزمة لتنفيذ عملية الطرح وإظهار النتيجة. الزر الذي سيكون على المستخدم أن ينقره نقرة واحدة بالزر األيسر للفارة وذلك بعد أن يُزود القيم الالزمة لتنفيذ عملية الضرب وإظهار النتيجة. الزر الذي سيكون على المستخدم أن ينقره نقرة واحدة بالزر األيسر للفارة إلغالق النموذج.
البرمجة إذا شغلنا هذا النموذج فسوف يظهر بالضبط كما نريده وبإمكاننا أن نستعمل صناديق النصوص لكتابة األعداد لكن إذا نقرنا بالزر األيسر للفارة على احد األزرار )… (ADD, SUB,فان شيئًا لن يحدث .لماذا؟ اإلجابة بسيطة ألننا لم نكتب معالجًا لهذه األحداث .أي لم نكتب االجراء الذي سيأخذ القيم من صناديق النصوص ثم يُنفذ العملية المطلوبة ويضع النتيجة في األداة .LabelResult
35
كتابة معالجات األحداث يوجد لكل أداة من األدوات المتوفرة في القائمة التي يعرضها الـ Toolboxمجموعة من األحداث التي تدعمها هذه األدوات .عند التعرف على أية أداة سيكون التعرف على األحداث التي تدعمها األداة جزءا من عملية دراسة األداة ودراسة إمكانياتها وخصائصها .لمعرفة مجموعة األحداث ألية أداة نختار األداة في عرض الـ Designومن ثم نحضر نافذة الخصائص كما بينا سابقا من خالل النقر على الزر األيمن للفارة واختيار Propertiesمن القائمة التي تظهر (احرص على وضع الفارة على األداة حين النقر على الزر األيمن للفارة) . حينها ستظهر(عادة) في الجهة اليمنى من الـ Visual Studioنافذة الخصائص التي تحتوي على جميع خصائص األداة بما في ذلك أيضا األحداث .الوصول إلى قائمة األحداث يكون من خالل النقر على الزر
الموجود أعلى نافذة الخصائص
النقر على زر األحداث يظهر قائمة األحداث التالية
36
37
انتبه إلى أن محتوى القائمة سيكون مختلفا حسب األداة التي نختارها في عرض الـ !Designالحظ أيضا المالحظة الموجودة في نهاية نافذة األحداث التي تشرح متى يحصل الحدث ال ُمختار. لكتابة معالج لحدث ما أُكتب اسما في الصندوق الذي إلى يمين اسم الحدث ثم أضغط .Enterانتبه إلى أن االسم الذي تكتبه هنا هو اسم إلجراء أي لعملية ولذلك عليه أن يكون محققا لشروط تسمية ال ُمعرفات ) .(Identifiers Naming Rulesمثال OnClickAddفقد جرت العادة عند مبرمجي الـ GUIsأن يبدأ اسم معالج الحدث بالنص Onللداللة على أن هذا هو المقطع الذي يستدعى كردة فعل على الحدث ) .(Response On an Eventفي االسم يظهر أيضا اسم الحدث Clickواسم يدل على الزر .طبعا هذه الطريقة للتسمية ليست ملزمة وإنما هي إحدى اإلمكانيات للتسمية .بعد إعطاء االسم والضغط على Enterسينقُلُك Visual Studioإلى النافذة التي ستكتب فيها الكود أي برمجة جميع معالجات األحداث وجميع الفئات والعمليات التابعة لنموذج معين. Public Class FormCalc Private Sub OnClickAdd(sender As System.Object, e As System.EventArgs) Handles ButtonAdd.Click End Sub End Class
انتبه إلى الجزء Handles ButtonAdd.Clickالموجود في نهاية رأس اإلجراء والذي يعني حرفيا: يعالج الحدث Clickالتابع لألداة .ButtonAdd في األمثلة المتقدمة التي ستأتي الحقا سوف نتعرف أكثر على قائمة البارامترات ووظيفة كل بارامتر. هنالك إمكانية ثانية لكتابة معالج الحدث بدون استعمال نافذة األحداث وذلك من خالل النقر مرتين بالفارة Double Clickعلى األداة التي نريد أن نكتب لها معالج حدث وذلك في عرض الـ .Designحينها سيكتب Visual Studioمعالجا لما يُسمى بالحدث االفتراضي لألداة (Default ) Eventوهو يختلف من أداة إلى أُخرى .فمثال الحدث االفتراضي لألداة Buttonهو الحدث Clickالذي يحصل عندما ينقر المستخدم بالزر األيسر للفارة على األداة .بينما الحدث االفتراضي لألداة TextBoxهو الحدث TextChangedالذي يحصل عندما يتغير محتوى صندوق النص.
38
يجب أن تالحظ أيضا أن االسم الذي يعطيه Visual Studioللمعالج عند استعمال هذه اإلمكانية هو كالتالي :فقد سار Visual Studioفي التسمية حسب القاعدة "اسم الحدث_اسم األداة". Private Sub ButtonAdd_Click(sender As System.Object, e As System.EventArgs) Handles ButtonAdd.Click End Sub
ننتقل اآلن إلى برمجة معالج الحدث ButtonAdd_Clickبشكل كامل .األلغوريثم التالي يبين ال ُخطُوات الالزمة: ُ .1خذ النص الموجود في الصندوق TextBoxXValueوحوله إلى عدد حقيقي وأحفظ العدد في المتغير Val1 ُ .2خذ النص الموجود في الصندوق TextBoxYValueوحوله إلى عدد حقيقي وأحفظ العدد في المتغير Val2 .3أحفظ القيمة Val1 + Val2في المتغير Result .4حول قيمة المتغير Resultإلى نص بواسطة الدالة )( ToStringوأعطه كقيمة إلى الخاصية Textالتابعة لألداة LabelResultوصلها بجملة توضيح مناسبة مثل The Result is في لغة VB.NETتكون البرمجة كالتالي: Private Sub ButtonAdd_Click(sender As System.Object, e As System.EventArgs) Handles ButtonAdd.Click Dim Val1, Val2 As Double )Val1 = Integer.Parse(TextBoxXValue.Text )Val2 = Integer.Parse(TextBoxYValue.Text Dim Result As Double Result = Val1 + Val2 LabelResult.Text = "The Result is:" + Result.ToString End Sub
الحظ أنه حين استدعاء اجراء ال يتلقى بارامترات مثل ToStringفال حاجة لكتابة األقواس الدائرية. 39
اآلن إذا شغلنا التطبيق وأدخلنا القيم التي نريد ثم نقرنا الزر ADDستظهر النتيجة كالتالي:
40
عمليات التأكد من صحة االدخال أو المعلومات ماذا لو أدخل ال ُمستخدم في المثال السابق قيما ليست أعدادا؟ كيف سيكون بإمكاننا في هذه الحالة التأكد من صحة االدخال؟ أي كيف سنفحص مثال أن ال ُمستخدم حقًا أدخل عددا أو عددا حقيقيًا وما الى ذلك. توفر VB.NETمجموعة من العمليات العامة لفحص مبنى المعلومات األساسية نلخصها فيما يلي: العملية IsNumeric Public Function IsNumeric(ByVal Expression As Object) As Boolean ادعاء الدخول
تتلقى تعبيرا قد يكون نصا أو أي شيء مشتق من الفئة Object
ادعاء الخروج
تعيد Trueإذا تبين أن التعبير عبارة عن عدد صحيح أو حقيقي. خالف ذلك تعيد .Falseالعملية تعيد أيضا Falseاذا كان البارمتر عبارة عن نص فارغ.
أمثلة Dim obj As Object Dim CheckResult As Boolean "obj = "53 ' The following call to IsNumeric returns True. )CheckResult = IsNumeric(obj "obj = "459.95 ' The following call to IsNumeric returns True. )CheckResult = IsNumeric(obj "obj = "45 Help ' The following call to IsNumeric returns False. )CheckResult = IsNumeric(obj
41
في التطبيق السابق ليكون أكثر أمانًا ويقبل فقطADD واآلن بامكاننا اعادة كتابة معالج الحدث للزر :أعدادا في صناديق النص Private Sub ButtonAdd_Click(sender As System.Object, e As System.EventArgs) Handles ButtonAdd.Click If Not IsNumeric(TextBoxXValue.Text) Then LabelResult.Text = "Error: Value of X is not a number" Exit Sub End If If Not IsNumeric(TextBoxXValue.Text) Then LabelResult.Text = "Error: Value of Y is not a number" Exit Sub End If Dim Val1, Val2 As Double Val1 = Integer.Parse(TextBoxXValue.Text) Val2 = Integer.Parse(TextBoxYValue.Text) Dim Result As Double Result = Val1 + Val2 LabelResult.Text = "The Result is:" + Result.ToString() End Sub
IsDate العملية Public Function IsDate(ByVal Expression As Object) As Boolean تتلقى تعبيرا قد يكون نصا أو أي شيء مشتق من الفئة
ادعاء الدخول
Object إذا تبين أن التعبير عبارة عنTrue تعيد تاريخ صالح
•
أو وقت صالح
•
أو تاريخ ووقت صالحين
•
ادعاء الخروج
.False خالف ذلك تعيد أمثلة Dim firstDate, secondDate As Date Dim timeOnly, dateAndTime, noDate As String Dim CheckResult As Boolean ' CDate converts the passed parameter to a Date object firstDate = CDate("February 12, 1969") secondDate = #2/12/1969#
42
timeOnly = "3:45 PM" dateAndTime = "March 15, 1981 10:22 AM" noDate = "Hello" ' The following calls to IsDate return True. CheckResult = IsDate(firstDate) CheckResult = IsDate(secondDate) CheckResult = IsDate(timeOnly) CheckResult = IsDate (dateAndTime) ' The following call to IsDate returns False. CheckResult = IsDate (noDate)
IsNothing العملية Public Function IsNothing(ByVal Expression As Object) As Boolean تتلقى تعبيرا قد يكون نصا أو أي شيء مشتق من الفئة
ادعاء الدخول
Object تُمثل.Nothing إذا تبين أن التعبير عبارةTrue تعيد
ادعاء الخروج
المعروفة في باقيnull هذه الكلمة المحجوزة القيمة وهي قيمة المؤشر أو الكائن عندما يكون غير.اللغات .صالح لالستعمال .False خالف ذلك تعيد أمثلة Dim obj As Object ' No instance has been assigned to variable obj yet. Dim CheckResult As Boolean ' The following call returns True. CheckResult = IsNothing(obj) ' Assign a string instance to variable obj. obj = "ABCDEF" ' The following call returns False. CheckResult = IsNothing(obj) ' Disassociate variable obj from any instance. obj = Nothing ' The following call returns True. CheckResult = IsNothing(obj)
43
تمارين تمرين رقم 1 أُكتب معالج حدث للحدث Clickللزر Closeفي تطبيق اآللة الحاسبة الموجود أعاله .على معالج الحدث أن يغلق النموذج وينهي التطبيق وذلك من خالل األمر )(Me.Close
تمرين رقم 2 أُكتب معالج حدث للحدث Clickلألزرار SUBو MULفي تطبيق اآللة الحاسبة الموجود أعاله. المعالجان يعمالن بالضبط مثل معالج الزر ADDلكنهما يقومان بالطرح والضرب بدال من الجمع. تمرين رقم 3 مؤشر كتلة الجسم (باإلنجليزية )Body mass index :هو أفضل مقياس متعارف عليه عالميا لتمييز الوزن الزائد عن السمنة أو البدانة عن النحافة عن الوزن المثالي ،وهو يعبر عن العالقة بين وزن الشخص وطوله .يُحسب مؤشر كتلة الجسم بتقسيم الوزن بالكيلوجرام على مربع الطول بالمتر كما يلي :مؤشر كتلة الجسم = الوزن بالكيلوجرام /مربع الطول بالمتر صمم التطبيق التالي لحساب مؤشر كتلة الجسم .الحظ أننا نستعمل هنا األداة .GroupBoxالغرض من استخدام هذه األداة هو تنظيم وضع األدوات التي لها نفس الهدف على النموذج (باإلضافة الى أغراض أخرى سنتعرف عليها الحقًا) .هذه األداة عبارة عن شكل رباعي ( ُمربع أو مستطيل) له اسم خاص به يُحدد بواسطة الخاصية Textالتابعة لها .نضع بداخلها األدوات التي لها نفس الهدف أو الوظيفة .يُمكننا الحصول على هذه األداة من الـ Toolboxمن المجموعة Containersكما تُبين الصورة التالية:
44
النموذج:
يظهر النموذج بأن لدينا مجموعة كبيرة من األدوات أو .Controlsهذه األدوات هي: األداة GroupBox
االسم أو الـ ID GroupBoxData
الشرح تُظهر النص: Data
45
تُظهر النص:
Label
LabelHeight
TextBox
TextBoxHeight
إلدخال طول الشخص بالمتر
Label
LabelWeight
تُظهر النص:
TextBox
TextBoxWeight
Button
ButtonCalcBMI
GroupBox
GroupBoxResults
Label
LabelBMI
تُظهر النص:
TextBox
TextBoxBMI
إلظهار قيمة الـ BMIالتي تم
Label
LabelCategory
TextBox
TextBoxCategory
(Your Height (in m
)Your Weight (in Kg إلدخال وزن الشخص بالكيلوغرام الزر الذي سيكون على المستخدم أن ينقره نقرة واحدة بالزر األيسر للفارة لحساب الـ .BMI تُظهر النص: Results
Your BMI is حسابها .أعط الخاصية ReadOnlyالتابعة لهذه األداة القيمة Trueحتى تكون للقراءة فقط وذلك ألن الذي سيدخل القيمة هو التطبيق وليس ال ُمستخدم. تُظهر النص: Your BMI Category is إلظهار تصنيف قيمة الـ BMIالتي تم حسابها .أعط الخاصية ReadOnlyالتابعة
46
لهذه األداة القيمة Trueحتى تكون للقراءة فقط وذلك ألن الذي سيدخل النص هو التطبيق وليس ال ُمستخدم. ال ُمستخدم يُدخل القيم الالزمة لحساب الـ ( BMIالطول والوزن) الى صناديق النص TextBoxHeightو .TextBoxWeightثم يضغط على الزر .Calculate BMIبعد ذلك يقوم التطبيق بحساب قيمة الـ BMIويُدخل النتيجة الى صندوق النص TextBoxBMIوالتصنيف الى صندوق النص .TextBoxCategoryيُلخص الجدول التالي تصنيف نتيجة الـ BMIبحسب مجال القيمة التي تم حسابها: Category
BMI range – kg/m2 less than 15نقص حاد جدا
Very severely underweight
Severely underweight Underweight )Normal (healthy weight
بالعربية
from 15.0 to 16.0نقص حاد from 16.0 to 18.5نقص في الوزن from 18.5 to 25وزن طبيعي from 25 to 30زيادة في الوزن
Overweight Obese Class I (Moderately )obese
from 30 to 35سمنة خفيفة (سمنة من الدرجة األولى)
Obese Class II (Severely )obese
from 35 to 40سمنة متوسطة (سمنة من الدرجة الثانية)
Obese Class III (Very severely )obese
over 40سمنة مفرطة (سمنة من الدرجة الثالثة)
47
يجب فحص ال ُمعطيات ال ُمدخلة الى صناديق النصوص بواسطة العملية IsNumericقبل البدء بالحسابات.
تمرين رقم 4 برمج نموذج حساب الـ BMIبحيث يظهر محتواه باللغة العربية
توفر األداة Formخاصيتين لدعم اللغة العربية واللغة العبرية: :RightToLeftاما أن تكون Yesأو .Noيجب اختيار Yesإلظهار النموذج وجميع األدوات من اليمين الى اليسار.
48
:RightToLeftLayoutاما أن تكون Trueأو .Falseيجب اختيار Trueإلظهار أزرار النموذج
في الجهة اليسرى العليا للنموذج.
49
تمرين رقم 5 صمم وبرمج النموذج التالي لحل المعادالت التربيعية
على ال ُمستخدم أن يُدخل معامالت المعادلة التربيعية a,b,cفي صناديق النصوص من اليسار الى اليمين .واظهار النتيجة بواسطة أداة Labelمناسبة التي تظهر في الـ GroupBoxأسفل النموذج وذلك بعد أن يضغط ال ُمستخدم على الزر !.Solve It يجب التأكد من صحة االدخال أي أن ال ُمستخدم أدخل أعدادا حقيقية للمعامالت وذلك بواسطة استعمال العملية .IsNumericفي حال وجود خطأ يجب اظهار ذلك بواسطة األداة Labelبحيث يكون لون الخط أحمرا. الحظ أن المعادلة قد • ال يكون لها حلول حقيقية (مثال b2 – 4ac < 0 :أو )a = 0, b = 0, c ≠ 0 • يكون لها عدد ال نهائي من الحلول ).(a = 0, b = 0, c = 0 • يكون لها حل واحد
50
األداة ComboBox تُسمى هذه األداة أيضا بالقائمة ال ُمنسدلة وهي تُشكل امكانية لعرض مجموعة من المعلومات (مثل األعداد أو النصوص) بحيث يكون بإمكان ال ُمستخدم اختيار واحد منها أو أكثر .القائمة ال تُظهر محتواها ُدفعة واحدة وانما تُظهر عنصرا واحدا فقط وبإمكان ال ُمستخدم أن يفتح القائمة ليرى كل محتواها مع امكانية المرور على محتوى القائمة بواسطة شريط المرور أي الـ .Scrolling bar شكل قائمة األشهر وهي ُمغلقة (أي تظهر شهرا واحدا فقط)
شكل قائمة األشهر بعد أن فُتحت من خالل النقر على السهم الظاهر في جهتها اليمنى:
الجدول التالي يُلخص بعض الخصائص ال ُمميزة لألداة ComboBoxوالتي سنستعملها في األمثلة. هناك مجموعة اضافية سنشرحها الحقا عند الحديث عنها. الخاصيّة Items
الشرح عبارة عن سلسلة أي Listمن العناصر التي تشكل محتوى القائمة .وهي بودرها توفر عدة عمليات منها Addإلضافة عنصر الى القائمة والعملية Clearلتفريغ القائمة من محتواها والعملية Containsلمعرفة هل عنصر ما موجود داخل القائمة وهكذا.
Text
النص ال ُمختار حاليًا
SelectedText
النص ال ُمختار حاليًا
SelectedIndexاندكس أي رقم النص ال ُمختار حاليًا في القائمة
51
مثال المطلوب تصميم وبرمجة إمكانية تمكن المستخدم من اختيار تاريخ ما .إحدى المشاكل التي نواجهها عندما نطلب من المستخدم تاريخا ما (مثال تاريخ ميالد المستخدم) هي فحص هل القيمة أو القيم التي أُدخلت تمثل حقًا تاري ًخا صحي ًحا .الحل األمثل لمثل هذه المهام إعطاء المستخدم إمكانية االختيار بدال من إمكانية الكتابة .هذا الحل يُعتبر أيضا لطيفا مع المستخدم أي .User-friendlyفاالختيار أسهل وأسرع من الكتابة ويحول دون حصول أخطاء .سوف نستعمل هنا أداة القائمة المنسدلة ComboBoxثالث مرات على النحو وبالتنسيق التالي:
األداة
االسم أو الـ ID
الشرح
ComboBox
ComboBoxDay
تستعمل هذه األداة إلظهار األيام
ComboBox
ComboBoxMonth
الممكنة وذلك وفقا للسنة والشهر المختارين .مثال إذا كانت السنة المختارة كبيسة والشهر المختار شباط فسيكون مجال األيام من 1 إلى .29خالف ذلك ستحتوي هذه القائمة بالنسبة لشهر شباط فقط 28 يوما .كذلك األمر بالنسبة لألشهر التي فيها 30يوما وتلك التي فيها 31يوما. تستعمل هذه األداة إلظهار األشهر من 1إلى 12
52
ComboBox
ComboBoxYear
تستعمل هذه األداة إلظهار السنين من المجال الذي نريده.
من الواضح أن أكثر قائمة من بين القوائم الثالث تحتاج إلى حرص ودقة في البرمجة هي قائمة األيام أي .ComboBoxDayمحتوى باقي القوائم ثابت بينما محتوى هذه القائمة متعلق بالسنة والشهر المختارين .متى سنمأل هذه القائمة بالقيم الممكنة؟ الجواب • عندما يُستدعى النموذج ألول مرة .هنا علينا برمجة معالج الحدث Loadالخاص بالنموذج .هذا الحدث يحصل قبيل ظهور النموذج لل ُمستخدم .لذلك بإمكاننا برمجة معالج له لنقوم بإجراء بعض التغييرات المطلوبة على النموذج .سنقوم بإدخال السنوات الى القائمة ComboBoxYear وأيضا األشهر الى القائمة ComboBoxMonthبعد ذلك سنأخذ التاريخ الحالي ومن ثم نقوم باختيار أجزائه المختلفة (اليوم والشهر والسنة) في القوائم الثالث وادخال األيام الممكنة بعد معرفة السنة والشهر الى القائمة .ComboBoxDay • في كل مرة يتم فيها تغيير السنة أو الشهر من قبل المستخدم .السؤال هنا هو كيف نعرف (أي كيف يعرف النموذج) أن المستخدم غير اختيار السنة أو الشهر؟ هذا يكون من خالل الحدث ( SelectedIndexChangedأو الحدث (SelectedTextChangedالذي تُحدثه القائمة المنسدلة .هذا الحدث هو أيضا الحدث االفتراضي للقائمة المنسدلة. البرمجة برمجة معالج الحدث Loadالخاص بالنموذج FormSelectData Private Sub FormSelectData_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load اجراء مساعد لتعبئة قائمة األشهر' ) (FillMonthsCB اجراء مساعد لتعبئة قائمة السنين ' ) (FillYearsCB نُعرف متغيرا من نوع تاريخ ' Dim ToDay As Date ToDay = Date.Now نأخذ تاريخ اليوم ' نختار السنة الحالية من قائمة السنين ' ComboBoxYear.SelectedText = ToDay.Year.ToString نختار الشهر الحالي من قائمة األشهر ' ComboBoxMonth.SelectedText = ToDay.Month.ToString اجراء مساعد لتعبئة قائمة األيام ' )(FillDaysCB نختار اليوم الحالي من قائمة األيام ' ComboBoxDay.SelectedText = ToDay.Day.ToString End Sub
53
العمليات المساعدة اجراء مساعد لتعبئة قائمة األشهر Private Sub FillMonthsCB() ComboBoxMonth.Items.Clear() ' نقوم بتفريغ القائمة حتى ال تظهر القيم أكثر من مرة Dim i As Integer For i = 1 To 12 ComboBoxMonth.Items.Add(i.ToString()) Next End Sub
اجراء مساعد لتعبئة قائمة السنين Private Sub FillYearsCB() ComboBoxYear.Items.Clear() Dim i As Integer Dim ToDay As Date ToDay = Date.Now For i = 1965 To ToDay.Year ComboBoxYear.Items.Add(i.ToString()) Next End Sub
اجراء مساعد لتعبئة قائمة األيام Private Sub FillDaysCB() Dim Month As Integer ' نأخذ الشهر ال ُمختار من قائمة األشهر Month = Integer.Parse(ComboBoxMonth.Text.ToString()) Dim Year As Integer ' نأخذ السنة ال ُمختارة من قائمة السنين Year = Integer.Parse(ComboBoxYear.Text.ToString()) Dim Days As Integer ' نحصل على عدد األيام في الشهر بمساعدة دالة مساعدة Days = GetNumOfDays(Month, Year) ComboBoxDay.Items.Clear() Dim i As Integer For i = 1 To Days ComboBoxDay.Items.Add(i.ToString()) Next End Sub
دالة مساعدة تتلقى الشهر والسنة وتعيد عدد األيام في الشهر Private Function GetNumOfDays(ByVal month As Integer, ByVal year As Integer) As Integer If month = 1 Or month = 3 Or month = 5 Or month = 7 Or month = 8 Then Return 31 End If If month = 2 Then 54
If IsLeapYear(year) Then Return 29 Else Return 28 End If End If If month = 4 Or month = 6 Or month = 9 Or month = 11 Then Return 30 End If If month = 10 Or month = 12 Then Return 31 End If Return 0 End Function
False خالف ذلك تعيد. إذا كانت سنة كبيسةTrue دالة مساعدة تتلقى سنة وتعيد
Private Function IsLeapYear(ByVal year As Integer) As Boolean Return (((year Mod 4 = 0) And (year Mod 100 <> 0)) Or (year Mod 400 = 0)) End Function
معالجات األحداث التي تحصل عندما يغير ال ُمستخدم اختياره في احدى القوائم – لكل قائمة معالج :خاص بها Private Sub ComboBoxMonth_SelectedIndexChanged( sender As System.Object, e As System.EventArgs) Handles ComboBoxMonth.SelectedIndexChanged FillDaysCB() ShowSelectedDate() End Sub Private Sub ComboBoxYear_SelectedIndexChanged( sender As System.Object, e As System.EventArgs) Handles ComboBoxYear.SelectedIndexChanged FillDaysCB() ShowSelectedDate() End Sub Private Sub ComboBoxDay_SelectedIndexChanged( sender As System.Object, e As System.EventArgs) Handles ComboBoxDay.SelectedIndexChanged ShowSelectedDate() End Sub LabelSelectedDate اجراء مساعد إلظهار التاريخ ال ُمختار بواسطة األداة 55
Private Sub ShowSelectedDate() Dim strDate As String strDate = ComboBoxDay.Text + "/" + ComboBoxMonth.Text + "/" strDate += ComboBoxYear.Text LabelSelectedDate.Text = "The selected date is " + strDate End Sub
56
األداة DateTimePicker توفر VB.NETأداة متطورة جدا لتمكين ال ُمستخدم من اختيار التواريخ التي يريد .هذه األداة تظهر كالتالي
إذا ضغط ال ُمستخدم خالل تشغيل النموذج على السهم الصغير الظاهر في الزاوية العليا اليُمنى من األداة فان الرزنامة التابعة لألداة تظهر وبامكان ال ُمستخدم حينها أن يختار التاريخ الذي يريد
التاريخ الذي اختاره ال ُمستخدم موجود في الخاصية Valueوهي من نوع Date
57
لدى هذه األداة حدث باسم ValueChangedيحدث كلما غير ال ُمستخدم التاريخ ال ُمختار Private Sub DateTimePicker1_ValueChanged(sender As System.Object, )e As System.EventArgs Handles DateTimePicker1.ValueChanged LabelSelectedDate.Text = "You date of birth is " + DateTimePicker1.Value End Sub
بارمترات معالجات األحداث اآلن نريد التعرف على بارامترات معالجات األحداث .يظهر في رأس معالج الحدث بارامتران وهما • :sender As Objectهذا البرامتر عبارة عن كائن من نوع objectومعروف أن الفئة objectهي الفئة التي تُشتق منها كل الفئات التي نكتبها بلغة .C#معنى ذلك أن هذا الكائن قد يكون حين المناداة على معالج الحدث ) (At Runtimeكائنا من نوع فئة ُمستقة (أي Derived )Subclassوذلك بواسطة منهج تحويل أنماط الكائنات ( .)Objetct Type Castingالتحويل الذي تم في هذه الحالة هو تحويل الى أعلى أي . Upcastingوبالفعل فقد يكون هذا الكائن من نوع Buttonاذا كان ُمرسل الحدث زرًا من نوع .Buttonأو قد يكون من نوع ComboBox اذا كان ُمرسل الحدث من نوع ComboBoxوهكذا ...ا ًذا هذا البرامتر وكما يدل على ذلك اسمه فانه يشير الى األداة التي أرسلت الحدث .احدى االمكانيات للقيام بعملية التحول هذه تكمن في استعمال العملية CTypeالتي هي اختصار لـ .Convert Typeتتلقى هذه العملية متغيرا أو مؤشرا على كائن ما والنوع الذي نريد الحصول على متغير أو مؤشر من نوعه )Expression CType(Expression, Typename العملية تعيد متغيرا أو مؤشرا من النمط المطلوب. المثال األول :أنماط أساسية Dim testNumber As Long = 1000 ' The following line of code sets testNewType to 1000.0. )Dim testNewType As Single = CType(testNumber, Single
58
المثال الثاني :تحويل مؤشرات Dim btn As Button )btn = CType(sender, Button • :e As EventArgsهذا البرامتر يحتوي على معلومات اضافية عن الحدث وما أرسله ُمرسل الحدث .شرح هذا البرامتر سيكون الحقًا من خالل مثال عملي. مثال المطلوب تصميم وبرمجة آلة حاسبة متطورة نوعا ما .التصميم المطلوب هو التالي:
الحظ أن معظم األدوات عبارة عن أزرار ألرقام .ما الذي يجب أن يحصل عندما ينقر ال ُمستخدم على أحد أزرار األرقام؟ عند النقر على أحد هذه األزرار يجب على معالج الحدث أن يضيف (عن طريق الوصل) الرقم الذي نُقر عليه الى العدد الموجود في صندوق النص .العشرة أزرار تشترك في هذه العملية (أي عملية الوصل) .عادة علينا أن نكتب عشرة معالجات أحداث بحيث نكتب لكل زر من األزرار معالجا خاصا به .لكن وبما أن كود معالجات األحداث سيكون متشابها جدا وللحيلولة دون اضافة نُسخ كود متشابهة وغير ضرورية نقترح كتابة معالج حدث واحد إذا كان بإمكاننا أن نعرف داخل معالج الحدث الرقم الذي يجب أن يضاف الى صندوق النص .هنا يأتي دور البارامتر sender
59
الذي يحتوي على معلومات عن ال ُمرسل .ا ًذا نضيف للعشرة أزرار معالج حدث واحد باسم عام وواحد مثل .OnDigitClickاضافة معالج الحدث تكون بإحدى الطرق التي ناقشناها سابقا. الصورة التالية تبين وضع البارامتر senderفي الـ Debug Modeحين استدعاء معالج الحدث بعد أن نقر المستخدم على الرقم .4
الحظ أن الخاصية Textالتابعة للزر والتي تظهر في القائمة التي في الصورة تحمل القيمة 4وهي نفس الرقم ال ُمراد اضافته الى نص الصندوق. الحظ أيضا أن أسماء األزرار العشرة التي تشترك في معالج الحدث OnDigitClickتظهر في رأس معالج الحدث بعد الكلمة Handles Private Sub OnDigitClick(sender As System.Object, e As System.EventArgs) Handles ... Dim btn As Button )btn = CType(sender, Button TextBoxResult.Text += btn.Text End Sub
انتبه الى أنه وجب علينا أن نقوم بعملية تحويل نمط الكائن senderالى أسفل أي DownCast لنعيده الى نمطه األصلي وهو .Buttonبدون القيام بعملية التحويل هذه لن يكون بإمكاننا الوصول الى الخاصية Textالمطلوبة .هذا المعالج مشترك لجميع أزرار األرقام كما تم شرح ذلك. واآلن سنكتب معال ًجا للحدث Clickللزر الذي يضغط عليه ال ُمستخدم إلضافة نقطة الى العدد إلدخال عدد حقيقي .علينا أن نقوم بفحص العدد الموجود في صندوق النص وفق القواعد التالية • ال يجوز للعدد أن يبدأ بنقطة 60
هذا الشرط ال يمكن فحصه اال عندما ينهي المستخدم ادخال العدد.• ال يجوز للعدد أن ينتهي بنقطة أو عندما ينهي+, -, *, … األول وهذا يحصل عندما ينقر احدى العمليات المتوفرة مثل = المستخدم ادخال العدد الثاني وبعدها ينقر الزر • ال يجوز للعدد أن يحتوي على أكثر من نقطة أي أن معالج الحدث ال يفعل.في حال عدم تحقق واحد من هذه الشروط فإننا نتجاهل محاولة االدخال .شيئا :واليكم برمجة المعالج Private Sub ButtonPoint_Click(sender As System.Object, e As System.EventArgs) Handles ButtonPoint.Click ' If the number is empty the request ' the user tries to insert a point at the beginning then ' make the number start with 0. If TextBoxResult.Text.Equals(String.Empty) Then TextBoxResult.Text = "0." Exit Sub End If
' If the user tries to insert more than one point then ' ignore it If TextBoxResult.Text.Contains(".") Then ' Ignore it Exit Sub End If
' Append the point Dim btn As Button btn = CType(sender, Button) TextBoxResult.Text += btn.Text End Sub 61
واآلن ماذا يجب أن يحصل عندما يضغط ال ُمستخدم على أحد أزرار العمليات الحسابية )(+,*,-,/؟ علينا • حفظ العدد األول الموجود في صندوق النص .TextBoxResultأما إذا كان الصندوق فارغا فعلينا اهمال هذا االختيار .الحظ أن حفظ العدد األول يجب أن يتم بمساعدة متغير يتم تعريفه كخاصيّة تابعة لفئة النموذج FormMyCalculatorوذلك حتى تكون معروفة لجميع العمليات ومعالجات األحداث الموجودة في الفئة. • حفظ العملية الحسابية التي اختارها ال ُمستخدم كخاصيّة تابعة لفئة النموذج FormMyCalculator • بعدها نقوم بتفريغ صندوق النص TextBoxResult • ثم يقوم ال ُمستخدم بإدخال العدد الثاني الى صندوق النص TextBoxResult الحظ أن جميع أزرار العمليات الحسابية تقوم بنفس المهام المذكورة أعاله .لذلك سنكتب لها جميعا معالجا واحدا باسم .OnOpClick أوال نقوم بتعريف المتغيرات على مستوى الفئة :FormMyCalculator Public Class FormMyCalculator Private FirstNumber, Operation As String ... End Class
:FirstNumberلحفظ العدد األول :Operationلحفظ العملية الحسابية ثم نكتب معالجا ألزرار العمليات الحسابية: Private Sub OnOpClick(sender As System.Object, e As System.EventArgs) Handles ButtonMult.Click, ButtonMinus.Click, ButtonDiv.Click, ButtonAdd.Click If TextBoxResult.Text = "" Then Exit Sub End If FirstNumber = TextBoxResult.Text Dim btn As Button )btn = CType(sender, Button Operation = btn.Text "" = TextBoxResult.Text End Sub 62
:= ثم نكتب معالجا لزر الحصول على النتيجة أي الزر المكتوب عليه الرمز Private Sub ButtonCalc_Click(sender As System.Object, e As System.EventArgs) Handles ButtonCalc.Click If TextBoxResult.Text = "" Then Exit Sub End If If FirstNumber = "" Or Operation = "" Then Exit Sub End If Dim N1, N2, Result As Double N1 = Double.Parse(FirstNumber) N2 = Double.Parse(TextBoxResult.Text) If Operation = "+" Then Result = N1 + N2 ElseIf Operation = "-" Then Result = N1 - N2 ElseIf Operation = "*" Then Result = N1 * N2 ElseIf Operation = "/" Then If N2 = 0 Then Exit Sub End If Result = N1 / N2 End If TextBoxResult.Text = Result.ToString FirstNumber = "" Operation = "" End Sub
63
تمارين تمرين رقم 1 أُكتب معالج حدث للحدث Clickللزر .BackSpaceعلى معالج الحدث أن يحذف من العدد ال ُمدخل آخر رقم تم ادخاله .بإمكانك االستعانة بالعملية Substringالتابعة للفئة String تمرين رقم 2 أُكتب معالج حدث للحدث Clickللزر Sqrtأي حساب الجذر التربيعي لعدد ما .بإمكانك االستعانة بالعملية Sqrtالتابعة لفئة الخدمة .Math تمرين رقم 3 أضف أزرارا الى اآللة الحاسبة لحساب الدوال الهندسية ) (Sin, Cos, Tanعليك كتابة معالج حدث واحد فقط لهذه الدوال باسم ..OnGeometricClickبإمكانك االستعانة بالعمليات الهندسية التابعة لفئة الخدمة .Mathانتبه الى أن هذه الدوال تطلب أن تكون الزوايا ال ُممرة لها كبارامترات بالرادين ) (Radiansوليس بالدرجات ) .(Degreesلتحويل الدرجات الى رادين عليك ضرب القيمة بـ .Math.PI/180 لمزيد من المعلومات: http://msdn.microsoft.com/en-us/library/system.math.cos.aspx تمرين رقم 4 أكمل اآللة الحاسبة بحيث تعمل جميع األزرار كما يجب. الحظ أنه بإمكان المستخدم استعمال لوحة المفاتيح إلدخال األعداد .األداة تدعم االدخال عبر لوحة المفاتيح بدون الحاجة الى أية برمجة اضافية.
64
األداة PictureBox ال تخلو التطبيقات التي نستعملها يوميا من الصور .لذلك توفر VB.NETعدة امكانيات الدراج وعرض الصور في النماذج .من هذه االمكانيات األداة PictureBoxالتي يوحي اسمها بأنها عبارة عن صندوق صورة أي عبارة عن إطار قادر على احتواء صورة وعرضها لل ُمستخدم .لتوضيح كيفية استعمال هذه األداة سنقوم بتطوير نموذج يعرض صورا لكلمات باللغة االنجليزية يختارها ال ُمستخدم بحيث تُعرض الصورة المناسبة كلما اختار ال ُمستخدم كلمة معينة. توضح الصورة التالية تصميم النموذج المطلوب: كلما ضغط ال ُمستخدم على أحد األزرار األربعة تظهر الصورة المناسبة.
65
توفر VB.NETامكانية خاصة لحفظ الصور ضمن المشروع بحيث تصبح جزءا من التطبيق وذلك من خالل اضافة الصور التي سنستعملها في التطبيق الى ملف مصادر خصوصي ويُسمى .Resources Fileللقيام بذلك علينا تنفيذ الخطوات التالية: افتح نافذة الـ Solution Explorerثم اختر اسم المشروع في هذه النافذة اختر من القائمة الرئيسية Projectفي الـ Visual Studioاألمر .Propertiesالحظ أن اسم المشروع جزء من اسم األمر .Propertiesمثال مشروعي اسمه .PictureBox_Appلذلك اسم األمر هو :PictureBox_App Properties
ثم اختر من النافذة التي تظهر األمر Resourcesفي الجهة اليسرى كم هو ُمبين في الصورة
66
ثم قم بواسطة الـ Windows Explorerباختيار ملفات الصور التي تريد اضافتها ثم سحبها ورميها على نافذة الـ .Resourcesبعدها سترى أن الصور اضيفت الى المصادر كما تُبين الصورة التالية:
67
لألربعة أزرار الموجودة في النموذجClick اآلن بامكاننا برمجة معالج الحدث Private Sub ShowPicture(sender As System.Object, e As System.EventArgs) Handles ButtonOrange.Click, ButtonKiwi.Click, ButtonBanana.Click, ButtonApple.Click Dim btn As Button btn = CType(sender, Button) If btn.Text = "Apple" Then PictureBox1.Image = My.Resources.Apple ElseIf btn.Text = "Orange" Then PictureBox1.Image = My.Resources.Orange ElseIf btn.Text = "Banana" Then PictureBox1.Image = My.Resources.Banana ElseIf btn.Text = "Kiwi" Then PictureBox1.Image = My.Resources.Kiwi End If End Sub
شرح البرمجة لنعرف النص المكتوب عليهButton الىsender أوال نقوم بتحويل البارامتر Dim btn As Button btn = CType(sender, Button)
الكلمة. والموجودة في المصادرApple نقوم باظهار الصورة المسماةApple إذا كان النص الكلمة تابعةResources توصلنا الى المصادر وهي أي المصادرعبارة عن خاصيّة باسمMy المحجوزة لديهاPictureBox الحظ أيضا أن األداة.Apple وهي تحوي بداخلها صورة باسمMy للكائن : قادرة على أخذ الصورة ال ُمراد عرضهاImage خاصيّة باسم If btn.Text = "Apple" Then PictureBox1.Image = My.Resources.Apple
.ثم نقوم بنفس األمر لكل زر من األزرار
ListBox األداة التي ُشرحت سابقًا باستثناء أنها تكون دائما مفتوحةComboBox هذه األداة شبيهة جدا باألداة الظهار باقي المحتوى أو للتنقل.وتُظهر كل المحتوى الذي يُمكن اظهاره بحسب ارتفاع األداة هنالك شريط. خاص لهذا الغرضScrolling ومعاينة محتوى األداة بامكان األداة اظهار شريط .Vertical Scrolling Bar وآخر عاموديHorizontal Scrolling Bar أفقي مثال 68
Stack Simulator نُريد استعمال هذه األداة الظهار كيفية عمل مبنى المعلومات "ال ّراصة" .يوفر هذا المبنى كما هو معلوم مجموعة من العمليات التي تضمن أن يكون اخراج وادخال المعلومات من والى رأس الرّاصة .وعليه فان آخر قيمة أُدخلت تكون أول قيمة تُخرج أي .LIFO – Last In First Out بعض عمليات المبنى "ال ّراصة" العملية
الشرح تُدخل المعلومة Xالى رأس الرّاصة
)Public Sub Push(X As Object
تُخرج المعلومة الموجودة في رأس ال ّراصة
Public Function Pop() As Object
تُعيد المعلومة الموجودة في رأس ال ّراصة بدون اخراجها
Public Function Top() As Object
تُعيد حجم ال ّراصة
Public Function Size() As Integer
نقترح التصميم التالي
ListBox Control
• الحظ أننا جعلنا صندوق النص ،الذي سيعرض نتيجة تنفيذ عملية من عمليات الرّاصة، للقراءة فقط من خالل اعطاء الخاصية ReadOnlyالتابعة لـ TextBoxالقيمة .True
69
• إذا كان صندوق االدخال TextBoxInputفارغا حينها سيكون الزر Pushغير قابل للضغط عليه أي سنعطي الخاصية Enabledالقيمة .Falseسنغير هذه الخاصية إذا تغير محتوى الصندوق ولم يعد فارغا وذلك من خالل معالجة الحدث TextChangedالتابع لـ .TextBoxيحصل هذا الحدث كلما تغير محتوى الصندوق. • إذا كانت الرّاصة فارغة حينها سيكون الزران Popو Topغير قابلين للضغط عليهما أي سنعطي الخاصية Enabledالقيمة .Falseسنغير هذه الخاصية إذا تغير محتوى الرّاصة ولم تعد فارغة .يحصل هذا في معالج الحدث Clickالتابع للزر .Push سنتحكم بعملية االدخال بحيث تكون الى رأس الرّاصة من خالل استعمال العملية Insertالتابعة للخصية Itemsالتابعة لـ :ListBox ) Public Sub Insert ( index As Integer, item As Object العملية تُضيف المعلومة itemالى المكان رقم indexفي الـ .ListBoxمن أجل اضافة itemالى رأس الرّاصة نستدعي Insertللمكان رقم :0 )ListBoxStack.Items.Insert(0, TextBoxInput.Text أما عملية االخراج من رأس الرّاصة فستكون من خالل استعمال العملية RemoveAtالتابعة للخصية Itemsالتابعة لـ :ListBox )Public Sub RemoveAt ( index As Integer العملية تحذف المعلومة الموجودة في المكان رقم indexمن الـ .ListBoxمن أجل حذف المعلومة الموجودة في رأس الرّاصة نستدعي RemoveAtللمكان رقم :0 )(TextBoxOutput.Text = ListBoxStack.Items(0).ToString )ListBoxStack.Items.RemoveAt(0 أي أننا نضيف فقط الى المكان رقم 0ونحذف أيضا فقط من المكان رقم .0 الحظ أن العملية RemoveAtعبارة عن اجراء وليس دالة أي أنها ال تُعيد القيمة التي تم حذفها. لذلك أخذنا أوال القيمة الموجودة في ) Items(0وحفظناها في الصندوق TextBoxOutputثم حذفناها. 70
:واليكم معالجات األحداث التي تشكل الحل Private Sub TextBoxInput_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBoxInput.TextChanged ButtonPush.Enabled = TextBoxInput.Text <> "" End Sub Private Sub ButtonPush_Click(sender As System.Object, e As System.EventArgs) Handles ButtonPush.Click ListBoxStack.Items.Insert(0, TextBoxInput.Text) ButtonPop.Enabled = True ButtonTop.Enabled = True End Sub Private Sub ButtonPop_Click(sender As System.Object, e As System.EventArgs) Handles ButtonPop.Click TextBoxOutput.Text = ListBoxStack.Items(0).ToString() ListBoxStack.Items.RemoveAt(0) ButtonPop.Enabled = ListBoxStack.Items.Count > 0 ButtonTop.Enabled = ListBoxStack.Items.Count > 0 End Sub Private Sub ButtonTop_Click(sender As System.Object, e As System.EventArgs) Handles ButtonTop.Click TextBoxOutput.Text = ListBoxStack.Items(0).ToString() End Sub Private Sub ButtonSize_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSize.Click TextBoxOutput.Text = ListBoxStack.Items.Count.ToString + " item(s)" End Sub Private Sub ButtonClose_Click(sender As System.Object, e As System.EventArgs) Handles ButtonClose.Click Me.Close() End Sub
71
تمرين رقم 5 Queue Simulator المطلوب استعمال هذه األداة الظهار كيفية عمل مبنى المعلومات "قائمة االنتظار" أو ما يُسمى أيضا "الطابور" .يوفر هذا المبنى كما هو معلوم مجموعة من العمليات التي تضمن أن يكون ادخال المعلومات الى نهاية الطابور .أما اخراج المعلومات فيجب أن يتم من رأس الطابور .وعليه فان أول قيمة أُدخلت تكون أول قيمة تُخرج أي .FIFO – First In First Out بعض عمليات المبنى "قائمة االنتظار" العملية
الشرح تُدخل المعلومة Xالى نهاية الطابور
)Public Sub Insert(X As Object
تُخرج المعلومة الموجودة في رأس الطابور
Public Function Remove() As Object
تُعيد المعلومة الموجودة في رأس الطابور بدون اخراجها
Public Function Head() As Object
تُعيد حجم الطابور
Public Function Size() As Integer
بامكان التصميم أن يكون مشابها لتصميم نموذج الرّاصة. الحظ أن االدخال سيكون الى المكان رقم .Items.Countأما االخراج فسيكون من المكان رقم .0 • يجب جعل صندوق النص ،الذي سيعرض نتيجة تنفيذ عملية من عمليات الطابور ،للقراءة فقط من خالل اعطاء الخاصية ReadOnlyالتابعة لـ TextBoxالقيمة .True • إذا كان صندوق االدخال TextBoxInputفارغا حينها سيكون الزر Insertغير قابل للضغط عليه أي سنعطي الخاصية Enabledالقيمة .Falseيجب تغيير هذه الخاصية إذا تغير محتوى الصندوق ولم يعد فارغا وذلك من خالل معالجة الحدث TextChangedالتابع لـ .TextBox • إذا كان الطابور فارغا حينها سيكون الزران Removeو Headغير قابلين للضغط عليهما أي سنعطي الخاصية Enabledالقيمة .Falseسنغير هذه الخاصية إذا تغير محتوى الطابور ولم يعد ا يحصل هذا في معالج الحدث Clickالتابع للزر .Insert
72
تمرين رقم 6 Evens And Odds نريد أن نبرمج نموذجًا يُمكن ال ُمستخدم من فرز األعداد الفردية واألعداد الزوجية .على ال ُمستخدم أن يبقي في احدى القوائم ) (ListBoxاألعداد الفردية وفي األخرى الزوجية .النموذج يُوفر زرين، واحدا لنقل أسطر من قائمة األعداد الفردية الى قائمة األعداد الزوجية وزرا للعملية العكسية. التصميم ال ُمقترح
• عنما يظهر النموذج تكون القائمة Evensمحتوية على 10أعداد طبيعية (زوجية وفردية) عشوائية من المجال ][1,100 • على ال ُمستخدم اختيار األعداد الفردية الموجودة في القائمة Evensونقلها بواسطة الضغط على الزر >== الى القائمة Odds • إذا أخطأ ال ُمستخدم بنقل بعض األعداد الزوجية الى القائمة Oddsبامكانه اختيارهم واعادتهم الى القائمة Evens • الضغط على الزر New Numbersيؤدي الى تعبئة القائمة Evensبعشرة أعداد جديدة طبيعية (زوجية وفردية) عشوائية من المجال ][1,100
73
Label يفحص محتوى القائمتين ويكتب رسالة مناسبة في الـCheck • الضغط على الزر خالف ذلك تُكتب رسالة خطأ باللون األحمر. باللون األزرقResult ال ُمسمى .New Numbers • يجب محو رسالة النتيجة كلما ضغط ال ُمستخدم على الزر علينا أن نعطي الخاصيّةListBox لتمكين ال ُمستخدم من اختيار أكثر من سطر في الـ حينها على ال ُمستخدم الضغط على ال ُمفتاح.MultiSimple التابعة له القيمةSelectionMode . بشكل ُمستمر خالل اختيار األسطرCtrl البرمجة Public Class FormMain Private Sub ButtonNew_Click(sender As System.Object, e As System.EventArgs) Handles ButtonNew.Click تغيير لون خط النتيجة الى األسود LabelResult.ForeColor = Color.Black وحذف النتيجة السابقة LabelResult.Text = "Result: " ListBoxEvens ومن ثم تعبئة القائمة Fill() End Sub Private Sub Fill() ListBoxOdds.Items.Clear() تفريغ القائمتين ListBoxEvens.Items.Clear() ListBoxEvens ومن ثم تعبئة القائمة Dim Rnd As New Random .بالقيم العشوائية Dim i As Integer For i = 1 To 10 ListBoxEvens.Items.Add(Rnd.Next(1, 101)) Next End Sub Private Sub ButtonToOdds_Click(sender As System.Object, e As System.EventArgs) Handles ButtonToOdds.Click While ListBoxEvens.SelectedItems.Count > 0 SelectedItems الخاصيّة ListBoxOdds.Items.Add(ListBoxEvens.SelectedItems(0).ToString) .تحوي جميع العناصر ال ُمختارة ListBoxEvens.Items.Remove(ListBoxEvens.SelectedItems(0)) الحظ أننا ننقل دائما العنصر End While ثم نحذفه فيؤدي0 ال ُمختار رقم End Sub ذلك الى خروجه من القائمة وأيضا من الخاصيّة Private Sub ButtonToEvens_Click(sender As System.Object, نُكرر ذلك.SelectedItems e As System.EventArgs) حتى ال تبقى أية عناصر ُمختارة Handles ButtonToEvens.Click .0 أي يصبح عددهم While ListBoxOdds.SelectedItems.Count > 0 ListBoxEvens.Items.Add(ListBoxOdds.SelectedItems(0).ToString) ListBoxOdds.Items.Remove(ListBoxOdds.SelectedItems(0)) End While End Sub
74
Private Sub ButtonCheck_Click(sender As System.Object, e As System.EventArgs) Handles ButtonCheck.Click If ListBoxEvens.Items.Count = 0 And ListBoxOdds.Items.Count = 0 Then Exit Sub End If Dim i, Num As Integer ' All items in ListBoxEvens MUST be even For i = 0 To ListBoxEvens.Items.Count - 1 Num = Integer.Parse(ListBoxEvens.Items(i).ToString()) If Num Mod 2 <> 0 Then LabelResult.ForeColor = Color.Red LabelResult.Text = "Error. Try Again!" Exit Sub End If Next ' All items in ListBoxOdds MUST be odd For i = 0 To ListBoxOdds.Items.Count - 1 Num = Integer.Parse(ListBoxOdds.Items(i).ToString()) If Num Mod 2 <> 1 Then LabelResult.ForeColor = Color.Red LabelResult.Text = "Error. Try Again!" Exit Sub End If Next LabelResult.ForeColor = Color.Blue LabelResult.Text = "Wow. Perfect!" End Sub End Class
75
األدوات RadioButtonو CheckBox توفر VB.NETهاتين األداتين لتمكين ال ُمستخدم من اختيار امكانية واحدة فقط من عدة امكانيات من خالل استعمال األداة .RadioButtonأو اختيار عدة امكانيات من عدة امكانيات بمساعدة األداة .CheckBoxهذه األدوات لديها وضعان :اما أن تكون ُمختارة (أي )Checkedأو غير ُمختارة. لذلك فهي توفر خاصيّة بوليانية باسم Checkedلمعرفة ذلك .تكون قيمة هذه الخاصيّة Trueإذا كانت األداة ُمختارة .خالف ذلك تكون قيمتها .False صورة األداة CheckBoxوهي غير ُمختارة
وصورتها وهي ُمختارة
أما األداة RadioButtonفهي تظهر كالتالي:
تستعمل التطبيقات ال ُمختلفة عادة أكثر من أداة RadioButtonواحدة كمجموعة اختيارات يتم وضعها ضمن GroupBoxمما يُكسبها صفة المجموعة التي يُمكن اختيار واحدة منها فقط .أي أن النقطة التي تكون في الدائرة تظهر عند أداة واحدة من المجموعة وإذا ضغط ال ُمستخدم على اداة أخرى من نفس المجموعة تنتقل النقطة اليها أي يتم اختيارها هي والغاء االختيار السابق. الحظ أنه بامكاننا تصميم النموذج األخير لمعرفة جنس ال ُمستخدم كالتالي:
76
فاألداة CheckBoxFemaleاما أن تكون ُمختارة فيعني ذلك Femaleأو أن تكون غير ُمختارة فيعني ذلك .Male مثال نريد تصميم نموذج يُمكننا من معرفة لُغات البرمجة المختلفة التي يتقنها ال ُمستخدم. التصميم ال ُمقترح
بامكان النموذج حساب عدد النقاط الكلي التي يحصل عليها ال ُمستخدم بحيث يُعطى لكل لغة يتقنها 25 نقطة. مثال ال ُمستخدم التالي حصل على 75نقطة ألنه يعرف 3لغات فقط:
77
Calculate Points للزرClick برمجة معالج الحدث Private Sub ButtonCalc_Click(sender As System.Object, e As System.EventArgs) Handles ButtonCalc.Click Dim Points As Integer = 0 نقطة إذا تم25 أضف If CheckBoxVB_NET.Checked Then VB.NET اختيار Points += 25 وهكذا بالنسبة لباقي End If االختيارات If CheckBoxCSharp.Checked Then Points += 25 End If If CheckBoxJava.Checked Then Points += 25 End If If CheckBoxCPP.Checked Then Points += 25 End If LabelPoints.Text = "Number Of Points: " + Points.ToString End Sub
مثال
78
نريد تصميم نموذج يُمكن طالبا ما من التدرب على حل مسائل جمع أعداد صحيحة .البرنامج ينشئ مسائل حسابية بشكل عشوائي من المجال ] [10,100ويقترح 4حلول على شكل األسئلة األمريكية. بامكان ال ُمستخدم الضغط على زر Check My Answerلفحص االجابة .البرنامج يُظهر صورة مناسبة تبين هل كانت االجابة صحيحة أم ال ويظهر لكل سؤال رقمه التسلسلي .ال ُمستخدم يطلب من البرنامج أن ينشئ سؤاال جديدا من خالل الضغط على زر .Generate A New Question السؤال األول يتم انشاؤه عند تشغيل التطبيق .األجوبة الثالثة الخطأ سيتم انشاؤها عشوائيا من المجال ] .[CorrectAnswer+1, CorrectAnswer+10ترتيب األجوبة سيكون أيضا عشوائيا قدر االمكان بحيث ال تظهر االجابة الصحيحة دائما في نفس األداة (أي االجابة األولى مثال أو الثانية وهكذا) .البرنامج يقوم أيضا بترقيم األسئلة بشكل تسلسلي. التصميم ال ُمقترح هنا االجابة صحيحة .لذلك تظهرعند فحص السؤال من خالل الضغط على الزر Check MyAnswerالصورة التي تدل على أن االجابة صحيحة.
79
بينما هنا االجابة غيرصحيحة .لذلك تظهرعند فحص السؤال من خالل الضغط على الزر Check MyAnswerالصورة التي تدل على أن االجابة غير صحيحة.
يصف الجدول التالي بعض األدوات ال ُمستعملة في هذا النموذج األداة
االسم أو الـ ID
الشرح
Button
ButtonNewQuestion
لطلب انشاء سؤال جديد
GroupBox
GroupBoxQuestionNr
PictureBox
PictureBoxAnswer
Label
LabelQuestion
الظهار السؤال الحالي
RadioButton
RadioButtonA
االجابة األولى
RadioButton
RadioButtonB
االجابة الثانية
RadioButton
RadioButtonC
االجابة الثالثة
RadioButton
RadioButtonD
االجابة الرابعة
Button
ButtonCheckAnswer
الظهار رقم السؤال الحالي الظهار صورة معبرة عن :هل كانت االجابة صحيحة أم ال.
80
لطلب فحص االجابة
شرح لبعض العمليات ال ُمساعدة ()Private Sub GenerateNewQuestion ادعاء الدخول: ادعاء الخروج :تقوم بانشاء سؤال عشوائي جديد من المجال ] [1,100وتشكيل األجوبة الخاطئة الى جانب االجابة الصحيحة .ثم تقوم بتوزيع األجوبة بشكل عشوائي على أدوات الـ RadioButtonال ُمعدة لالجابات.
)(Private Sub ResetControls ادعاء الدخول: ادعاء الخروج :تقوم بازالة أي اختيار من أدوات الـ RadioButtonال ُمعدة لالجابات .أي أنها تحذف اجابة السؤال السابق.
)Private Sub FillArray(ByVal A() As Integer ادعاء الدخول :تتلقى مصفوفة األجوبة .هذه المصفوفة فيها فقط في الخلية األولى االجابة الصحيحة .باقي القيم سالبة. ادعاء الخروج :تقوم بتعبئة باقي خاليا المصفوفة بقيم عشوائية من المجال [ . ]A(0)+1, A(0)+10هذه القيم تمثل االجابات الخاطئة .العملية تمنع تكرار نفس االجابة .أي أن األجوبة ال تتكرر لتفس السؤال.
)Private Sub Randomize(ByVal A() As Integer ادعاء الدخول :تتلقى مصفوفة األجوبة. ادعاء الخروج :تقوم بتغيير أماكن األجوبة بشكل عشوائي.
)(Private Sub CheckAnswer ادعاء الدخول: ادعاء الخروج :تقوم بفحص االجابة التي اختارها ال ُمستخدم وتقارنها مع االجابة الصحيحة وتظهر صورة معبرة عن نتيجة الفحص.
احرص على اضافة صورتين من اختيارك الى My.Resourcesكما ُشرح سابقا عند األداة .PictureBoxواحدة تُعبر عن اجابة صحيحة مثال مكتوب عليها Correctأو صحيح أو أي كلمة أو رمز آخر .واألخرى مكتوب عليها Wrongأو خطأ أو أي كلمة أو رمز آخر. البرمجة في البداية نقوم بتعريف ع ّداد لرقم السؤال ونصفره .التعريف يكون على مستوى فئة النموذج. Public Class FormSimpleMathTrainer Private QuestionNr As Integer = 0 End Class
81
بانشاء السؤال األول قبل ظهور النموذجGenerateNewQuestion() ثم نقوم بمساعدة العملية التابع للنموذجLoad لل ُمستخدم في معالج الحدث Private Sub FormSimpleMathTrainer_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load GenerateNewQuestion() End Sub
واليكم البرمجة الكاملة للتطبيق Public Class FormSimpleMathTrainer Private QuestionNr As Integer = 0 Private Sub ButtonNewQuestion_Click(sender As System.Object, e As System.EventArgs) Handles ButtonNewQuestion.Click GenerateNewQuestion() End Sub Private Sub GenerateNewQuestion() QuestionNr += 1 GroupBoxQuestionNr.Text = "Question Nr " + QuestionNr.ToString PictureBoxAnswer.Visible = False Dim R As New Random Dim X, Y, CorrectAnswer As Integer X = R.Next(10, 101) Y = R.Next(10, 101) CorrectAnswer = X + Y LabelQuestion.Text = X.ToString + " + " + Y.ToString() Dim Answers(3) As Integer Answers هنا نستعمل المصفوفة األحادية Answers(0) = CorrectAnswer استعمال المصفوفة يُمكننا من.لتخزين األجوبة FillArray(Answers) خلط أماكن األجوبة بشكل عشوائي بحيث ال Randomize(Answers) .تظهر االجابة الصحيحة دائما في نفس المكان RadioButtonA.Text = Answers(0) بعد الخلط نوزع األجوبة على أدوات االجابة أي RadioButtonB.Text = Answers(1) وأخيرا نقوم بازالة.RadioButtons على الـ RadioButtonC.Text = Answers(2) .االجابة األخيرة RadioButtonD.Text = Answers(3) ResetControls() End Sub
Private Sub ResetControls() هذه العملية تقوم بازالة أي اختيار من أدوات الـ . ال ُمعدة لالجاباتRadioButton
RadioButtonA.Checked = False RadioButtonB.Checked = False RadioButtonC.Checked = False RadioButtonD.Checked = False End Sub
82
Private Sub Randomize(ByVal A() As Integer) Dim R As New Random Dim i, j As Integer For i = A.Length - 1 To 1 Step -1 j = R.Next(0, i) Swap(A, i, j) Next
هذه العملية تقوم بتغيير أماكن األجوبة بشكل j الحظ أننا نقوم بانشاء رقم الخلية.عشوائي .Swap بشكل عشوائي وتمريره للعملية
End Sub Private Function Exists(ByVal A() As Integer, ByVal x As Integer) As Boolean For i = 0 To A.Length - 1 If A(i) = x Then Return True End If Next Return False End Function Private Sub FillArray(ByVal A() As Integer) Dim Rnd As New Random Dim x As Integer For i = 1 To A.Length - 1 x = Rnd.Next(A(0) + 1, A(0) + 11) While Exists(A, x) And i < A.Length x = Rnd.Next(A(0) + 1, A(0) + 11) End While A(i) = x Next
أي0 االجابة الصحيحة موجودة في الخلية رقم .A(0) العملية تقوم بتعبئة باقي خاليا المصفوفة بقيم عشوائية من المجال هذه القيم تمثل. ]A(0)+1, A(0)+10[ العملية تمنع تكرار نفس.االجابات الخاطئة . أي أن األجوبة ال تتكرر لتفس السؤال.االجابة
End Sub
Private Sub Swap(ByVal A() As Integer, ByVal i As Integer, ByVal j As Integer) Dim Temp As Integer Temp = A(i) A(i) = A(j) A(j) = Temp End Sub
83
Private Sub ButtonCheckAnswer_Click(sender As System.Object, e As System.EventArgs) Handles ButtonCheckAnswer.Click CheckAnswer() End Sub Private Sub CheckAnswer() Dim X, Y, CorrectAnswer, UsersAnswer As Integer Dim Q As String = LabelQuestion.Text X = Integer.Parse(Q.Substring(0, Q.IndexOf(" "))) Y = Integer.Parse(Q.Substring(Q.IndexOf("+") + 1)) CorrectAnswer = X + Y If RadioButtonA.Checked Then UsersAnswer = Integer.Parse(RadioButtonA.Text) ElseIf RadioButtonB.Checked Then UsersAnswer = Integer.Parse(RadioButtonB.Text) ElseIf RadioButtonC.Checked Then UsersAnswer = Integer.Parse(RadioButtonC.Text) ElseIf RadioButtonD.Checked Then UsersAnswer = Integer.Parse(RadioButtonD.Text) End If PictureBoxAnswer.Visible = True If UsersAnswer = CorrectAnswer Then PictureBoxAnswer.Image = My.Resources.Correct Else PictureBoxAnswer.Image = My.Resources.Wrong End If
منY وقيمةX هنا نستخلص قيمة نص السؤال LabelQuestion.Text
هنا نجد االجابة التي اختارها .ال ُمستخدم
إذا كانت االجابة صحيحة نُظهر خالف ذلك.Correct الصورة .Wrong نُظهر الصورة
End Sub Private Sub FormSimpleMathTrainer_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load GenerateNewQuestion() End Sub Private Sub ButtonClose_Click(sender As System.Object, e As System.EventArgs) Handles ButtonClose.Click Me.Close() End Sub End Class
84
تمرين رقم 7 أضف الى نموذج المثال السابق للتدرب على عملية الجمع االمكانيات التالية: اختيار مجال األعداد اختيار العملية التي يريد ال ُمستخدم التدرب عليها من بين العمليات +, -, *, /
العملية MessageBox.Show توفر هذه العملية امكانية الظهار نافذة حوار من التطبيق لل ُمستخدم البالغة رسالة معينة (أي )Messageأو سؤاله عن أمر ما بحيث أن االجابة على السؤال تتحكم في مجرى تنفيذ التطبيق. تتلقى هذه العملية عدة بارامترات تحدد مبنى ومحتوى نافذة الحوار. أمثلة االستدعاء التالي يتلقى فقط نص الرسالة "!"Hi, I am a simple MessageBox )"!MessageBox.Show("Hi, I am a simple MessageBox
ويظهر النافذة التالية
االستدعاء التالي يتلقى الى جانب نص الرسالة "! "Hi, I am a simple MessageBoxالعنوان ""Sample 2 )"MessageBox.Show("Hi, I am a simple MessageBox!", "Sample 2
ويظهر النافذة التالية
85
"Confirmation" العنوانAre You Sure?" االستدعاء التالي يتلقى الى جانب نص الرسالة No والزرYes هنا نريد الزر.ويحدد األزرار التي يريدها في نافذة الحوار MessageBox.Show("Are You Sure?", "Confirmation", MessageBoxButtons.YesNo)
ويظهر النافذة التالية
:األزرار التي يُمكننا اضافتها الى نافذة الحوار هي التالية األزرار
القيمة
Ignore وزرRetry وزرAbort زرMessageBoxButtons.AbortRetryIgnore فقطOK زرMessageBoxButtons.OK Cancel وزرOK زرMessageBoxButtons.OKCancel Cancel وزرRetry زرMessageBoxButtons.RetryCancel No وزرYes زرMessageBoxButtons.YesNo Cancel وزرNo وزرYes زرMessageBoxButtons.YesNoCancel
كيف يُمكننا معرفة االجابة التي اختارها ال ُمستخدم؟ 86
وهي واحدة من احدى القيم التالية بحسب األزرارDialogResult تعيد هذه العملية قيمة من نوع التي تظهر في نافذة الحوار • Windows.Forms.DialogResult.No • Windows.Forms.DialogResult.Abort • Windows.Forms.DialogResult.Retry • Windows.Forms.DialogResult.Ignore • Windows.Forms.DialogResult.OK • Windows.Forms.DialogResult.Cancel مثال Dim Res As DialogResult Res = MessageBox.Show("Are You Sure?", "Confirmation",MessageBoxButtons.YesNo) If Res = Windows.Forms.DialogResult.No Then يتمNo إذا أجاب ال ُمستخدم بـ Exit Sub .مثال الخروج من االجراء End If
"Confirmation" " العنوانAre You Sure?" االستدعاء التالي يتلقى الى جانب نص الرسالة ويضيف صورة تُعبرNo والزرYes هنا نريد الزر.ويحدد األزرار التي يريدها في نافذة الحوار عن سؤال تظهر الى يسار نص الرسالة MessageBox.Show("Are You Sure?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
ويظهر النافذة التالية
87
:الصور التي يُمكننا اضافتها الى نافذة الحوار هي التالية • MessageBoxIcon.Asterisk • MessageBoxIcon.Error • MessageBoxIcon.Exclamation • MessageBoxIcon.Hand • MessageBoxIcon.Information • MessageBoxIcon.Question • MessageBoxIcon.Stop • MessageBoxIcon.Warning . التي تدل على طلب عدم اضافة صورةMessageBoxIcon.None القيمة االفتراضية هي
88
تمرين رقم 8 صمم تطبيقًا لجمع المعلومات عن مرشحين لوظائف معينة فيه األدوات التالية:
يُقبل المرشح إذا حصل على األقل على 30نقطة. كيفية توزيع النقاط: • المرشح المتزوج يحصل على 12نقطة .خالف ذلك يحصل على 10نقاط. • المرشح الذي سنه أقل من 25سنة يحصل على 15نقطة. • المرشح الذي سنه بين 25و 45سنة يحصل على 20نقطة. • المرشح الذي تجاوز 45سنة يحصل على 13نقطة. • المرشح الذي له خبرة خارج البالد يحصل على 15نقطة .مقابل خبرة داخل البالد يحصل على 10نقاط. عند النقر على الزر " افحص المعطيات" على النموذج أن يفحص أنه بالفعل تم اختيار الحالة الشخصية والجيل ومن ثم حساب عدد النقاط وكتابة النتيجة في صندوق النتيجة .يجب كتابة النص "قُبل" أو " لم يُقبل" بنا ًء على عدد النقاط .اذا لم يتم اختيار المعلومات الالزمة ،على الصفحة أن تظهر نص خطأ من خالل استعمال .MessageBox
89
عند النقر على الزر " المرشح التالي" على النموذج أن يزيل جميع األختيارات استعداداً ألستيعاب المعلومات عن المرشح التالي. أما عند النقر على الزر "النتائج النهائية" على الصفحة أن تظهر عدد المرشحين الكلي وعدد الذين تم قبولهم.
90
األداة ListView تُمكننا هذه األداة من عرض المعلومات على هيئة جدول مكون من أعمدة بحيث يكون لكل عامود اسم خاص به. بعد اضافة األداة ListViewالى النموذج نقوم بالضغط على السهم الذي يظهر في الزاوية اليمنى العليا من األداة لنفتح قائمة تمكننا من تحديد بعض خصائص األداة واضافة األعمدة اليها:
نختار من القائمة التي تظهر Viewمن نوع Details
91
ثم نضيف أعمدة من خالل الضغط على الرابط Edit Columnsالموجود في القائمة والزر Add
بعد الضغط على Addتظهر النافذة التالية
ثم نقوم بتغيير قيمة الحقل Nameالى اسم مناسب مثال ( FullNameالحظ أن هذا اسم متغير للعامود) والحقل Textنعطيه القيمة Full Nameوهي القيمة التي ستظهر كعنوان للعامود .الحظ أن العامود الجديد ظهر في القائمة Membersالتي في يسار النافذة .ثم نضيف بهذه الطريقة جميع األعمدة التي نريد:
92
تُظهر النافذة أننا أضفنا 4أعمدة وهي .FullName, Address, Age, EmailAddress الحظ أيضا أننا حددنا بأن يظهر عنوان كل عامود في الوسط من خالل الخاصية أو الحقل TextAlignواعطائه القيمة .Center إذا شغلنا النموذج ستظهر األداة كالتالي
أي تظهر كجدول مكون من 4أعمدة. اآلن نضيف الى النموذج 4صناديق نص الدخال القيم وزرا باسم Addالضافة سطر جديد الى الـ ListViewفيه القيم التي أُدخلت الى الصناديق
93
برمجة معالج الحدث Clickللزر Add هنالك عدة امكانيات الضافة أسطر الى األداة .ListViewنناقش هنا امكانيتين .الحقا سنعود لألداة لنستعملها لعرض معلومات من قاعدة بيانات. )Private Sub ButtonAdd_Click(sender As System.Object, e As System.EventArgs Handles ButtonAdd.Click ُن َعرّ ف مصفوفة أحادية من نوع String Dim A(3) As String ثم ُن َخ ّزن فيها القيم الموجودة في صناديق A(0) = TextBoxFullName.Text النص. A(1) = TextBoxAddress.Text ثم ُن َع ّرف وننشئ كائنا من نوع A(2) = TextBoxAge.Text .ListViewItemالعملية البنائية تتلقى A(3) = TextBoxEmailAddress.Text المصفوفة كبارامتروتنقل قيم خالياها الى السطر الحالي بحيث ُتعطى قيمة الخلية Dim itm As ListViewItem األولى الى العامود األول والثانية الى الثاني )itm = New ListViewItem(A وهكذا. )ListViewStudentsDetails.Items.Add(itm End Sub
94
امكانية أخرى الضافة أسطر جديدة الى األداة )Private Sub ButtonAdd_Click(sender As System.Object, e As System.EventArgs Handles ButtonAdd.Click Dim itm As ListViewItem )(ListViewStudentsDetails.BeginUpdate
أوال ُنخبر األداة ببدء عملية تغيير محتواها نضيف قيم العامود األول .هذه العملية تنشئ وتجهز الكائن بحيث يصبح صالحا لالستعمال
)itm = ListViewStudentsDetails.Items.Add(TextBoxFullName.Text
)itm.SubItems.Add(TextBoxAddress.Text نضيف باقي القيم بمساعدة الكائن itm )itm.SubItems.Add(TextBoxAge.Text )itm.SubItems.Add(TextBoxEmailAddress.Text نطلب من األداة القيام بتغيير محتواها ثم ُنخبر األداة بانتهاءعملية تغيير محتواها
)(ListViewStudentsDetails.Update )(ListViewStudentsDetails.EndUpdate End Sub
عملية اخبار األداة ببداية ونهاية عملية تغيير المحتوى هي فقط لرفع نجاعة عملية االضافة ومنع رسم األداة خالل عملية االضافة. بعد اضافة بعض المعلومات تظهر األداة كالتالي
95
تمرين رقم 9 صمم نموذجا لتسجيل طالب لدروس وفق التصميم التالي:
تعبئة األيام واألشهر والسنوات الى قوائم تاريخ الميالد يجب أن تتم عند ظهور النموذج للمرة األولى ويجب اظهار قيم تصلح لتكوين التاريخ مع مراعاة سنة كبيسة وما الى ذلك .أما ادخال أسماء الدروس فيجب أن يتم في ملف التنسيق .أي أن ادخال أسماء الدروس يتم بشكل يدوي. عند النقر على الزر " أضف الطالب الى القائمة" على النموذج أن يفحص أنه بالفعل تمت تعبئة جميع المعلومات الالزمة .اذا لم يتم ادخال أو اختيار المعلومات الالزمة ،على النموذج أن يظهر نص خطأ في مكان ما في النموذج من خالل استعمال األداة Labelأو اظهار رسالة بواسطة .MessageBoxبعدها يتم إضافة المعلومات كسطر جديد في قائمة الطالب بالنسق الظاهر في المثال أعاله ويتم تفريغ جميع صناديق النصوص من محتواها.
96
MDI Applications تطبيقات متعددة المستندات أو النماذج تتكون معظم التطبيقات من عدة نماذج أو ما يُسمى أيضا بال ُمستندات ( .)Documentsمن هنا أطلق على هذه التطبيقات االسم "واجهات متعددة المستندات" أي .Multiple-Document Interface الذي يُميز هذه التطبيقات وجود نموذج رئيسي ) (Main Form or Parent Formيحتوي على قائمة رئيسية ) .(Main Menuهذه القائمة تحتوي على عناصر متعددة وقد تكون متداخلة (أي قائمة داخل قائمة وهكذا) .الضغط على واحد من هذه العناصر يُظهر عادة نموذجًا فرعيّا )(Child Form ما من نماذج التطبيق .وقد تكون عدة نماذج فرعيّة ُمشغلة أو ظاهرة في نفس الوقت. مثال
األداة MenuStrip تعتبر اداة القائمة من اهم االدوات التي تمكن المستخدم من التنقل بين نماذج التطبيق والوصول الى العمليات المختلفة التي يوفرها التطبيق .تتبع هذه االداة في الـ Toolboxلمجموعة ادوات القوائم وأشرطة األدوات او ما يسمى بـ .Menus And Toolbars مبنى هذه االداة تراجعي Recursive Structureبمعنى ان كل عنصر من عناصرها (Menu ) Itemقد يكون بحد ذاته قائمة وهكذا نحصل على قائمة مكونة من قوائم. المشترك بين هذه العناصر ان المستخدم ينقر على عناصرها نقرة واحدة Single Click Event ليفتح القائمة الفرعية او لينفذ االمر (أي فتح نموذج جديد وما الى ذلك) ولذلك تشترك جميع عناصر القائمة بنفس معالج الحدث أي اننا نكتب معالج حدث واحد لجميع هذه العناصر والحدث هو . MenuItemClick من أجل الحصول على النموذج الرئيسي الذي يصلح النشاء تطبيق ،MDIعلينا أن ننشئ مشروعًا جديدًا ثم نعطي الخاصيّة IsMdiContainerالتابعة للنموذج القيمة :True
97
98
الحظ كيف تغير مظهر النموذج. واآلن نضيف القائمة الرئيسية الى النموذج من خالل سحبها من الـ Toolboxورميها على النموذج:
الضافة عناصر الى القائمة نضغط على الصندوق الذي يظهر أعلى النموذج ومكتوب عليه “Type ” .Hereنريد تصميم تطبيق نجمع فيه جميع النماذج التي تمت برمجتها حتى اآلن كوظائف. بعد اضافة العنصر الجديد Homeworksتصبح القائمة كالتالي
99
وبعد اضافة عدة عناصر اضافية تصبح كالتالي
واآلن نضيف قائمة فرعية تظهر عند الضغط على العنصر Simulatorsمن خالل الكتابة في الصندوق الذي يظهر الى يمين العنصر Simulatorsومكتوب عليه ” “Type Hereلتصبح القائمة كالتالي:
100
وعند تشغيل التطبيق يظهر كالتالي
كتابة معالجات األحداث لعناصرالقائمة الحدث االفتراضي لعناصر القائمة هو الحدث .Clickاضافة معالج لهذا الحدث تتم بنفس الطريقة التي سبق شرحها في األمثلة السابقة. في المثال التالي نريد تطوير تطبيق نجمع فيه كل التمارين والوظائف السابقة بحيث نتمكن من استدعاء أي نموذج من نماذج هذه التمارين من خالل اختيار عنصر القائمة الذي يُظهر هذا النموذج .أي أننا سنقوم باضافة جميع النماذج التي تمت برمجتها حتى اآلن الى هذا المشروع وتوفير عنصر قائمة لكل نموذج اسمه يساوي اسم النموذج ووظيفته اظهار النموذج لل ُمستخدم. الضافة نماذج جاهزة (مثل التمارين أو الوظائف السابقة) نضغط في الـ Solution Explorerعلى الزر األيمن للفارة ثم نختار األمر Add Existing Itemالذي يظهر عند الضغط على األمر Addكما هو ُمبين في الصورة التالية
101
ثم نختار في النافذة التي تظهر جميع الملفات ال ُمتعلقة بالنموذج (يجب مراعاة أنه ال يوجد في المشروع نموذج نفس االسم وإذا ُوجد بامكاننا تغيير االسم من خالل الضغط على اسم النموذج بالزر األيمن للفارة في الـ Solution Explorerللمشروع الذي نريد أخذ النموذج منه ثم نختار األمر Renameثم نكتب االسم الجديد) وفي النهاية نضغط على الزر Addكما هو ُمبين في الصورة التالية
الحظ أنه يجب اضافة جميع الصور التابعة للنموذج (ان ُو ِجدت) الى الـ Resourcesكما ُش ِرح سابقًا. بعد ذلك نقوم بانشاء معالج للحدث Clickالتابع لعنصر القائمة Math Trainerلنحصل على المعالج التالي Private Sub MenuItemMathTrainer_Click(sender As System.Object, )e As System.EventArgs Handles MenuItemMathTrainer.Click End Sub
الظهار النموذج عندما يختار ال ُمستخدم هذا العنصر نقوم بالخطوات التالية • نبني كائنا جديدا من النموذج FormSimpleMathTrainer Dim f As New FormSimpleMathTrainer
• نُبلغ النموذج بأنه تابع للنموذج الرئيسي ( )FormMainمن خالل استعمال الخاصية MdiParent 102
f.MdiParent = Me
• وأخيرا نظهر النموذج بمساعدة العملية Show ()f.Show
وبهذا يصبح المعالج كامال Private Sub MenuItemMathTrainer_Click(sender As System.Object, )e As System.EventArgs Handles MenuItemMathTrainer.Click Dim f As New FormSimpleMathTrainer f.MdiParent = Me )(f.Show End Sub
اختيار هذا العنصر يُظ ِهر النموذج كما يلي
الحظ أن • النموذج يبقى محصو ًرا داخل إطار النموذج الرئيسي وال يمكنه الظهور خارجه .ولو حاول ال ُمستخدم سحبه الى خارج اإلطار لما تمكن من ذلك. • وإذا تم اغالق النموذج الرئيسي يُغلق أيضًا النموذج التابع له. • وإذا تم تصغير النموذج الرئيسي يُصغرأيضًا النموذج التابع له. هذا هو بالضبط معنى كون النموذج الفرعي Childبالنسبة للنموذج الرئيسي أو أن النموذج الرئيسي هو Parentالنموذج الفرعي .وهو ما فعلناه من خالل األمر f.MdiParent = Meالمشروح أعاله.
103
تمارين تمرين رقم 1 المطلوب اكمال المثال بحيث تُضاف اليه نماذج جميع التمارين السابقة
تمرين رقم 2 أضف نموذ ًجا جديدا يعرض معلومات عنك (االسم ،العنوان ،التخصص وما الى ذلك) وبتصميم من اختيارك يظهر عندما يختار ال ُمستخدم العنصر .About
تمرين رقم 3 المطلوب تنفيذ الخطوات التالية على المثال من تمرين 1وتمرين .2 نريد اضافة حماية للتطبيق بحيث تظهر جميع عناصر القائمة غير قابلة لالختيار ) (Disabledوعلى ال ُمستخدم أن يقوم بعملية Loginناجحة للدخول للتطبيق .لدينا عنصرقائمة لعملية .Loginنص العنصر (أي الخاصيّة )Text تتغير بعد دخول ال ُمستخدم بنجاح لتصبح .Logoutوإذا خرج من خالل اختيار Logoutتُصبح .Login الحظ أن عناصرالنموذج الرئيسي Fileو Helpغير قابلة لالختيار
104
Login تصميم نموذج
التابعة لصندوق كلمةPasswordChar من أجل اخفاء كلمة المرورعند كتابتها قم باعطاء الخاصيّة .ال ُمرور القيمة * أو أي رمز آخر تختاره Login اظهار نافذة الحوار Private Sub MenuItemLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemLogin.Click If MenuItemLogin.Text = "Logout" Then MenuItemLogin.Text = "Login" DisableMenuItems() Return End If
Login يُغير الىLogout إذا كان النص ومن ثم تُستدعى العملية لجعل جميعDisableMenuItems عناصر القائمة الرئيسية غير قابلة لالختيار هنا نظهر النموذج كنافذة حوار وليس تمنعShowDialog العملية.كنموذج ال ُمستخدم من اختيار أي شيء في التطبيق .اال بعد اغالق هذه النافذة بنجاح
Dim dlg As New FormLogIn Dim Res As DialogResult Res = dlg.ShowDialog()
If Res = Windows.Forms.DialogResult.Yes Then For Each item As ToolStripMenuItem In MenuStrip1.Items item.Enabled = True القيمةShowDialog إذا أرجعت العملية If item.Name.ToString = "MenuItemLogin" Then معنى ذلك أنه مسموح لل ُمستخدمYes item.Text = "Logout" حينها تُجعل جميع العناصر قابلة.الدخول End If لالختيار ويصبح اسم عنصر الدخول Next Logout End If End Sub
105
DisableMenuItems العملية غير قابلة لالختيار أيLogin هذه العملية تجعل جميع عناصر القائمة الرئيسية باستثناء العنصر Disabled Private Sub DisableMenuItems() For Each item As ToolStripMenuItem In MenuStrip1.Items If item.Name.ToString <> "MenuItemLogin" Then item.Enabled = False End If Next End Sub FormLogIn في النموذجLogin التابع للزرClick برمجة معالج الحدث
الحقًا سنحصل على.123 وMe في هذه المرحلة ستكون كلمة ال ُمرور واسم ال ُمستخدم القيم الثابتة هذه القيم من قاعدة البيانات Private Sub ButtonLogIn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonLogIn.Click If TextBoxUserName.Text = "Me" And TextBoxPassword.Text = "123" Then Me.DialogResult = DialogResult.Yes Else Me.DialogResult = DialogResult.No End If Me.Close() End Sub
التابعةDialogResult اعادة القيمة من قبل نافذة الحوار تكون من خالل استعمال الخاصيّة خالف ذلك تُعاد القيمة.DialogResult.Yes في حال كان ال ُمستخد ُم معروفًا تُعاد القيمة.للنموذج الحظ أننا نفحص القيمة ال ُمرجعة من قبل هذه النافذة الحوار في معالج الحدث.DialogResult.No . الذي تم شرحه أعالهMenuItemLogin_Click FormLogIn في النموذجCancel التابع للزرClick برمجة معالج الحدث Private Sub ButtonCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonCancel.Click Me.DialogResult = DialogResult.No Me.Close() End Sub
. ويتم اغالق النافذةDialogResult.No تُعاد القيمةCancel إذا ضغط ال ُمستخدم على الزر
106
االستثناءات هنالك عدة أنواع من األخطاء المتعلقة بالبرامج: أخطاء قواعد – :Syntax Errorsوهي عبارة عن أخطاء سببها عدم االلتزام بقواعد اللغة .تظهر هذه األخطاء خالل عملية ترجمة البرنامج أي خالل عملية الـ Compilationولذلك فان الـ Compilerهو المسؤول عنها .تُسمى هذه األخطاء أيضا Compile Time Error أمثلة: األمر
الخطأ النوع Integersغير معروف
Dim x As Integers
الكلمة Thenناقصة
If x Mod 2 = 0 x=x+1 End If
العملية Parsغير معروفة .األمر يجب أن يكون
Dim N, Result As Double =N
)Double.Parse(TextBoxResult.Text
)Double.Pars(TextBoxResult.Text
استثناءات – :Exceptionsوهي عبارة عن أخطاء تظهر خالل عملية تنفيذ البرنامج ولذلك تُسمى أيضا .Run Time Errorيُعتبر هذا النوع من األخطاء من أشد األخطاء خطورة ألنه يحصل زمن تشغيل البرنامج أي عندما يكون البرنامج عند الزبون الذي اشتراه .واألشد من ذلك أن االستثناء إذا حصل ولم يكن المبرمج قد عالجه فان البرنامج يتوقف عن العمل بشكل غير طبيعي – .Unhandled Exceptionsأمثلة لهذه األخطاء: • محاولة التقسيم على Attempted to divide by zero - 0وبشكل أدق تُسمى .OverflowExceptionمثال: Dim x As Integer = 0 Dim y As Integer y=5/x عند التنفيذ تظهر نافذة الخطأ التالية:
107
• االدخال غير صالح Input string was not in a correct formatوبشكل أدق تُسمى .FormatExceptionمعنى ذلك أن البرنامج توقع مثال أن يكون االدخال عددا ولكنه كان مثال نصا غير قابل للتحويل الى عدد .مثال: Dim X As Integer )"X = Integer.Parse("Me
Meليست عددا .عند التنفيذ تظهر نافذة الخطأ التالية:
• محاولة للوصول الى خلية من خاليا مصفوفة برقم غير صالح أي برقم ليس من المجال المسموح به Index was outside the bounds of the arrayوبشكل أدق تُسمى .IndexOutOfRangeExceptionمثال: 108
Dim A(4) As Integer A(5) = 2 عند التنفيذ تظهر نافذة الخطأ التالية:
• محاولة فتح قاعدة بيانات غير موجودة أو ال يمكن الوصول اليها FileNotFoundException • أخطاء قواعد باستعالمات SQLكما سيُبين ذلك الحقا كيف يُمكننا معالجة هذه األخطاء؟ علينا احاطة المقطع الذي نتوقع أن تحدث معه أخطاء خالل تنفيذ البرنامج باألوامر tryوcatch على النحو التالي: Try العمليات التي قد ال ينجح تنفيذها خالل التشغيل Catch ex As Exception اظهار الخطأ للمستخدم بشكل مفهوم أو انهاء عمل العملية أو ... أي بشكل عام معالجة االستثناء أو الخطأ End Try
109
مثال Try Dim X As Integer )"X = Integer.Parse("Me Catch ex As Exception "!LabelMessage.Text = "Error: Me is not a number End Try
:tryأي حاول تنفيذ األوامر التالية :catchأي التقط األخطاء التي أُرسلت أو رُميت من قبل العمليات على شكل كائن من نوع Exceptionباسم .exالحظ أن األمر Catch ex As Exceptionيتضمن تعريفا لمتغير من نوع Exceptionباسم .exوالحقيقة أن هذا الكائن يتم انشاؤه وتجهيزه من قبل العملية التي حصل معها الخطأ .يوفر هذا الكائن الخاصية النصية Messageالتي تحتوي على شرح مفصل عن الخطأ الذي حصل وهو يكون عادة النص الذي يظهر في السطر الثاني في نوافذ األخطاء آنفة الذكر .الحظ أن الفئة Exceptionهي الـ Base Classلجميع فئات االستثناءات الموجودة في VB.NETأي أن الكائن يمر بعملية UpCastingمن فئته الخاصة الى الفئة .Exceptionلكن بإمكاننا أن نكتب أكثر من أمر catchفي نفس السلسلة بحسب األخطاء التي نتوقها (الحقيقة أن كل عملية أو أمر يحدث أو يرمي استثناء - Throws an Exception -خالل زمن التشغيل ،توثق ذلك بشكل واضح):
try العمليات التي قد ال ينجح تنفيذها خالل التشغيل catch ex As System.IO.FileNotFoundException اظهار أو معالجة الخطأ بأن الملف غير موجود catch ex As IndexOutOfRangeException اظهار أو معالجة الخطأ بأن أرقام الخاليا غير صالحة catch ex As DivideByZeroException اظهار أو معالجة الخطأ بأن التقسيم على 0غير ممكن Finally اظهار أو معالجة الخطأ بشكل عام End Try
110
هذه الطريقة تُسهل علينا اظهار نصوص واضحة للمستخدم النهائي ( )End Userبلغة بعيدة عن لغة المبرمجين. يجب التنويه الى أنه بإمكان العمليات التي يكتبها المبرمج بنفسه أيضا رمي استثناءات بواسطة األمر Throwلكننا سنترك دراسة هذا األمر للقارئ لعدم أهميته هنا .الموقع التالي يوفر مادة ال بأس فيها عن الموضوع:
)Creating and Throwing Exceptions (VB.NET Programming Guide https://msdn.microsoft.com/en-us/library/ty79csek.aspx
111
SQL Structured Query Language لغة االستعالم الهيكلية لغة SQLهي لغة ُمستخدمة على نطاق واسع جدًا للوصول للمعلومات (البيانات) ال ُمخزنة في قواعد البيانات االرتباطية أو العالئقية ) (Relational Databasesأي التي تُخزن المعلومات في جداول تربط بينها عالقات معينة .والمقصود بالوصول للمعلومات أي استخراجُها وتحديثُها وحذفُها .هذه األمثلة من العمليات هي على سبيل المثال ال الحصر .وهي تُعطي تفسيرًا لسبب تسمية هذه اللغة بهذا االسم .إال أن هذه التسمية مغلوطة نوعًا ما ألن اللغة قادرة على أكثر من ذلك فهي قادرة على إنشاء وحذف مكونات كائنات قاعدة البيانات من جداول وحقول داخل هذه الجداول وخصائص هذه الحقول كالفهارس أو المفاتيح ( )Indexes, Keysوالعالقات ) . (Relationsإضافة إلى ذلك هي قادرة على تنفيذ مهام إدارية مختلفة لدعم العمليات اليومية التي تتم على قاعدة البيانات .تم تطوير هذه اللغة من قبل المهندسين Donal D. Chamberlinو Raymond F. Boyceمن شركة IBMوذلك في عام .1970وكانت آنذاك تسمى بلغة .SEQUEL مالحظة: من أجل القيام بتجربة وتنفيذ األمثلة التالية عليك القيام بالخطوات التالية التي من شأنها فتح محرر أوامر SQLالتابع لبرنامج أكسس: •
أفتح برنامج أكسس
•
أنشئ الجداول الالزمة والعالقات التي تربطها
•
أمأل الجداول بقيم من اختيارك أو بتلك المبينة في األمثلة
•
اختر بعد ذلك كله الكائن استعالمات (שאילתות) (انظر في الشكل التالي إلى الرقم )1
•
أنقر الزر جديد חדש (انظر في الشكل التالي إلى الرقم )2
•
اختر (יצירת שאילתה בתצוגת עיצוב) (انظر في الشكل التالي إلى الرقم )3
•
اختر في نافذة الحوار التي تظهر(תצוגת עיצוב) ثم انقر الزر אישור (انظر في الشكل التالي إلى الرقم )5 + 4
112
•
أغلق نافذة الحوار التي تظهر(הצגת טבלה) من خالل النقر على الزر סגור (انظر في الشكل التالي إلى الرقم )6
•
أنقر على الزر األيمن للفارة في المنطقة العليا من النافذة التي تظهر (انظر في الشكل التالي إلى الرقم )7ثم أختر من القائمة التي تظهر األمر תצוגת ( SQLانظر في الشكل التالي إلى الرقم )8
• •
ابدأ بكتابة استعالمك في النافذة التي تظهر (انظر في الشكل التالي إلى الرقم )9 لتنفيذ االستعالم أنقر على الزر
في شريط األدوات (انظر في الشكل التالي إلى الرقم
)10 •
عند االنتهاء من تنفيذ وفحص االستعالم بإمكانك حفظه من النقر على الزر األدوات.
113
في شريط
114
115
تتكون لغة SQLكأية لغة من •
مجموعة من الرموز والكلمات المحجوزة والتي لكل واحد وواحدة منها معنى معين.
•
مجموعة من القواعد التي تبين كيفية استعمال الرموز والكلمات المحجوزة لبناء جمل أو أوامر مركبة.
تنقسم أوامر لغة SQLبشكل أساسي إلى عدة أقسام منها: •
االستعالمات ) :)Queriesاألمر SELECT
•
لغة معالجة البيانات ( :)DML = Data Manipulation Languageوهي عبارة عن مجموعة من األوامر لتغيير محتوى الجداول في قاعدة البيانات مثل
•
o
إضافة سجالت جديدة بواسطة األمر INSERT INTO
o
حذف سجالت موجودة في الجداول بواسطة األمر DELETE
o
تحديث سجالت موجودة بواسطة األمر UPDATE
لغة تعريف البيانات ( :)DDL = Data Definition Languageوهي عبارة عن مجموعة من األوامر لتغيير مبنى قاعدة البيانات من خالل إنشاء وتغيير الكائنات المكونة لقاعدة البيانات مثل الجداول والحقول داخل هذه الجداول وخصائص هذه الحقول كالفهارس
116
( )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
117
االستعالمات ))Queries األمر SELECT األمر SELECTبأبسط صوره عند االنتهاء من هذا الدرس عليك أن تكون قادراً على •
شرح قدرات األمر SELECT
•
تنفيذ أمر SELECTبسيط
الستخراج معلومات من قاعدة البيانات علينا أن نستعمل األمر SELECTالتابع للغة .SQLربما تحتاج أيضا ً إلى تحديد النتائج بناءاً على شروط معينة .هذا الدرس يشرح جميع صيغ األمر SQL الالزمة لهذا الغرض. قدرات األمر :SELECT األمر SELECTيسترجع معلومات من قاعدة البيانات .وهو قادر على: •
استخراج اسطر من جدول تحقق شروطا ً معينة وهو ما يسمى باختيار Selection
•
استخراج أعمدة من جدول تحقق شروطا ً معينة وهو ما يسمى باختيار Projection
•
استخراج معلومات مخزنة في أكثر من جدول تربط بينها عالقات أي يشتركون بيتهم بحقل واحد أو أكثر وهو ما يسمى باالتحاد أو الدمج .Join
المبنى العام لألمر : SELECT يكون األمر SELECTفي ابسط صوره مكونا من القسمين ):(Clauses •
: SELECTالذي يحدد الحقول التي نريد إظهار معلوماتها
•
:Fromالذي يحدد الجدول (الجداول) التي تحتوي على الحقول التي حُددت في قسم ألـ SELECT
118
}…]SELECT * | {Column1 [as OtherName1], Column2 [as OtherName2 ;FROM table األقواس ] [ تعني أن الكلمة أي االستعمال غير ملزم الرمز | يعني أو األقواس } { تعني سلسلة أو قائمة شرح :األمر SELECTيبدأ بالكلمة SELECTثم إما أن يأتي الرمز * إلعادة جميع الحقول أو أن تأتي بعد هذه الكلمة سلسلة من أسماء الحقول طولها على األقل حقل واحد .تفصل بين أسماء الحقول فواصل .بإمكاننا أن نحدد اسما خاصا لكل حقل يعطى له في جدول النتيجة .يجب أن ينتهي هذا األمر بالقسم FROMالذي نحدد فيه اسم الجدول (الجداول) الذي يحوي الحقول مالحظات: •
أوامر SQLليست حساسة لحالة الحرف.
•
ليس بالضرورة أن ينتهي األمر في نفس السطر بل يجوز أن يكون مكتوبا ً في أكثر من سطر وذلك لتسهيل قراءة وفهم األمر.
•
ال يجوز قطع أو فصل الكلمات المحجوزة مثل.FROM, SELECT :
•
من المفضل كتابة الكلمات المحجوزة بأحرف كبيرة لتفريقها عن غيرها من الكلمات
أمثلة: اختيار جميع أعمدة الجدول :Friends ;SELECT * FROM Friends هذا األمر يعيد لنا الجدول التالي الذي يحوي جميع أعمدة الجدول .Friendsفالنتيجة تكون متطابقة مع األمر التالي الذي يحدد أسماء جميع الحقول SELECT IDFriend, FName, LName, BirthDay, Address, Mobile ;FROM Friends
119
الحظ انه يجب الفصل بين أسماء الحقول بالفاصلة .ترتيب الحقول هنا يحدد ترتيبهم في جدول النتيجة.
اختيار أعمدة جدول محددة: ;SELECT FName, LName from Friends هذا األمر يعيد لنا الجدول التالي والذي يحوي على الحقلين المطلوبين فقط.
اختيار أعمدة محددة من الجدول وإعطاؤها أسماء جديدة في جدول النتيجة: ;SELECT FName AS [First Name], LName AS [Last Name] from Friends
120
هذا األمر يعيد لنا الجدول التالي والذي يحوي على الحقلين المطلوبين فقط .في جدول النتيجة يظهر االسم First Nameبدال من االسم FNameواالسم Last Nameبدال من االسم .LName الحظ أن •
األسماء الجديدة ُوضعت بين القوسين ][ ألنها تحتوي على فراغات
•
الكلمة ASيجب أن تأتي مباشرة بعد اسم الحقل الذي نريد تغيير اسمه في جدول النتيجة.
•
بإمكاننا إعطاء فقط جزء من الحقول أسماء جديدة
حذف المعلومات المتكررة حذف المعلومات المتكررة من جدول النتيجة يتم بمساعدة الكلمة ( DISTINCTأي مختلف) التي تأتي قبل اسم الحقل. مثال :األمر التالي يعيد لنا جميع األسطر التي تحتوي على عناوين مختلفة:
SELECT DISTINCT Address ;FROM Friends
121
الحظ أن العنوان سخنين أُعيد مرة واحدة مع أنه موجود مرتين.
122
تحديد وترتيب النتائج عند االنتهاء من هذا الدرس عليك أن تكون قادراً على •
تحديد األسطر بناء ً على شروط معينة
•
ترتيب األسطر وفق حقول معينة
عند استخراج معلومات من قاعدة البيانات علينا أحيانا أن نحدد النتائج بناء ً على شروط معينة .هذا ما ينجزه بالضبط القسم WHEREفي األمر :SQL }…]SELECT * | {Column1 [as OtherName1], Column2 [as OtherName2 ;FROM table WHERE Condition بحيث أن Conditionيمثل شرطا ً .الشرط عبارة عن أي تعبير قيمته النهائية إما صدق )(True أو كذب ) .(Falseالتعبير يقارن عادة قيمتين أو أكثر بواسطة عمليات المقارنة .هذه المقارنات قد تكون مرتبطة مع بعضها بواسطة عمليات الربط المنطقي .القيم قد تكون قيم حقول أو قيم ثابتة أو سلسلة من القيم (انظر إلى استعمال الكلمة INالحقا ً). عمليات المقارنة: عملية المقارنة
المعنى
=
يساوي
>
أكبر من
=>
أكبر من أو يساوي
<
أصغر من
=<
أصغر من أو يساوي
><
ال يساوي
123
BETWEEN
أكبر أو يساوي xو
x AND y
أصغر أو يساوي y
)IN (list
يساوي أية قيمة من القائمة
LIKE
يشبه بناء ً على تعبير رسمي يساوي NULL
IS NULL المعامل LIKE
يُمكننا المعامل LIKEمن استعمال رموز خاصة (مثل * و #و؟) في الجزء الخاص بالشرط (أي جزء الـ )WHEREضمن أي أمر .SQLاستعمال هذه الرموز للبحث يُسمى أيضًا بـ Pattern Matchingأو مطابقة النصوص .بامكان هذه الرموز أن تظهر أكثر من مرة في النص المبحوث عنه الذي هو عبارة عن قالب أو .Pattern الجدول التالي يُلخص أهم هذه الرموز المعنى
الرمز ?
يعني أي رمز
*
سلسلة رموز بطول 0أو أكثر
#
رقم واحد فقط من األرقام 0ولغاية 9
الرمز #يعني رق ًما واحدًا فقط .فحيث ما ُو ِجد في التعبير يعني أننا نأخذ القيمة فقط اذا احتوت في األماكن التي فيها الرمز #على رقم أمثلة • األمر التالي يعيد لنا جيمع السجالت الموجودة في الجدول Contactsالتي قيم الحقل MyNameفيها بطول رمزين فقط وتبدأ بالحرف Tوتنتهي برقم (أي مثال T0, T1, T2, )…, T9
124
* SELECT FROM Contacts 'WHERE Contacts.MyName Like 'T# • األمر التالي يعيد لنا جيمع السجالت الموجودة في الجدول Contactsالتي قيم الحقل MyNameفيها بطول 3رموز فقط وتبدأ وتنتهي برقم وتنتهي وبين الرقمين الحرف T (أي مثال )2T0, 0T1, 1T2, …, 8T9 * SELECT FROM Contacts 'WHERE Contacts.MyName Like '#T# الرمز ? يعني رم ًزا واحدًا فقط .فحيث ما ُو ِجد في التعبير يعني أننا نأخذ القيمة فقط إذا احتوت في األماكن التي فيها الرمز ? على رمز مثال • األمر التالي يعيد لنا جيمع السجالت الموجودة في الجدول Contactsالتي قيم الحقل MyNameفيها يبدأ بالعدد 2015وينتهي بأي رمز * SELECT FROM Contacts '?WHERE Contacts.MyName Like '2015 الرمز * يعني سلسلة رموز وقد تكون فارغة مثال • األمر التالي يعيد لنا جيمع السجالت الموجودة في الجدول Contactsالتي قيم الحقل MyNameفيها يبدأ بأي نص ويتنهي بالنص Tester * SELECT FROM Contacts 'WHERE Contacts.MyName Like '*Tester • األمر التالي يعيد لنا جيمع السجالت الموجودة في الجدول Contactsالتي قيم الحقل MyNameفيها يبدأ ويتنهي بأي نص ويحتوي على النص Tester 125
* SELECT FROM Contacts '*WHERE Contacts.MyName Like '*Tester عمليات الربط عملية الربط
المعنى
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 126
* SELECT FROM Friends ;WHERE IDFriend=2
األمر التالي يعيد لنا تفاصيل األصدقاء الذين أرقامهم أكبر أو تساوي 4و أصغر أو يساوي :5
* SELECT FROM Friends ;WHERE IDFriend BETWEEN 4 AND 5 نفس النتيجة يعيدها األمر * SELECT FROM Friends ;)WHERE IDFriend IN (4, 5
انتبه إلى أن نوع القيم يجب أن يكون من نوع الحقل! فمثال األمر التالي يستعمل حقال من نوع نص ولذلك نصوص كقيم وأحيطت بشطائر فردية:
127
SELECT * FROM Friends WHERE Address IN ('Haifa', 'Sakhnin');
أي يعيد لنا تفاصيل األصدقاء الذينOR نفس النتيجة يعيدها األمر التالي من خالل استعمال الرابط :Haifa أو فيSakhnin يسكنون في
SELECT * FROM Friends WHERE Address='Sakhnin' OR Address='Haifa'; :Samra واسم العائلةSami األمر التالي يعيد لنا تفاصيل األصدقاء الذين اسمهم األول SELECT * FROM Friends WHERE FName='Sami' AND LName='Samra';
128
األمر التالي يعيد من خالل استعمال الرابط NOTتفاصيل األصدقاء الذين ال يسكنون في Sakhnin وأيضا ليس في :Haifa
* SELECT FROM Friends ;)'WHERE Address NOT IN ('Sakhnin','Haifa
الحقا ً سنرى أن عناصر القائمة قد تكون نتيجة استعالم SELECTداخلي.
129
ترتيب النتائج ترتيب األسطر في جدول النتيجة يكون غير معروف مسبقا ً .القسم 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
الترتيب قد يكون حسب أكثر من حقل .مثال ً األمر التالي يعيد لنا جميع األصدقاء مرتبين حسب تاريخ ميالدهم من األصغر إلى األكبر وأيضا ً حسب أسماء العائلة مرتبة ترتيبا ً أبجديا ً تنازليا ً:
130
* SELECT FROM Friends ;ORDER BY LName DESC , BirthDay DESC
استخراج معلومات من أكثر من جدول من الصعب جدًا أن نجد أو أن نصمم قاعدة بيانات تخزن المعلومات في جداول منفصلة عن بعضها البعض .وإنما من الطبيعي أن نجد القاعدة مكونة من جداول عدة تربط بين معظمها عالقات .ولذلك سنتعلم اآلن كيفية صياغة أوامر SQLالستخراج المعلومات من أكثر من جدول تربط بينها عالقات مختلفة .هذه العالقات تنشأ عادةً من خالل حقول مفاتيح مشتركة بين هذه الجداول .من المعروف أن العالقات الممكنة ثالث: •
واحد لواحد ))1 1
•
واحد لكثير ))1 N
•
كثير لكثير ))M N
لنأخذ على سبيل المثال الجدولين التاليين (جدول الدول وجدول العواصم) والذين تربط بينهم عالقة واحد لواحد وذلك من خالل اشتراكهما في الحقل .IDCapitalهذا الحقل يكون في الجدول Capitalsمفتاحًا رئيسيًا أي Primary Keyوفي الجدول الثاني أي Countriesيكون مفتاحًا غريبًا أي Foreign Key 131
132
133
اآلن نريد كتابة استعالم يعيد لنا اسم الدول واسم عاصمتها وعدد سكان العاصمة .أي يعيد لنا الجدول التالي:
الحظ أن المعلومات المطلوبة موجودة في جدولين .ولذلك علينا أن نعيد األسطر التي تشترك في قيمة الحقل الرابط .IDCapitalالشكل التالي يوضح ذلك:
األمر التالي يعيد لنا النتيجة المطلوبة:
SELECT Countries.Name, Capitals.Name, Capitals.Inhabitants FROM Capitals, Countries ;WHERE Capitals.IDCapital=Countries.IDCapital
134
الحظ أننا كتبنا قبل كل اسم حقل اسم الجدول الذي يحتويه وذلك للحيلولة دون حصول تعارض بين أسماء الحقول المتساوية. اإلمكانية الثانية للحصول على هذه النتيجة تكون من خالل استعمال األمر INNER JOINوالذي يعني دمج جدولين في جدول نتيجة واحد بنا ًء على حقل مشترك (أي قيم مشتركة): 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أية عملية مقارنة منطقية (أي ><.)=, <,<=,>,>=,
135
مثال لعالقة : 1 N الشكل التالي يبين الجدول طالب Studentsوالجدول صفوف Classesويبين أن العالقة بينهم هي عالقة واحد لكثير .ففي الصف الواحد يتعلم كثير من الطالب .الحقل الواصل هو الحقل .IDClass
نريد كتابة أمر دمج يعيد لنا الجدول التالي الذي يعرض بعض تفاصيل الطالب الموجودة في الجدول Studentsوبعض التفاصيل عن صفه الموجودة في الجدول :Classes
136
األمر التالي يستعين باألمر 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 مثالً لدينا الجدول رسائل Messagesالذي تربطه عالقة واحد لكثير مع جدول الطالب المذكور أعاله .كل طالب يستطيع كتابة رسائل كثيرة في منتدى المدرسة على سبيل المثال .إذا دمجنا الجدولين بواسطة األمر INNER JOINفهذا سيعيد لنا تفاصيل الطالب الذين كتبوا رسائل وتفاصيل الرسائل التي كتبوها .أي أن تفاصيل الطالب الذين لم يشاركوا في المنتدى سوف ال تعاد. كيف نستطيع الحصول أيضًا على تفاصيل الطالب الذين لم يشاركوا في المنتدى؟ هذا هو بالضبط ما يقوم به األمر LEFT JOINالذي يعيد تفاصيل الطالب حتى ولو لم يجد سجالت مناسبة في جدول الرسائل:
137
:محتوى الجدولين
: التاليINNER JOIN األمر SELECT Students.FirstName, Students.LastName, Messages.Message FROM Students INNER JOIN Messages ON Students.IDStudent = Messages.IDStudent;
138
يعيد لنا الجدول التالي الذي يحتوي على تفاصيل الطالب الذين شاركوا في المنتدى:
بينما األمر LEFT JOINالتالي
SELECT Students.FirstName, Students.LastName, Messages.Message FROM Students LEFT JOIN Messages ;ON Students.IDStudent=Messages.IDStudent سيعيد لنا تفاصيل الجميع أي تفاصيل الذين شاركوا والذين لم يشاركوا:
139
الحظ أن األمر أعاد لنا السجالت من الجدول األيسر مع أنه لم يجد سجالت تحقق الشرط في الجدول األيمن. مالحظة: األمر LEFT JOINيسمى أيضًا LEFT OUTER JOINوبإمكاننا كتابة LEFT OUTER JOINبدالً من LEFT JOINونحصل على نفس النتيجة.
األمر RIGHT JOIN من الممكن أن نسأل السؤال التالي :هل يوجد مشاركات في المنتدى ألناس ليسوا طالبًا في المدرسة؟ من الواضح أن وجود مثل هذه السجالت يدل على خطأ في تصميم قاعدة البيانات .ألن وجودها يعني استعمال مفتاح غريب في جدول الرسائل ال أصل له في جدول الطالب .مع أن الخطوة التي نعرف فيها العالقات بين الجداول تفترض أيضا أن نضمن مبدأ ما يسمى بالتكامل المرجعي ( Referential .)Integrityهذا المبدأ يُطلب من مدير قاعدة البيانات ) (DBMS: Data Base Management Systemويضمن: •
عدم السماح بإدخال قيم لمفتاح غريب غير موجودة كقيم للمفتاح الرئيسي.
•
إذا حُذف سجل أب تُحذف كل سجالت االبن المرتبطة به وذلك لكي ال تبقى سجالت غير مرتبطة بشيء في الجدول األصلي.
مع ذلك لو حصل الخطأ كيف يمكننا أن نحصل على هذه السجالت؟ هذا هو بالضبط ما يقوم به األمر .RIGHT JOINفهو يعيد جميع السجالت الموجودة في الجدول األيمن (أي )table2حتى لو لم تحقق الشرط المذكور في األمر وراء الكلمة ONأي حتى ولو لم يجد سجالت مناسبة في الجدول األيسر (أي .)table1 من أجل إعطاء مثال قمت بإيقاف فحص التكامل المرجعي في قاعدة البيانات وأدخلت رسائل من أناس غير موجودين في جدول الطالب ومن ثم نفذت األمر التالي: 140
SELECT Students.FirstName, Students.LastName, Messages.Message FROM Students RIGHT JOIN Messages ;ON Students.IDStudent=Messages.IDStudent وحصلت على الجدول التالي الذي يعرض أيضًا رسالة كاتبها غير موجود في جدول الطالب:
مثال لعالقة : M N الشكل التالي يبين الجدول طالب( ( Studentsوالجدول دروس ( ( Lessosويبين أن العالقة بينهم هي عالقة كثير لكثير .فكل طالب يتعلم أكثر من درس وكل درس يتعلمه أكثر من طالب. معروف أن هذه العالقة تُصمم بمساعدة جدول ثالث ( ) LessonsStudentsتُحفظ فيه المفاتيح الرئيسية كمفاتيح غريبة (اندكسات) مع إمكانية التكرار( .)IDLesson, IDStudent
141
السؤال اآلن هو كيف يمكننا أن نستخرج المعلومات من هذه الجداول الثالث لنحصل على تفاصيل الطالب وتفاصيل الدروس التي يدرسونها؟ أي أن جدول النتيجة يجب أن يعرض من يدرس ماذا كالتالي:
من الجداول التالية:
142
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;
143
األمر الداخلي Lessons INNER JOIN LessonsStudents ON Lessons.IDLesson = LessonsStudents.IDLesson يعيد لنا نتيجة دمج الجدول 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
144
تمرين رقم 1 أكتب استعال ًما يعيد قائمة بأسماء الدروس التي ال يدرسها طالب معين.
األمر SELECT INTO يُستعمل األمر SELECT INTOالستخراج معلومات من جدول موجود وحفظُها في جدول جديد يتم إنشاؤه من قبل األمر .SELECT INTOإذا كان الجدول الجديد موجودًا حينها يتم حذفه ومن ثم إنشاؤه من جديد .يُستعمل هذا األمر عادةً إلنشاء نُسخ عن الجداول وهو ما يُسمى باألرشيفات. المبنى العام لألمر:
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 مثال
145
األمر التالي ينسخ الجدول Lessonsإلى نفس القاعدة لكن يعطيه االسم الجديد :Lessons_BackUp * SELECT INTO Lessons_BackUp FROM Lessons انتبه إلى أنه ال يجوز وجود أكثر من جدول بنفس االسم في نفس قاعدة البيانات .من هنا وجب إعطاء الجدول اس ًما جديدًا مختلفًا عن االسم الموجود.
146
تمرين رقم 2 صمم قاعدة بيانات باسم Courses.mdbإلدارة معلومات حول التسجيل لدورات استكمال فيه الجداول التالية:
العالقات •
بإمكان كل طالب أن يتسجل ألكثر من دورة
•
الدورة الواحدة يتسجل لها أكثر من طالب
الجداول •
الجدول Adminsيحتوي على كلمات المرور وأسماء المستخدمين لمديري موقع التسجيل للدورات
•
الجدول Announcementsيحتوي على آخر األخبار
المطلوب ▪
أعد تصميم الجدولين Studentsو Coursesبحيث تحقق العالقات التي يجب أن تكون بينهم
▪
أمأل جداول قاعدة البيانات بقيم من اختيارك .حاول إدخال بعض القيم بواسطة األمر INSERT INTO
▪
أكتب استعالما يطبع تفاصيل جميع الطالب ولكل طالب تفاصيل الدورات ال ُمسجل فيها. 147
▪
أكتب استعالما يعيد لطالب معين تفاصيل الدورات ال ُمسجل فيها .االستعالم يطلب .ID الطالب عند تشغيل االستعالم.
▪
أكتب استعالما يعيد لدورة معينة تفاصيل الطالب ال ُمسجلين فيها.
▪
أكتب استعالما يطبع تفاصيل جميع الطالب الذين لم يسجلوا ألية دورة.
▪
أكتب استعالما يطبع تفاصيل جميع األطباء الذين ال يعالجون مرضى بعد أي (الذين ال مرضى لهم)
تمرين رقم 3 صمم قاعدة بيانات باسم Hospital.mdbإلدارة معلومات في مستشفى وفق المخطط التالي:
المطلوب ▪
أمأل جداول قاعدة البيانات Hospital.mdbبقيم من اختيارك .حاول إدخال بعض القيم بواسطة األمر INSERT INTO 148
▪
أكتب استعالما يطبع تفاصيل جميع المرضى ولكل مريض تفاصيل األطباء المعالجين
▪
أكتب استعالما يعيد لمريض معين تفاصيل ألطباء المعالجين .االستعالم يطلب IDالمريض عند تشغيل االستعالم.
▪
أكتب استعالما يعيد لطبيب معين تفاصيل المرضى الذين يعالجهم
▪
أكتب استعالما يطبع تفاصيل جميع المرضى الذين ال يعالجون بعد (أي الذين ال طبيب لهم)
▪
أكتب استعالما يطبع تفاصيل جميع األطباء الذين ال يعالجون مرضى بعد أي (الذين ال مرضى لهم)
اإلدخال يتم إدخال سجالت جديدة إلى جدول ما بواسطة األمر INSERT INTO الصيغة العامة لألمر هي كالتالي: (… INSERT INTO TableName )Column1, Column2, ;) … VALUES (ValueOfColumn1, ValueOfColumn2, أي )قائمة بأسماء الحقول التي نريد إدخال قيم إليها( INSERT INTO TableName ;)قائمة بقيم الحقول التي نريد إدخالها( VALUES
• يجب إحاطة قيمة الحقول التي من نوع نص بشطائر منفردة. • يجب إحاطة قيمة الحقول التي من نوع تاريخ /وقت بالرمز .# • إذا وجد في الجدول حقل من نوع Auto Numberال يجوز لنا أن نبقي قائمة أسماء الحقول فارغة ،بل يجب كتابة الحقول التي نريد إدخال قيم إليها باستثناء حقل الـ ( Auto Numberأي ال نكتبه ألن الـ DBMSمسئول عن إعطاء قيم له) .أما في الجدول الذي ال يحتوي على حقل من نوع Auto Numberفبإمكاننا أن نترك قائمة أسماء الحقول فارغة وتزويد قيم لكل الحقول. 149
نريد إدخال سجالت جديدة إلى الجدول.2 معطاة قاعدة البيانات من الفعالية رقم:أمثلة :Courses
:الحل INSERT INTO Courses ([Name], [Hours], [Location], [BeginDate], [EndDate]) VALUES(‘VB’,80,’college’,#1/9/12#,#10/10/12#)
من المفضل إحاطة جميع أسماء الحقول باألقواس ] [ لمنع أي تعارض مع أي أسماء:مالحظة .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
150
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 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 56 112 112 112
وليس من قبلنا ألن الحقل من نوعAccess من قبلCourseID أُعطيت للحل6 الحظ أن القيم .Auto Number
151
التحديث تتم حتلنة سجالت موجودة في جدول ما بواسطة األمر 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
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
أكتب استعالما يحتلن السجل الذي رقمه ( 3أي )CourseID=3بحيث يجعل عدد الساعات 112 بدال من :56
152
الحل 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
بدال56 ) بحيث يجعل عدد الساعاتCourseID=4 (أي4 أكتب استعالما يحتلن السجل الذي رقمه :05/11/2011 لتصبحDate ويغير قيمة الحقل112 من الحل 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
153
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
154
:بعد تنفيذ األمر يصبح محتوى الجدول كالتالي
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
155
الدوال توفر لغة 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 الحل: 156
]SELECT AVG(Hours) AS [Hours Average FROM WorkHours WHERE WorkerID=100 النتيجة:
Hours Average 8.3333333333 مالحظة من المفضل استعمال المعامل ASإلعطاء اسما جديدا لحقل النتيجة وذلك ألن Accessيعطي اسما عشوائيا ال معنى له ) (Expr1000لهذا الحقل اذا لم نستعمل .ASفجدول النتيجة سيكون كالتالي بدون AS
Expr1000 8.3333333333
الدالة ) SUM(Columnتتلقى اسم حقل من نوع Numberوتعيد لنا المجموع الكلي لقيم الحقل ( العمود ) الذي حددناه لها وفقط للسجالت التي ال تكون فارغة. مثال :أكتب استعالما يعيد مجموع الساعات الكلي للعامل الذي رقمه .106
الحل:
157
]SELECT SUM(Hours) AS [Sum Of Hours FROM WorkHours WHERE WorkerID=106 النتيجة:
Sum Of Hours 26
مثال :أكتب استعالما يعيد مجموع الساعات الكلي لجميع العمال يوم .2011/12/21 الحل: ]SELECT SUM(Hours) AS [Sum Of Hours FROM WorkHours WHERE MyDate=#21/12/2011# النتيجة:
Sum Of Hours 19
الدالة ) COUNT(Columnتتلقى اسم حقل وتعيد لنا عدد السجالت التي تحقق شرطا معينا .اذا كتبنا مكان اسم الحقل الرمز * (وبدون شرط) فان الدالة تعيد عدد جميع األسطرالموجودة في الجدول. مالحظة :استعمال هذه الدالة يكون في أغلب األحيان بالصيغة الثانية أي مع الرمز *.
158
مثال :أكتب استعالما يعيد حجم الجدول ( 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# النتيجة:
Num Of Days 3
الدالة ) MAX(Columnتتلقى اسم حقل من نوع Numberأو Date/Timeوتعيد لنا أكبر قيمة لهذا الحقل (العمود) الذي حددناه لها وفقط للسجالت التي ال تكون فارغة. مثال :أكتب استعالما يعيد أكبر عدد ساعات عمله العامل الذي رقمه 100خالل شهر 12سنة .2011
159
الحل: ]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وتعيد لنا أصغر قيمة لهذا الحقل ( العمود ) الذي حددناه لها وفقط للسجالت التي ال تكون فارغة.
160
إنشاء مجموعات في جدول النتيجة اإلضافة Group BYتمكننا من إنشاء مجموعات في جدول النتيجة .لبيان ذلك نفرض أننا نريد استخراج معلومات من جدول الطالب التالي بحيث يُعرض معدل كل طالب في جميع المواضيع حسب رقم الطالب ):(StudentID جدول :Students Mark 80 90 89 88 100 95
StudentID 100 100 100 106 106 106
SubjectID 4 5 6 4 5 6
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
161
الجزء األخير GROUP BY StudentIDيؤدي إلى تقسيم الجدول إلى مجموعات Groupsبناء على اسم الحقل الذي جاء بعد GROUP BYأي .StudentIDبعد هذا التقسيم إلى مجموعات يتم حساب المعدل لكل مجموعة على حدا:
Mark 80 90 89 88 100 95
StudentID 100 100 100 106 106 106
SubjectID 4 5 6 4 5 6
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لكن حينها ال يظهر هذا الحقل في جدول النتيجة .لذلك ألالستعالم التالي يعيد الجدول التالي:
162
SELECT AVG(Mark) AS Average FROM Students GROUP BY StudentID ORDER BY AVG(Mark) DESC
Average 94.333 86.333
163
ADO.NET ActiveX Data Objects
ما هي تقنية Ado.net هي مجموعة من الفئات ( )Classesالمدمجة مع إطار عمل دوت نت (أي الـ DOT NET (Frameworkوالتي تمكننا من الوصول إلى البيانات الموجودة في قاعدة بيانات ما من أجل استخراج المعلومات أو من أجل تعديلها أو حذفها أو إضافة سجالت جديدة إليها. هذه الفئات موجودة ضمن فضاء األسماء System.Data.OleDbولذلك يجب علينا أن نضيف هذا الفضاء إلى التطبيق من خالل األمر Import System.Data. OleDb الشكل التالي يبين لنا ما يسمى بمعمارية الفئات (أي الـ .)ADO.NET Classes Architecture المصدرhttp://msdn.microsoft.com/en-us/library/ff647768.aspx :
نقاط هامة لفهم المكونات الظاهرة بالصورة والتي معظمها أسماء لفئات معينة
164
.1الوصول الى المعلومات الموجودة بأي ملف يقتضي معرفة اسم الملف الكامل أي يشمل مكان وجوده في القرص الصلب للحاسوب وهو ما يسمى بالمسار الكامل ( Full .)Pathثم يتم بعد ذلك فتح الملف لمعالجة المعلومات .الفئة المسؤولة عن فتح قواعد البيانات والتي توفر لنا عمليات مناسبة للقيام بذلك هي الفئة Connectionأو .OleDbConnectionواضح أن االسم يشير الى وظيفة إنشاء اتصال مع قاعدة البيانات. .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هذا الجدول قد يكون فار ًغا ولكن مبناه حتى عندما يكون فار ًغا يكون وفق مبنى االستعالم أي وفق مجموعة الحقول التي طلبنا قيمها. .6الفئة DataSetعبارة عن مجموعة أي Set • من كائنات DataTableأي مجموعة جداول • والعالقات بين الجدوال (ان ُوجدت) ولذلك يمكننا النظر الى كائن من نوع DataSetعلى أنه كل قاعدة البيانات. .7تخزين هذه النتيجة في كائن من نوع DataTableأو DataSetيتم من قبل وسيط ما بين الوسط المادي أي الملف نفسه ) (Physical Fileوالكائن .الفئة التي تقوم بدور هذا الوسيط هي الفئة DataAdapterأو .OleDbDataAdapterتمثل هذه الفئة الجسر الذي يربط بين DataSetوقاعدة البيانات ويدعم أوامر Select - Update - 165
Delete - Insertوبالتالي بإمكانه القيام بعمليات مختلفة على البيانات كما أنه المسؤول عن تحميل أو تعبئة كائن DataSetبالبيانات .الحظ أن عمل هذه الفئة يتم باالتجاهين :من قاعدة البيانات الى الـ DataSetوالعكس.
166
الوصول الى البيانات • يُلخص الجدول التالي األوامر الالزمة لفتح قاعدة البيانات برمجيًا وانشاء كائن اتصال معها. نفضل في هذه المرحلة أن يكون اسم قاعدة البيانات DB.accdbوذلك ألننا سوف نستعمل في التطبيقات قاعدة واحدة فقط .بشكل عام بامكاننا تمرير اسم القاعدة كبارامتر الى العملية التي تفتح القاعدة ونترك ذلك كتمرين اضافي للقارئ. الشرح هنا يتم تجهيز نص االتصال أي الـ
األمر Dim ConnectionString, strDB As String
Connection Stringلقاعدة بيانات
strDB = Application.StartupPath +
من نوع .MS Accessنص االتصال عبارة عن نص يحتوي على معلومات عن قاعدة البيانات وعن
""\App_Data\DB.accdb = ConnectionString "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strDB
ال ُمزود التابع لها .نص االتصال مكون من مجموعة من األزواج التي يفصل بينها الرمز ; كل زوج مكون من اسم وقيمة يفصل بينها الرمز = .الشرح ال ُمفصَّل موجود في الجدول التالي. إنشاء كائن من الفئة OleDbConnectionوتزويده
Dim OleDBConn As OleDbConnection = New )OleDbConnection(ConnectionString
بنص االتصال الذي يحتوي على معلومات عن القاعدة. فتح القاعدة بمساعدة العملية Open
;)(OleDBConn.Open
التابعة للفئة OleDbConnection
167
يحتوي الجدول التالي على شرح ُمفصَّل لمبنى نص االتصال الشرح
القيمة
االسم
MS 2010 هذه القيمة تدل على أن قاعدة البيانات من نوعMicrosoft.ACE.OLEDB.12.0 Provider Access حفظ قاعدة البيانات فيVB.NET من ال ُمعتاد في برامج
. المسار الكامل لقاعدة البياناتData
.App_Data مجلد خاص ضمن مجلدات البرنامج باسم
Source
تعيد لنا مسارApplication.StartupPath الخاصية المجلد أي المكان المخزن فيه ملف التشغيل وهو (خالل App_Data المجلد.Release أوDebug البرمجة) اما يجب أن يكون (في هذه الحالة) مجلدا داخل واحد من Release أوDebug المجلدين
Dbase ضمن الفئةMakeConnection ضعت هذه األوامر في العملية ِ ُو :ادعاء الدخول OleDbConnection تعيد مؤشرًا على كائن االتصال أي كائن من الفئة:ادعاء الخروج Public Class Dbase Public Function MakeConnection() As OleDb.OleDbConnection Dim connectionString, strDB As String strDB = Application.StartupPath + "\App_Data\DB.accdb" connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strDB Dim OleDBConn As OleDbConnection = New OleDbConnection(connectionString) OleDBConn.Open() Return OleDBConn End Function End Class
168
تنفيذ استعالمات أي SELECT Queries ضعت أوامر تنفيذ االستعالمات في العملية SelectFromTableضمن الفئة .Dbaseوتم ُو ِ شرحها سطرًا سطرًا. ادعاء الدخول :نص االستعالم strSql ادعاء الخروج :تنفذ االستعالم على القاعدة وتعيد مؤشرًا على كائن DataTableيحتوي على نتيجة االستعالم Public Function SelectFromTable(ByVal strSql As String) As DataTable انشاء اتصال مع قاعدة البيانات ' Dim c As OleDbConnection )(c = MakeConnection انشاء كائن لتنفيذ أوامر ' SQL Dim comm As New OleDbCommand اعطاؤه نص االستعالم ' comm.CommandText = strSql واعطاؤه كائن االتصال ' comm.Connection = c انشاء كائن لحفظ نتيجة االستعالم ' Dim dt As New DataTable انشاء كائن وسيط مسؤول عن تعبئة جدول التنيجة في الكائن ' dt )Dim da As New OleDbDataAdapter(comm تعبئة جدول التنيجة في الكائن ' dt )da.Fill(dt اغالق االتصال ' )(c.Close اعادة مؤشرعلى كائن ' DataTable Return dt End Function
169
تنفيذ أوامر SQLال تعيد جدول نتيجة DELETE, UPDATE, INSERT INTO ضعت هذه األوامر في العملية ChangeTableضمن الفئة .Dbaseوتم شرحها سطرًا سطرًا. ُو ِ ادعاء الدخول :نص االستعالم strSql ادعاء الخروج :تنفذ االستعالم على القاعدة وتعيد عدد األسطر أو السجالت التي تأثرت من األمر أي أضيفت اذا كان األمر INSERT INTOأو حذفت اذا كان األمر DELETEأو ُع ّدلت اذا كان األمر UPDATE Public Function ChangeTable(ByVal strSql As String) As Integer انشاء اتصال مع قاعدة البيانات ' Dim c As OleDbConnection )(c = MakeConnection Dim comm As New OleDbCommand اعطاؤه نص االستعالم ' comm.CommandText = strSql واعطاؤه كائن االتصال ' comm.Connection = c العملية ' ExecuteNonQuery تُنفذ األمر وتعيد عدد األسطر أو السجالت التي تأثرت من األمر' Dim nAffectedRows As Integer ()nAffectedRows = comm.ExecuteNonQuery اغالق االتصال ' )(c.Close اعادة عدد األسطر أو السجالت ' Return nAffectedRows End Function
170
تنفيذ أوامر SQLتعيد قيمة واحدة ذكرنا فيما سبق في الفصل عن لغة SQLأنها توفر مجموعة من الدوال التي تُعيد قيمة واحدة ) (Scalarمثل الدوال ( .)AVG, SUM, MAX, MIN, COUNTلذلك سنكتب عملية مساعدة ضمن الفئة Dbaseتتلقى نص استالم فيه واحدة من هذه الدوال أو شبيهاتها وتُعيد لنا القيمة الناتجة عن هذا التنفيذ. ضعت هذه األوامر في العملية ExecuteScalarضمن الفئة .Dbaseوتم شرحها سطرًا سطرًا. ُو ِ ادعاء الدخول :نص االستعالم strSql ادعاء الخروج :تنفذ االستعالم على القاعدة وتعيد القيمة الناتجة عن هذا التنفيذ .الحظ أن نوع القيمة ال ُمرجعة Objectأي أنه يمكنها أن تكون من أي نوع ممكن من األنواع التي تدعمها وتوفرها قاعدة البيانات. Public Function ExecuteScalar(ByVal strSql As String) As Object انشاء اتصال مع قاعدة البيانات ' Dim c As OleDbConnection )(c = MakeConnection Dim comm As New OleDbCommand اعطاؤه نص االستعالم ' comm.CommandText = strSql واعطاؤه كائن االتصال ' comm.Connection = c العملية ' ExecuteScalar تُنفذ األمر وتعيد القيمة ' Dim val As Object ()val = comm.ExecuteScalar اغالق االتصال ' )(c.Close اعادة عدد األسطر أو السجالت ' Return val End Function 171
كاملةDbase الفئة Imports System.Data.OleDb Public Class Dbase Public Function MakeConnection() As OleDb.OleDbConnection Dim connectionString, strDB As String strDB = Application.StartupPath + "\App_Data\DB.accdb" connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strDB Dim OleDBConn As OleDbConnection = New OleDbConnection(connectionString) OleDBConn.Open() Return OleDBConn End Function Public Function SelectFromTable(ByVal strSql As String) As DataTable Dim c As OleDbConnection c = MakeConnection() Dim comm As New OleDbCommand comm.CommandText = strSql comm.Connection = c Dim dt As New DataTable Dim da As New OleDbDataAdapter(comm) da.Fill(dt) c.Close() Return dt End Function Public Function ChangeTable(ByVal strSql As String) As Integer Dim c As OleDbConnection c = MakeConnection() Dim comm As New OleDbCommand comm.CommandText = strSql comm.Connection = c Dim nAffectedRows As Integer nAffectedRows = comm.ExecuteNonQuery() c.Close() Return nAffectedRows End Function
172
Public Function ExecuteScalar(ByVal strSql As String) As Object Dim c As OleDbConnection )(c = MakeConnection Dim comm As New OleDbCommand comm.CommandText = strSql comm.Connection = c Dim val As Object )(val = comm.ExecuteScalar )(c.Close Return val End Function End Class
فيما يلي سنقوم بتطوير تطبيقات تستعمل قاعدة بيانات من نوع .MS Accessهذه التطبيقات ستستعمل الفئة Dbaseللوصول الى قاعدة البيانات ومن ثم عرض المعلومات والقيم بتعديلها وحذفها واضافة سجالت جديدة الى الجداول. مالحظة مهمة جدا :في حال حصول أي خطأ في هذه العمليات فانها سوف ترمي استثناءات .ولذلك علينا احاطة الكود الذي يستدعي هذه العمليات بأمر .Try/Catch
173
3-Levels Architecture يتكون التطبيق عادة من عدة مكونات برمجية وهي • النماذج أي الـ Formsوظيفتها عرض البيانات لل ُمستخدم .وتُسمى أيضا بواجهة ال ُمستخدم التخطيطية (GUI – Graphical Use Interface) oأو Presentation Layer • البرمجة (العمليات والفئات ال ُمختلفة) التي تتعامل مع قاعدة البيانات أي التي تفتح القاعدة وتقرأ منها معلومات أو بيانات أو تكتب فيها بيانات وتحتلنها وتُسمى أيضا Application Layer o • قاعدة (أو قواعد) البيانات وتُسمى أيضا Database Layer o لذلك يُفضل مطورو التطبيقات استعمال ما يُسمى معمارية الطبقات الثالث (3-Levels ) .Architectureتفصل هذه المعمارية هذه المكونات الثالث عن بعضها البعض أي أنها تفصل ما بين واجهة االستخدام المعروضة وإجراءات المعالجة في التطبيق ،وقاعدة البيانات .من أهم ايجابيات هذه المعمارية أنها تمكننا من بناء تطبيقات م ِرنة وقابلة إلعادة االستخدام والصيانة .فعند الحاجة إلضافة طبقات أو تعديلها ال توجد حاجة إلعادة كتابة التطبيق بالكامل أو من جديد .وانما يكفي تبديل الطبقة التي تغيرت .فمثال إذا تقرر أن تُغير قاعدة البيانات من مثال MS Accessالى MS SQL Serverفان الطبقة التي تتأثر بالتغيير هي فقط طبقة قاعدة البيانات .إضافة الى ذلك فسوف ال نرى أية أوامر SQLمبعثرة ومتكررة داخل النماذج وانما فقط كائنات من الفئات المناسبة التي تقوم بتنفيذ المهام ال ُمختلفة التي تطلب النماذج تنفيذها على قاعدىة البيانات. لذلك سنعتمد في الفصول التالية ،التي نبدأ فيها باستعمال قاعدة بيانات ،هذه البُنية بحيث نكتب لكل جدول في قاعدة البيانات فئة بلغة VB.NETوبنفس اسم الجدول بعد اضافة الكلمة Classالى بدايتها لتمييزها على أنها فئة خاصة بنا (مثال ClassUsersلجدول باسم .)Usersتوفر هذه الفئة عادةً • خاصيّة لكل حقل من حقول الجدول • وتوفر مجموعة كافية من العمليات البنائية 174
• والعمليات التي تُنفذ معظم أو كل العمليات ال ُمتوقع تنفيذها على الجدول مثل oاسترجاع بيانات ُمعينة (أي )SELECT oاضافة بيانات ُمعينة (أي )INSERT INTO oحذف بيانات ُمعينة (أي )DELETE oتحديث أو حتلنة بيانات ُمعينة (أي )UPDATE oاعادة قيمة واحدة الصورة التالية تُوضح هذه المعمارية
Presentation Layer عرض البيانات
Application Layer برمجة التواصل مع القاعدة
Database Layer
قاعدة البيانات
كمثال على ذلك سنعيد كتابة نموذج الـ Loginالذي ُشرح سابقًا بحيث يتم هذه المرة قراءة المعلومات عن ال ُمستخدم من قاعدة البيانات. أوال سنضيف الفئة Dbaseالى المشروع من خالل الضغط في الـ Solution Explorerعلى الزر األيمن للفارة ثم نختار األمر Classالذي يظهر عند الضغط على األمر Addثم نُدخل اسم الفئة أي االسم .Dbase.vbبعد ذلك نُغلق النافذة بالضغط على الزر .Add 175
اآلن ننسخ عمليات الفئة Dbaseمن الصفحات السابقة ونُضيفها الى الفئة ونحفظ الملف. قاعدة البيانات نضيف الى المشروع قاعدة البيانات DB.accdbوالتي تحتوي فقط على جدول واحد هو الجدول Usersوفق المبنى التالي
ننسخ قاعدة البيانات ونحفظها في المجلد App_Dataالذي علينا أن ننشئه في المجلد Binكما هو مبين كالتالي: MDI_App\MDI_App\Bin\Debug\App_Data
176
بعد ذلك نكتب الفئة ClassUsers.vbالتي تُشكل بالنسبة للتطبيق الـ .Application Layer Public Class ClassUsers لكل حقل خاصيّة أو صفة
Private UserID, UserName, UserPW As String
)Public Sub New(UserName As String, UserPW As String Me.UserName = UserName عملية بنائيّة Me.UserPW = UserPW End Sub Public Function Login() As Boolean عملية تبحث في القاعدة عن ُمستخدم Dim strRet As String صا بالمواصفات المعطاة .العملية تُعيد ن ً "strRet = "Not Found فار ًغا للداللة على نجاح عملية البحث. Try إذا لم تجده تُعيد النص .Not Found Dim strSQL As String أما إذا حصل خطأ ما من نوع استثناء Dim Db As New Dbase فانها تعيد نص الخطأ الذي يوفره الكائن Dim dt As New DataTable .ex "'" Dim strUN = "'" + UserName + "'" Dim strPW = "'" + UserPW + strSQL = "SELECT * FROM Users WHERE UserName=" + strUN strSQL += " AND UserPW=" + strPW صا للداللة على اخترنا أن تعيد الدالة ن ً )dt = Db.SelectFromTable(strSQL النجاح أو الخطأ حتى نُرجع نص الخطأ If dt.Rows.Count = 1 Then ex.Messageللمقطع الذي استدعى "" = strRet هذه العملية وبالتالي يتمكن ال ُمبرمج من End If معرفة مصدر الخطأ. Catch ex As Exception strRet = ex.Message End Try Return strRet End Function End Class
الفئة DataTable نتيجة تنفيذ أي أمر SELECTبواسطة العملية SelectFromTableالتابعة للفئة Dbaseتُعيد لنا دائما كائنًا من نوع DataTableيحتوي على جدول النتيجة أي يحتوي على الجدول الذي حصلنا عليه بعد تنفيذ أمر الـ .SELECTأسطر هذا الجدول موجودة في الخاصيّة Rowsالتابعة لهذا الكائن .هذه الخاصيّة توفر من خالل الخاصيّة Countعدد األسطر الموجودة في جدول النتيجة. يظهر ذلك في الكود عند استعمال األمر dt.Rows.Countلمعرفة وفحص عدد األسطر التي حصلنا عليها .فان كان عددها يساوي 1معنى ذلك أننا عثرنا على هذا ال ُمستخدم وهو حقًا موجود في جدول الـ Usersوبالتالي يُسمح له باستعمال التطبيق .الحقًا سنتعرف على كيفية تمشيط كائن من نوع DataTableوقراءة المعلومات منه. 177
FormLogIn في النموذجLogin التابع للزرClick برمجة معالج الحدث واآلن نعيد كتابة هذا المعالج بحيث نبحث عن ال ُمستخدم في قاعدة البيانات من خالل استعمال الفئة ClassUsers Private Sub ButtonLogIn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonLogIn.Click Dim obj As New ClassUsers(TextBoxUserName.Text, TextBoxPassword.Text) Dim strRet As String ClassUsers من الفئةobj ننشئ الكائن strRet = obj.Login() Login ونستدعي العملية If strRet <> "" Then MessageBox.Show(strRet) Me.DialogResult = DialogResult.No Return End If Me.DialogResult = DialogResult.Yes Me.Close() End Sub
مقارنة بسيطة بين صيغتي هذا المعالج (السابقة وهذه) تُبرز أهمية وصحة هذا المنهج أي منهج .الطبقات الثالث
178
Data Controls سوف نقوم في هذا الفصل بشرح تفاصيل التواصل مع قاعدة البيانات من خالل تطوير تطبيق يستعمل قاعدة بيانات الدارة محتوى دليل تلفونات أو ما يُسمى أيضا دفتر تلفونات.
قاعدة البيانات فيما يلي نُلخص مكونات القاعدة من جداول وعالقات
الجداول الجدول Contactsيحتوي هذا الجدول على تفاصيل األشخاص الموجودين في دفتر التلفونات
الجدول Groupsيحتوي هذا الجدول على تفاصيل المجموعات .يُمكنُنا هذا التطبيق من انشاء مجموعات لألشخاص الموجودين في دفتر التلفونات مثل مجموعة األصدقاء أو مجموعة األقارب أو أوالد الصف وما الى ذلك .الحظ أن الشخص الواحد قد يكون في أكثر من مجموعة وأن المجموعة تحتوي عادة على أكثر من شخص .أي أن العالقة بين هذا الجدول والجدول Contactsهي عالقة كثير لكثير كما سيظهر ذلك الحقًا في ُمخطط العالقات
179
الجدول ContactsGroupsيربط هذا الجدول بين الجدول Contactsوالجدول Groups بعالقة كثير
الجدول :Usersيحتوي هذا الجدول على تفاصيل ال ُمستخدمين للتطبيق
العالقات
180
النموذج الرئيسي تُبين الصور التالية للنموذج الرئيسي أنه يحتوي على 3عناصر قائمة رئيسية وهي • Fileوهي تحتوي بداخلها على 3عناصر Contacts oتحتوي على أوامر الدارة تفاصيل األشخاص Groups oتحتوي على أوامر الدارة تفاصيل المجموعات Close oالغالق وانهاء عمل التطبيق • Logoutللخروج بدون انهاء عمل التطبيق .اسم هذا العنصر بعد الخروج يُصبح Loginكما ُشرح سابقًا • Helpالظهار نموذج يحتوي على شرح لكيفية عمل التطبيق
181
األمر FileContactsAdd ولنبدأ بشرح تفاصيل األمر Addالتابع للقائمة .Contactsوظيفة هذا األمر تمكين ال ُمستخدم من اضافة أشخاص جدد الى دفتر التلفونات .عند اختيار هذا األمر يظهر النموذج التالي
واضح أننا نوفر هنا لكل حقل من حقول الجدول Contactsأداة مناسبة الدخال قيم الى هذا الحقل. باستثناء الحقل ContactIDالذي هو عبارة عن المفتاح الرئيسي للجدول وهو من نوع رقم تلقائي وبالتالي فالمسؤول عن اعطاء قيمة جديدة لكل سجل جديد هو MS Accessنفسه وليس ال ُمستخدم. األزرار • الزر Resetيقوم بتفريغ الصناديق • الزر Closeيُغلق النموذج • الزر Saveيقوم بحفظ تفاصيل الشخص الجديد بعد التأكد من صحة االدخال ومن أنه ال يوجد شخص بالدفتر له نفس رقم الهاتف أو نفس رقم الجوال
182
برمجة معالج الحدث Clickللزر Reset تفريغ الصناديق يتم من خالل اعطاء الخاصيّة Textالتابعة لكل صندوق النص الفارغ كقيمة .الحظ أننا كتبنا عملية خاصة لهذا الغرض باسم ResetControls Private Sub ButtonReset_Click(ByVal sender As System.Object, )ByVal e As System.EventArgs Handles ButtonReset.Click )(ResetControls End Sub )(Private Sub ResetControls "" = TextBoxName.Text "" = TextBoxMobileNr.Text "" = TextBoxPhoneNr.Text "" = TextBoxAddress.Text "" = TextBoxDOB.Text End Sub
برمجة معالج الحدث Clickللزر Save وظيفة هذا الزر حفظ تفاصيل الشخص الجديد بعد التأكد من صحة االدخال ومن أنه ال يوجد شخص بالدفتر له نفس رقم الهاتف أو نفس رقم الجوال. قبل أن نبدأ ببرمجة هذا المعالج علينا توفير الفئة ClassContactsالتي ستكون مسؤولة عن كل عمليات التواصل التي يطلبها النموذج من قاعدة البيانات .كما ذكرنا سابقًا فان هذه الفئة ستكون من مكونات الطبقة .Application Layer الفئة ClassContacts من ال ُمفضل انشاء مجلد جديد ضمن المشروع باسم App_Codeنحفظ فيه جميع الفئات ال ُمكونة لطبقة الـ .Applicationبامكاننا انشاء المجلد الجديد من خالل الضغط في الـ Solution Explorerعلى الزر األيمن للفارة ثم نختار األمر New Folderالذي يظهر عند الضغط على األمر .Addانقل الى هذا المجلد الفئة Dbase.vbآنفة الذكر .الصورة التالية تبين هذا المجلد ومحتواه حتى اآلن 183
برمجة الفئة ClassContacts يُلخص الجدول التالية واجهة هذه الفئة .بعد ذلك سيتم عرض وشرح برمجة الفئة بشكل كامل. الخاصيّة أو العملية
الشرح لكل حقل من حقول الجدول خاصيّة باسمه العمليات البنائية ُكتبت بحسب الحاجة وسهولة االستعمال.
Get/Set تفحص هذه العملية ما إذا كان الشخص موجودًا في الدفتر بنا ًء على أرقام الهواتف .إذا لم يكن موجو ًدا تعيد العملية صا فارغا .خالف ذلك تعيد شرحا ً اما عن الخطأ إذا ن ً حصل خطأ أو شرحً ا بأنه موجود. تعيد هذه العملية تفاصيل الشخص بحسب الـ ContactIDال ُمزودة من خالل الكائن .إذا كان الشخص صا فارغا .خالف غير موجود في الدفتر تعيد العملية ن ً ذلك تعيد شر ًحا اما عن الخطأ إذا حصل خطأ أو شر ًحا بأنه غير موجود. تعيد هذه العملية مصفوفة األشخاص الذين يُشبه اسمهم النص الموجود في MyNameال ُمزود من خالل الكائن. عملية المقارنة تتم من خالل استعمال المعامل LIKE الذي تُوفره لغة .SQL تعيد هذه العملية مصفوفة أشخاص فيها تفاصيل جميع األشخاص الموجودين في الدفتر الذي تُوفره لغة .SQL تعيد هذه العملية كائن DataTableفيه تفاصيل جميع األشخاص الموجودين في الدفتر.
ContactID MyName, MobileNr As String PhoneNr, MyAddress, DateOfBirth As String )(Public Sub New )Public Sub New(ContactID As String Public Sub New(MobileNr As String, )PhoneNr As String Public Sub New(MyName As String, MobileNr As String, PhoneNr As String, )MyAddress As String, DateOfBirth As String … Public Property Public Function Exists() As String
184
Public Function GetByContactID() As String
Public Function GetByName(ByRef strRet As )(String) As ClassContacts
Public Function GetAllAsArray(ByRef strRet )(As String) As ClassContacts Public Function GetAll(ByRef strRet As String) As DataTable
تضيف هذه العملية تفاصيل شخص جديد الى القاعدة وتعيد نصا فارغا إذا نجحت .خالف ذلك تعيد شر ًحا عن الخطأ كنص. تحذف هذه العملية تفاصيل الشخص من القاعدة بحسب الـ ContactIDالتابعة له وتعيد نصا فارغا إذا نجحت. خالف ذلك تعيد شر ًحا عن الخطأ كنص. تقوم هذه العملية بتحديث تفاصيل الشخص من القاعدة بحسب الـ ContactIDالتابعة له وتعيد نصا فارغا إذا نجحت .خالف ذلك تعيد شر ًحا عن الخطأ كنص .الحظ ً مجهزا بجميع القيم الجديدة. أن الكائن يجب أن يكون
Public Function Insert() As String
Public Function Delete() As String
Public Function Update() As String
ال بد من مالحظة أن جميع هذه العمليات • تستعمل Try/Catchللقيام بمهامها تحسبًا ألي خطأ قد يحصل خالل تنفيذ العملية وتشغيل التطبيق. صا يحتوي على شرح عن الخطأ. • تعيد نصًا فارغا للداللة على النجاح أو ن ً • معظم العمليات ،تتوقع تزويد الكائن ،عند انشائه بمساعدة العمليات البنائية ال ُمختلفة ،بقيم لبعض الخصائص حتى تتمكن من القيام بمهامها. Public Class ClassContacts Private m_ContactID, m_MyName, m_MobileNr As String نوفر لكل حقل خاصيّة Private m_PhoneNr, m_MyAddress, m_DateOfBirth As String )(Public Sub New 'The Default Constructor End Sub )Public Sub New(ContactID As String توفر الفئة مجموعة من Me.m_ContactID = ContactID العمليات البنائية بحسب "" = Me.m_MyName الحاجة كما سيتبين ذلك "" = Me.m_MobileNr في االستعماالت التي "" = Me.m_PhoneNr في العمليات "" = Me.m_MyAddress "" = Me.m_DateOfBirth End Sub )Public Sub New(MobileNr As String, PhoneNr As String "" = Me.m_MyName Me.m_MobileNr = MobileNr Me.m_PhoneNr = PhoneNr "" = Me.m_MyAddress "" = Me.m_DateOfBirth End Sub
185
Public Sub New(MyName As String, MobileNr As String, PhoneNr As String, MyAddress As String, DateOfBirth As String) Me.m_MyName = MyName Me.m_MobileNr = MobileNr Me.m_PhoneNr = PhoneNr Me.m_MyAddress = MyAddress Me.m_DateOfBirth = DateOfBirth End Sub Public Property ContactID() As String Get Return m_ContactID End Get Set(ByVal value As String) m_ContactID = value End Set End Property
نوفر لكل حقل أو صفة Private محميّة Property خاصيّة أي Public محميّة
Public Property MyName() As String Get Return m_MyName End Get Set(ByVal value As String) m_MyName = value End Set End Property Public Property MobileNr() As String Get Return m_MobileNr End Get Set(ByVal value As String) m_MobileNr = value End Set End Property Public Property PhoneNr() As String Get Return m_PhoneNr End Get Set(ByVal value As String) m_PhoneNr = value End Set End Property
186
Public Property MyAddress() As String Get Return m_MyAddress End Get Set(ByVal value As String) m_MyAddress = value End Set End Property Public Property DateOfBirth() As String Get Return m_DateOfBirth End Get Set(ByVal value As String) m_DateOfBirth = value End Set End Property Public Function Exists() As String تفحص هذه العملية ما إذا كان الشخص Dim strRet As String .موجودًا في الدفتر بنا ًء على أرقام الهواتف strRet = "" .صا فارغا ً إذا لم يكن موجودًا تعيد العملية ن Try خالف ذلك تعيد شرحا ً اما عن الخطأ إذا Dim strSQL As String .حصل خطأ أو شر ًحا بأنه موجود Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Contacts WHERE MobileNr='{0}' OR PhoneNr='{1}'" تفترض هذه العملية أن ال ُمبرمج زودها strSQL = String.Format(strSQL, m_MobileNr, m_PhoneNr) MobileNr, بقيم للخصائص dt = Db.SelectFromTable(strSQL) يتم التي الهواتف أي ألرقامPhoneNr If dt.Rows.Count = 1 Then تزويد القيم يتم عند انشاء.البحث عنها strRet = "A Contact with this number already exists" .كائن من الفئة الستدعاء العمليّة End If Catch ex As Exception strRet = "Error in ClassContacts.Exists(): " + ex.Message End Try Return strRet End Function
187
تعيد هذه العملية تفاصيل الشخص بحسب الـ إذا. ال ُمزودة من خالل الكائنContactID كان الشخص غير موجود في الدفتر تعيد خالف ذلك تعيد شر ًحا.صا فارغا ً العملية ن اما عن الخطأ إذا حصل خطأ أو شرحً ا بأنه .غير موجود
Public Function GetByContactID() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Contacts WHERE ContactID={0}" strSQL = String.Format(strSQL, m_ContactID) dt = Db.SelectFromTable(strSQL) الحظ أن العملية تقوم بتعبئة جميع If dt.Rows.Count = 1 Then خصائص الكائن الحالي بالقيم التي تم Dim dr As DataRow نقوم.الحصول عليها من قاغدة البيانات سطرا سطراdt هنا بتمشيط الكائن dr = dt.Rows(0) واألستعانة بكائنfor بواسطة حلقة m_ContactID = dr("ContactID").ToString() لحفظ السطر الحالي فيDataRow m_MyName = dr("MyName").ToString() كل دورة لتسهيل عملية الوصول الى قيم m_MyAddress = dr("MyAddress").ToString() الحظ أننا نصل الى قيم كل.الحقول m_PhoneNr = dr("PhoneNr").ToString() ليس عن طريقdr حقل أو خلية في m_DateOfBirth = dr("DateOfBirth").ToString() االندكس وانا عن طريق اسم الحقل كما m_MobileNr = dr("MobileNr").ToString() هذه الطريقة.يظهر في جدول النتيجة strRet = "" أفضل من استعمال االندكس ألنها تجعل End If .الكود أسهل للفهم Catch ex As Exception strRet = "Error in ClassContacts.GetByContactID(): " + ex.Message End Try Return strRet End Function
188
Public Function GetByName(ByRef strRet As String) As ClassContacts() strRet = "" Dim A() As ClassContacts تعيد هذه العملية مصفوفة األشخاص الذين يُشبه ال ُمزود منMyName اسمهم النص الموجود في Try عملية المقارنة تتم من خالل.خالل الكائن Dim strSQL As String . SQL الذي تُوفره لغةLIKE استعمال المعامل Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Contacts WHERE MyName LIKE '%{0}%'" strSQL = String.Format(strSQL, m_MyName) قلنا عندما شرحنا المعامل:تنبيه dt = Db.SelectFromTable(strSQL) بأننا نستعمل الرمز * للداللةLIKE A = New ClassContacts(dt.Rows.Count - 1) {} على سلسلة نصوص بطول أكبر أو Dim i As Integer عند تنفيذ هذا األمر.0 يساوي For i = 0 To dt.Rows.Count - 1 أن يجبADO.NET بمساعدة Dim dr As DataRow بدال من * واال% نستعمل الرمز Dim obj As New ClassContacts() .فاألمر ال يعمل dr = dt.Rows(i) obj.ContactID = dr("ContactID").ToString() obj.MyName = dr("MyName").ToString() الحظ أن العملية تقوم بتعبئة جميع obj.MyAddress = dr("MyAddress").ToString() خصائص الكائن الحالي بالقيم التي تم obj.PhoneNr = dr("PhoneNr").ToString() .الحصول عليها من قاغدة البيانات obj.DateOfBirth = dr("DateOfBirth").ToString() obj.MobileNr = dr("MobileNr").ToString() A(i) = obj Next Return A Catch ex As Exception strRet = "Error in ClassContacts.GetByName(): " + ex.Message End Try Return Nothing End Function
189
Public Function GetAllAsArray(ByRef strRet As String) As ClassContacts() strRet = "" Dim A() As ClassContacts تعيد هذه العملية مصفوفة أشخاص فيها تفاصيل ُجميع األشخاص الموجودين في الدفتر الذي ت وفره Try .SQL لغة Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Contacts ORDER BY [MyName] ASC" dt = Db.SelectFromTable(strSQL) A = New ClassContacts(dt.Rows.Count - 1) {} Dim i As Integer For i = 0 To dt.Rows.Count - 1 Dim dr As DataRow الحظ أن العملية تقوم بتعبئة Dim obj As New ClassContacts() المصفوفة سطرا سطرا بالمعلومات dr = dt.Rows(i) .المأخوذة من القاعدة obj.ContactID = dr("ContactID").ToString() obj.MyName = dr("MyName").ToString() obj.MyAddress = dr("MyAddress").ToString() obj.PhoneNr = dr("PhoneNr").ToString() obj.DateOfBirth = dr("DateOfBirth").ToString() obj.MobileNr = dr("MobileNr").ToString() A(i) = obj Next Return A Catch ex As Exception strRet = "Error in ClassContacts.GetByName(): " + ex.Message End Try Return Nothing End Function Public Function GetAll(ByRef strRet As String) As DataTable strRet = "" Try DataTable تعيد هذه العملية كائن Dim strSQL As String فيه تفاصيل جميع األشخاص Dim Db As New Dbase .الموجودين في الدفتر Dim dt As New DataTable strSQL = "SELECT * FROM Contacts ORDER BY [MyName] ASC" dt = Db.SelectFromTable(strSQL) Return dt Catch ex As Exception strRet = "Error in ClassContacts.GetAll(): " + ex.Message End Try Return Nothing End Function
190
Public Function Insert() As String تضيف هذه العملية تفاصيل شخص جديد الى Dim strRet As String خالف ذلك.القاعدة وتعيد نصا فارغا إذا نجحت strRet = "" .تعيد شر ًحا عن الخطأ كنص Try Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "INSERT INTO Contacts ([MobileNr], [MyName], [PhoneNr], [MyAddress], [DateOfBirth])" strSQL += " VALUES ('{0}','{1}','{2}','{3}','{4}')" strSQL = String.Format(strSQL, m_MobileNr, m_MyName, m_PhoneNr, m_MyAddress, m_DateOfBirth) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to save Contact!" End If Catch ex As Exception strRet = "Error in ClassContacts.Insert(): " + ex.Message End Try Return strRet End Function Public Function Delete() As String تحذف هذه العملية تفاصيل الشخص من القاعدة Dim strRet As String التابعة له وتعيد نصاContactID بحسب الـ strRet = "" خالف ذلك تعيد شر ًحا عن.فارغا إذا نجحت Try .الخطأ كنص Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "DELETE FROM Contacts WHERE [ContactID]={0}" strSQL = String.Format(strSQL, m_ContactID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to delete Contact!" End If Catch ex As Exception strRet = "Error in ClassContacts.Delete(): " + ex.Message End Try Return strRet End Function
191
Public Function Update() As String تقوم هذه العملية بتحديث تفاصيل الشخص من Dim strRet As String التابعة له وتعيدContactID القاعدة بحسب الـ strRet = "" خالف ذلك تعيد شر ًحا عن.نصا فارغا إذا نجحت Try الحظ أن الكائن يجب أن يكون.الخطأ كنص Dim strSQL As String ً .مجهزا بجميع القيم الجديدة Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "UPDATE [Contacts] SET [MobileNr]='{0}', [MyName]='{1}', [PhoneNr]='{2}', [MyAddress]='{3}', [DateOfBirth]='{4}'" strSQL += " WHERE [ContactID]={5}" strSQL = String.Format(strSQL, m_MobileNr, m_MyName, m_PhoneNr, m_MyAddress, m_DateOfBirth, m_ContactID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to update Contact!" End If Catch ex As Exception strRet = "Error in ClassContacts.Update(): " + ex.Message End Try Return strRet End Function End Class
192
ولنعد اآلن الى برمجة النموذج ال ُمعد الضافة تفاصيل جديدة الى الجدول Contactsوبرمجة معالج الحدث Clickللزر Saveوظيفة هذا الزر حفظ تفاصيل الشخص الجديد بعد التأكد من صحة االدخال ومن أنه ال يوجد شخص بالدفتر له نفس رقم الهاتف أو نفس رقم الجوال.
Private Sub ButtonSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSave.Click Try أوال نتأكد من صحة االدخال If Not ValidateData() Then Return بعدها ننشئ كائنًا من الفئة End If ClassContacts Dim obj As New ClassContacts(TextBoxName.Text, TextBoxMobileNr.Text, TextBoxPhoneNr.Text, TextBoxAddress.Text, )TextBoxDOB.Text Dim strRet As String نفحص ما إذا كان هنالك شخص بالجدول وله نفس أرقام )(strRet = obj.Exists التلفونات التي نُريد ادخالها .حينها نُخبر ال ُمستخدم If strRet <> "" Then ونرجع. )MessageBox.Show(strRet Return End If نُدخل التفاصيل الى الجدول ونُخبر ال ُمستخدم بأي خطأ )(strRet = obj.Insert يحصل خالل عملية االدخال If strRet <> "" Then )MessageBox.Show(strRet Return End If نُخبر ال ُمستخدم بنجاح عملية االدخال )"MessageBox.Show("Saved Successfully Catch ex As Exception )MessageBox.Show(ex.Message End Try End Sub
الحظ أن العملية ال تستعمل ُمطلقًا أية أوامر SQLوانما تتعامل مع قاعدة البانات فقط من خالل طبقة الـ Applicationأي فقط من خالل عمليات الفئة ClassContactsوهي • العملية البنائيّة التي تُزود الكائن بجميع التفاصيل التي أدخلها ال ًمستخدم الى الصناديق • العملية Existsالتي تفحص كما بيّنا سابقًا ما إذا كان هنالك شخص بالجدول وله نفس أرقام التلفونات التي نُريد ادخالها. • العملية Insertالتي تُدخل التفاصيل الى الجدول
193
العمليات ال ُمساعدة ال ُمستعملة Private Function ValidateData() As Boolean TextBoxName.Text = TextBoxName.Text.Trim() If TextBoxName.Text = "" Then MessageBox.Show("Name is Missing") TextBoxName.Focus() Return False End If
عملية للتأكد من صحة االدخال لصندوقFocus استدعاء العملية هوTextBoxName النص لوضع مؤشر الكتابة في هذا الصندوق كارشاد لل ُمستخدم ولفت انتباهه وتركيزه على هذه األداة التي تحتوي على محتوى خاطئ
TextBoxMobileNr.Text = TextBoxMobileNr.Text.Trim() If Not IsMobileNr(TextBoxMobileNr.Text) Then MessageBox.Show("Mobile Nr is Missing") TextBoxMobileNr.Focus() Return False End If TextBoxPhoneNr.Text = TextBoxPhoneNr.Text.Trim() If Not IsPhoneNr(TextBoxPhoneNr.Text) Then MessageBox.Show("Phone Nr is Missing") TextBoxPhoneNr.Focus() Return False End If TextBoxAddress.Text = TextBoxAddress.Text.Trim() If TextBoxAddress.Text = "" Then MessageBox.Show("Address is Missing") TextBoxAddress.Focus() Return False End If TextBoxDOB.Text = TextBoxDOB.Text.Trim If Not IsDate(TextBoxDOB.Text) Then MessageBox.Show("Date of birth is Missing") TextBoxDOB.Focus() Return False End If Return True End Function
194
Private Function IsMobileNr(ByVal MobileNr As String) As Boolean Dim i As Integer رقم جوال أيMobileNr إذا كانTrue تعيد If MobileNr.Length <> 10 Then مزودي أرقام ويبدأ بواحدة من أرقام10 مكون من Return False الخدمات المعروفين End If Dim Provider As String = MobileNr.Substring(0, 3) Dim Providers() As String = {"050", "052", "053", "054", "057", "058"} If Not Exists(Providers, Provider) Then Return False End If For i = 3 To MobileNr.Length - 1 If MobileNr(i) < "0" Or MobileNr(i) > "9" Then Return False End If Next Return True End Function Private Function IsPhoneNr(ByVal PhoneNr As String) As Boolean Dim i As Integer رقم تلفون أي مكونPhoneNr إذا كانTrue تعيد If PhoneNr.Length <> 9 Then أرقام ويبدأ بواحدة من أرقام المناطق المعروفة9 من Return False End If Dim Area As String = PhoneNr.Substring(0, 2) Dim Areas() As String = {"02", "03", "04", "08", "09"} If Not Exists(Areas, Area) Then Return False End If For i = 2 To PhoneNr.Length - 1 If PhoneNr(i) < "0" Or PhoneNr(i) > "9" Then Return False End If Next Return True End Function Public Function Exists(A() As String, x As String) As Boolean Dim i As Integer A موجودًا في المصفوفةx إذا كانTrue تعيد For i = 0 To A.Length - 1 If x = A(i) Then Return True End If Next Return False End Function
195
تعيد Trueإذا كان Preمقدمة ُمزود خدمة معروف End Function Public Function IsProvider(Pre As String) As Boolean Dim i As Integer }"Dim Providers() As String = {"050", "052", "053", "054", "057", "058 For i = 0 To Providers.Length - 1 If Pre = Providers(i) Then Return True End If Next Return False End Function
األمر FileContactsDelete نقترح لبرمجة عنصر القائمة Deleteالنموذج التالي
• عند ظهور النموذج تكون القائمة ال ُمنسدلة معبأةً بأسماء جميع األشخاص الموجودين في الجدول Users • لحذف تفاصيل شخص ما ،يقوم ال ُمستخدم باختيار اسم الشخص من القائمة ،ثم يضغط على الزر Delete • معالج الحدث Clickللزر Deleteيتأكد من خالل سؤال ال ُمستخدم ان كان حقُا يريد حذف تفاصيل الشخص ال ُمختار • بعد التأكد يقوم معالج الحدث بحذف التفاصيل من الجدول وتحديث محتوى القائمة أي يقوم بحذف اسم الشخص منها
196
تعبئة القائمة ال ُمنسدلة هنالك أكثر من امكانية لتعبئة القائمة ال ُمنسدلة بأسماء جميع األشخاص الموجودين في الجدول .Usersالطريقة األولى تكمن في تمشيط جدول النتيجة سطرًا سطرًا ثم ادخال كل سطر بالطريقة التي تعرفنا عليها سابقًا .الطريقة التي تهمنا هنا هي الطريقة التي تستعمل مبدأ ما يُسمى "بوصل األدوات بمصادر المعلومات" أو .DataBindingتعتبر DataBindingمنهج أساسي من مناهج ميكروسفت المتعلقة بكيفيات عرض المعلومات والتعامل معها وهو ما يسمى بـ Windows ) DataBinding .Presentation Foundation (WPFعبارة عن عملية وصل األداة (التي تمثل واجهة المستخدم الصُورية أي )Graphical User Interfaceبالكائنات التي تحتوي على المعلومات التي غالبا ما تُؤخذ من قاعدة البيانات .هذا الوصل أو الربط يكون من خالل ما يلي: • اعطاء الكائن الذي يحتوي على المعلومات كقيمة للخاصية ( DataSourceأي مصدر المعلومات) التابعة لألداة التي تدعم هذا المنهج.
البرمجة التعبئة تتم في معالج الحدث Loadالتابع للنموذج بحيث تظهر القائمة وقد ُملِئت بأسماء جميع األشخاص الموجودين في الجدول Users Private Sub FormDeleteContact_Load(ByVal sender As System.Object, )ByVal e As System.EventArgs Handles MyBase.Load )(FillComboBox End Sub )(Private Sub FillComboBox Try )(Dim obj As New ClassContacts هنا يتم انشاء كائن من الفئة ClassContactsثم استدعاء "" = Dim strRet As String العملية GetAllبواسطة هذا الكائن والحصول على جدول Dim dt As DataTable النتيجة داخل الكائن .dt )dt = obj.GetAll(strRet If strRet <> "" Then )MessageBox.Show(strRet توفر األداة ComboBoxالخاصيتين Return DisplayMemberو .ValueMemberتتلقى End If الخاصية DisplayMemberاسم الحقل الذي على "ComboBoxNames.DisplayMember = "MyName األداة أن تعرض قيمه .أما الخاصية ValueMember "ComboBoxNames.ValueMember = "ContactID فانها تتلقى كقيمة اسم الحقل الذي يشكل مفتا ًحا رئيسيا في ComboBoxNames.DataSource = dt الجدول .الهدف من ذلك أن ال ُمستخدم يُفضل التعامل مع Catch ex As Exception األسماء التي يعرفها وليس مع قيم ُمجردة كقيم المفاتيح )MessageBox.Show("Error: " + ex.Message خاصً ة إذا كانت قيما لحقل من نوع رقم تلقائي فانها ال End Try معنى لها بالنسبة لل ُمستخدم .هنا نريد اظهار قيم الحقل اعطاء الكائن dtللخاصية DataSource End Sub MyNameواخفاء قيم المفتاح .ContactID (أي مصدر المعلومات) التابعة لألداة ComboBoxNames 197
Delete التابع للزرClick برمجة معالج الحدث Private Sub ButtonDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonDelete.Click Try If ComboBoxNames.Items.Count = 0 Then نرجع إذا كانت أو أصبحت القائمة ال ُمنسدلة Return .فارغة End If Dim Answer As DialogResult Answer = MessageBox.Show("Are You Sure?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question) If Answer = Windows.Forms.DialogResult.No Then Return End If Dim obj As New ClassContacts(ComboBoxNames.SelectedValue.ToString) Dim strRet As String ClassContacts هنا يتم انشاء كائن من الفئة strRet = obj.Delete() واعطاؤه قيمة المفتاح أي قيمة الـ If strRet <> "" Then يتم الحذف ألن ختار للنص ال ُمValueMember MessageBox.Show(strRet) . Delete ثم نستدعي العملية.بواسطته Return End If MessageBox.Show("Deleted Successfully.") FillComboBox() نقوم بتعبئة القائمة من جديد من أجل أن يتم Catch ex As Exception .حذف االسم الذي حُذف من القاعدة MessageBox.Show(ex.Message) End Try End Sub
198
األمر FileContactsUpdate نقترح لبرمجة عنصر القائمة Updateالنموذج التالي
• عند ظهور النموذج تكون القائمة ال ُمنسدلة معبأةً بأسماء جميع األشخاص الموجودين في الجدول Users • لتحديث تفاصيل شخص ما ،يقوم ال ُمستخدم باختيار اسم الشخص من القائمة .حينها يقوم النموذج بتعبئة صناديق النص المتوفرة في النموذج بتفاصيل الشخص ال ُمتار في القائمة. • معالج الحدث Clickللزر Updateيتأكد من خالل سؤال ال ُمستخدم ان كان حقُا يريد تحديث تفاصيل الشخص ال ُمختار • بعد التأكد يقوم معالج الحدث بتحديث التفاصيل في الجدول وتحديث محتوى القائمة • تعبئة القائمة ال ُمنسدلة بأسماء جميع األشخاص الموجودين في الجدول Usersتتم بنفس الطريقة التي شرحناها عند األمر Deleteوال حاجة العادة الشرح مرة أخرى.
199
Private Sub ComboBoxNames_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBoxNames.SelectedIndexChanged FindContactByID(ComboBoxNames.SelectedValue) End Sub Private Sub FindContactByID(ByVal ContactID) Try Dim obj As New ClassContacts(ContactID) Dim strRet As String strRet = obj.GetByContactID() If strRet <> "" Then MessageBox.Show(strRet) Return End If TextBoxName.Text = obj.MyName TextBoxAddress.Text = obj.MyAddress TextBoxPhoneNr.Text = obj.PhoneNr TextBoxDOB.Text = obj.DateOfBirth TextBoxMobileNr.Text = obj.MobileNr OldMobileNr = obj.MobileNr OldPhoneNr = obj.PhoneNr Catch ex As Exception MessageBox.Show("Error: " + ex.Message) End Try End Sub
200
هنا تتم عملية نقل المعلومات من الكائن الذي جهزته العملية الى صناديقGetByContactID الحظ أننا نحفظ أرقام الهواتف.النص األصلية ألن عملية الحفظ سوف تمنع لكن لو.تخزين نفس األرقام لشخصين غبر ال ُمستخدم تفاصيل أخرى غير األرقام وطلب التخزين فال مانع من .التخزين
Save التابع للزرClick برمجة معالج الحدث Private Sub ButtonSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSave.Click Try If Not ValidateData() Then Return End If Dim db As New Dbase Dim obj As New ClassContacts(TextBoxName.Text, TextBoxMobileNr.Text, TextBoxPhoneNr.Text, TextBoxAddress.Text, TextBoxDOB.Text) Dim strRet As String If OldMobileNr <> TextBoxMobileNr.Text Or OldPhoneNr <> TextBoxPhoneNr.Text Then strRet = obj.Exists() If strRet <> "" Then MessageBox.Show(strRet) TextBoxMobileNr.Focus() Return End If End If obj.ContactID = ComboBoxNames.SelectedValue.ToString strRet = obj.Update() If strRet <> "" Then MessageBox.Show(strRet) Return End If MessageBox.Show("Updated Successfully") Me.Close() Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
201
األمر FileContactsNavigate نقترح لبرمجة عنصر القائمة Navigateالنموذج التالي
الهدف من هذا النموذج هو التنقل بين سجالت دفتر التلفون. • عند ظهور النموذج نقرأ تفاصيل جميع األشخاص الموجودين في الجدول Usersونحظ جدول النتيجة في مصفوفة أشخاص وعلى مستوى النموذج حتى تكون متوفرة لمعالجات الحدث Clickللزرين NextوPrevious • الضغط على الزر Nextيُظهر تفاصيل الشخص التالي (ان ُوجد) كما هو في جدول النتيجة أو في مصفوفة األشخاص • الضغط على الزر Previousيُظهر تفاصيل الشخص السابق (ان ُوجد) كما هو في جدول النتيجة أو في مصفوفة األشخاص
202
البرمجة Public Class FormNavigateThruContacts Private MyContactsArray() As ClassContacts Private CurrentRowIndex As Integer
هنا نُعرفُ على مستوى الفئة مصفوفة MyContactsArray األشخاص CurrentRowIndex واالندكس الحالي .للشخص الذي يتم عرض تفاصيله
Private Sub FormNavigateThruContacts_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Try نقوم خالل تحميل الصفحة بتعبئة Dim obj As New ClassContacts() منMyContactsArray المصفوفة Dim strRet As String = "" قاعدة البيانات بواسطة العملية MyContactsArray = obj.GetAllAsArray(strRet) ثم نعرض بواسطةGetAllAsArray If MyContactsArray.Length >= 1 Then GetDataByRowIndex العملية GetDataByRowIndex(0) تفاصيل الشخص األول في المصفوفة ان Else خالف ذلك نُبلغ ال ُمستخدم بأنه ال.ُوجد MessageBox.Show("No contacts available") .يوجد أشخاص في القاعدة End If Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub Private Sub GetDataByRowIndex(ByVal RowIndex As Integer) Dim obj As New ClassContacts() obj = MyContactsArray(RowIndex) تعرض هذه العملية تفاصيل الشخص الذي CurrentRowIndex = RowIndex .RowIndex رقمه في مصفوفة األشخاص TextBoxName.Text = obj.MyName أي تنقل القيم وتعرضها بواسطة الصناديق TextBoxAddress.Text = obj.MyAddress ضا االندكس ً العملية تحتلن أي.ال ُمعدة لذلك TextBoxPhoneNr.Text = obj.PhoneNr .الحالي TextBoxDOB.Text = obj.DateOfBirth TextBoxMobileNr.Text = obj.MobileNr End Sub Private Sub ButtonNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonNext.Click هنا يتم عرض تفاصيل الشخص التالي في CurrentRowIndex = CurrentRowIndex + 1 العملية خالف ذلك تُبلغ.المصفوفة ان ُو ِجد If CurrentRowIndex < MyContactsArray.Length Then ال ُمستخدم بأن التفاصيل المعروضة حاليا هي GetDataByRowIndex(CurrentRowIndex) .للشخص األخير في المصفوفة Else CurrentRowIndex = CurrentRowIndex - 1 MessageBox.Show("End Reached: No more Contacts!") End If End Sub
203
Private Sub ButtonPrevious_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonPrevious.Click هنا يتم عرض تفاصيل الشخص السابق في CurrentRowIndex = CurrentRowIndex - 1 خالف ذلك تُبلغ العملية.المصفوفة ان ُو ِجد If CurrentRowIndex >= 0 Then هي ال ُمستخدم بأن التفاصيل المعروضة حاليا GetDataByRowIndex(CurrentRowIndex) .للشخص األول في المصفوفة Else CurrentRowIndex = CurrentRowIndex + 1 MessageBox.Show("Begin Reached: No more Contacts!") End If End Sub End Class
204
األمر FileContactsSearch نقترح لبرمجة عنصر القائمة Searchالنموذج التالي
• للبحث عن تفاصيل شخص ما ،على ال ُمستخدم أن يُدخل اسم الشخص أو جز ًء من االسم • البحث يتم بواسطة المعامل LIKEالذي تُوفره لغة .SQL • بعد الضغط على الزر Findتُعرض نتيجة البحث بواسطة األداة ListViewالظاهرة في وسط النموذج تحت العنوان .Search Resultالحظ أن النتيجة قد تكون عبارة عن أكثر من سجل ألن LIKEقد يجد أكثر من سجل يحقق الشبه المطلوب. • يبقى الزر Findغير قابل لالختيار طالما أن الصندوق Nameيكون فار ًغا .أي أننا نتحكم بالزر Findفي معالج الحدث TextChangedالتابع لألداة .TextBoxName
205
Find التابع للزرClick برمجة معالج الحدث Private Sub ButtonFind_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonFind.Click FindContact() End Sub GetByName كما ُذ ِكر سابقًا فان العملية تبحث عن التفاصيل بحسب االسم ال ُمعطى Private Sub FindContact() للكائن أي بحسب االسم الذي أدخله ال ُمستخدم Try البحث يتم.Name الى صندوق التص Dim strRet As String = "" ُ الحظ أن العملية تعيد.LIKE بمساعدة األمر Dim obj As New ClassContacts() النتيجة في مصفوفة أشخاص أي من نوع obj.MyName = TextBoxName.Text .ClassContacts Dim A() As ClassContacts A = obj.GetByName(strRet) If A Is Nothing Then MessageBox.Show(strRet) Return End If Dim Row(4) As String Dim i As Integer For i = 0 To A.Length - 1 obj = A(i) Row(0) = obj.MyName Row(1) = obj.MobileNr Row(2) = obj.PhoneNr Row(3) = obj.MyAddress Row(4) = obj.DateOfBirth
هنا نمشط مصفوفة األشخاص وننقل قيم كل .Row كائن منها الى مصفوفة نصوص باسم
Dim itm As ListViewItem itm = New ListViewItem(Row) ListViewSearchResult.Items.Add(itm)
كسطر جديدRow وأخي ًرا نضيف المصفوفة ListViewSearchResult في الـ
Next Catch ex As Exception MessageBox.Show("Error: " + ex.Message) End Try End Sub
FileContactsShow All األمر التي تُمكنُنا من عرض البيانات على شكلDataGridView لبرمجة هذا األمر سوف نستعمل األداة .فر ُد لهذه األداة فصًال خاصًا ِ ُ ولذلك سن.جدول مع امكانيات كاملة للتحرير
206
األداة DataGridView تعتبر األداة DataGridViewمن أهم األدوات لعرض المعلومات على شكل جدول .واألهم من ذلك أن هذه األداة تمكننا من تحرير المعلومات الموجودة في الجدول أي تمكننا من • حذف هذه المعلومات • اضافة سطر جديد الى الجدول • تحديث معلومات سطر من أسطر الجدول • ترتيب الجدول حسب عامود معين تصاعديا أو تنازليا بل وتمكننا من اضافة أدوات أخرى داخل خاليا الجدول وحسب الحاجة .اضافة الى ذلك فان هذه األداة تدعم ما يسمى بربط األدوات بمصادر المعلومات (أي .)DataBinding وكمثال على كل ذلك سوف نقوم هنا ببرمجة األمر األمر .FileContactsShow All التصميم ال ُمقترح
207
• تعبئة النموذج بتفاصيل األشخاص الموجودة في الجدول Contactsتتم في معالج الحدث Loadالتابع لهذا النموذج وبمساعدة طريقة الـ DataBinding • لحذف سجالت ،نقوم باختيارها ثم الضغط على الزر Delete • ولحفظ أية تغييرات قمنا بها نقوم باختيارها ثم الضغط على الزر Save الجدول قابل للتحرير بشكل كامل .أي أنه بامكاننا • تغيير معطيات السجالت الموجودة بالضبط كما نفعل في مستند اكسل • وأيضا يمكننا اختيا ُر أسطر وطلبُ حذفِها من دفتر التلفونات • وأخيرا بامكاننا اضافة سجل جديد من خالل تعبئة تفاصيله في السطر األخير الذي توفره األداة دائما .فكلما أدخلنا سجال جديدا وفرت األداة سطرا جديدا فارغا الستيعاب معطيات سجل جديد وهكذا. إذا هي أداة ذات قدرات كبيرة للتحرير .للحصول على هذه العمليات كلها وتمكين األداة من توفيرها يجب أ نختار الـ CheckBoxesالتي تظهر في الـ Designوبعد الضغط على السهم الصغير الذي يظهر في الزاوية اليمنى العليا من األداة كما تُبيّن ذلك الصورة التالية
Enable Addingلتوفير السطر الفارغ في نهاية األسطر لتمكين ال ُمستخدم من اضافة سجل جديد Enable Editingلتمكين ال ُمستخدم من تحرير محتوى الخاليا Enable Deletingلتمكين ال ُمستخدم من حذف أسطر يختارها Enable Column Renderingلدعم العمليات Updateو Deleteبمساعدة .DataSource لسنا بحاجة لهذه االمكانية في هذا المثال.
208
التابع للنموذجLoad برمجة معالج الحدث بمساعدة طريقةContacts هنا تتم عملية تعبئة النموذج بتفاصيل األشخاص الموجودة في الجدول DataBinding الـ Private Sub FormShowAll_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load FillGrid() End Sub Private Sub FillGrid() Dim strRet As String = "" Dim obj As New ClassContacts Dim dt As New DataTable dt = obj.GetAll(strRet) If dt Is Nothing Then MessageBox.Show(strRet) Return End If
تفاصيل جميعGetAll تعيد العملية فيContacts األشخاص من الجدول .DataTable كائن من نوع
من خالل اعطاءDatabinding هنا تتم عملية الـ DataGridView التابعة للـDataSource الخاصيّة dt الكائن
'Data Binding DataGridViewContacts.DataSource = dt DataGridViewContacts.Columns("ContactID").Visible = False LabelNumOfRecords.Text += dt.Rows.Count.ToString End Sub
209
بعد ذلك نقوم باخفاء العامود ثم نعرض عدد السجالتContactID بواسطة األداة LabelNumOfRecords
Save التابع للزرClick برمجة معالج الحدث Private Sub ButtonSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSave.Click Try Dim row As Integer Dim strRet As String Dim obj As New ClassContacts() 'For each row For row = 0 To DataGridViewContacts.Rows.Count - 2
.نمر على كل سطر من أسطر األداة الحظ أننا ال نمر على السطر األخير .ألنه سطر فارغ
' ValidateData will fill the object if the validation passed obj = ValidateData(row) بفحصValidateData تقوم العملية ال ُمعطيات وتعيد كائنًا من نوع If obj Is Nothing Then محفوظة فيهClassContacts Return .جميع القيم إذا نجحت عملية الفحص End If .Nothing خالف ذلك يُعاد If obj.ContactID = Nothing Or obj.ContactID = "" Then 'Insert a new contact كيف نعرف هل السطر الحالي strRet = obj.Insert() جديد أم موجود في القاعدة وتم Else تعديله؟ قيمة المفتاح الرئيسي لكل 'Update existing contact Nothing سطر جديد تكون strRet = obj.Update() ولذلك نضيفه بواسطة العملية End If خالف ذلك نقوم بتحديثه.Insert .Update بواسطة العملية If strRet <> "" Then MessageBox.Show(strRet) DataGridViewContacts.Rows(row).Selected = True Return End If Next MessageBox.Show("Succesfully Saved") FillGrid()
.بعد التخزين الناجح لل ُمعطيات نعبئ األداة من جديد
Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
210
Private Function ValidateData(ByVal RowIndex As Integer) As ClassContacts Dim strMyName, strPhoneNr, strMyAddress, strDateOfBirth As String Dim strContactID, strMobileNr As String strContactID = DataGridViewContacts.Rows(RowIndex).Cells("ContactID").Value.ToString() strMobileNr = DataGridViewContacts.Rows(RowIndex).Cells("MobileNr").Value.ToString() strMobileNr = strMobileNr.Trim() If Not IsMobileNr(strMobileNr) Then DataGridViewContacts.Rows(RowIndex).Cells("MobileNr").Selected = True MessageBox.Show("Invalid MobileNr") Return Nothing End If strMyName = DataGridViewContacts.Rows(RowIndex).Cells("MyName").Value.ToString() strMyName = strMyName.Trim() If strMyName = "" Then DataGridViewContacts.Rows(RowIndex).Cells("MyName").Selected = True MessageBox.Show("Invalid Name") Return Nothing End If strPhoneNr = DataGridViewContacts.Rows(RowIndex).Cells("PhoneNr").Value.ToString() strPhoneNr = strPhoneNr.Trim() If Not IsPhoneNr(strPhoneNr) Then DataGridViewContacts.Rows(RowIndex).Cells("PhoneNr").Selected = True MessageBox.Show("Invalid PhoneNr") Return Nothing End If strMyAddress = DataGridViewContacts.Rows(RowIndex).Cells("MyAddress").Value.ToString() strMyAddress = strMyAddress.Trim() If strMyAddress = "" Then DataGridViewContacts.Rows(RowIndex).Cells("MyAddress").Selected = True MessageBox.Show("Invalid MyAddress") Return Nothing End If strDateOfBirth = DataGridViewContacts.Rows(RowIndex).Cells("DateOfBirth").Value.ToString() strDateOfBirth = strDateOfBirth.Trim() If Not IsDate(strDateOfBirth) Then DataGridViewContacts.Rows(RowIndex).Cells("DateOfBirth").Selected = True MessageBox.Show("Invalid DateOfBirth") Return Nothing End If
211
Dim obj As New ClassContacts() obj.ContactID = strContactID obj.MobileNr = strMobileNr obj.MyName = strMyName obj.MyAddress = strMyAddress obj.PhoneNr = strPhoneNr obj.DateOfBirth = strDateOfBirth Return obj End Function
Delete التابع للزرClick برمجة معالج الحدث Private Sub ButtonDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonDelete.Click Try تُعيدDataGridViewContacts.SelectedRows.Count الخاصيّة .عدد األسطر ال ُمختارة If DataGridViewContacts.SelectedRows.Count = 0 Then MessageBox.Show("No Contacts are selected!") Return End If Dim res As DialogResult res = MessageBox.Show("R U Sure?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) If res = Windows.Forms.DialogResult.No Then Return End If Dim row As Integer Dim ContactID, strRet As String Dim obj As New ClassContacts() For row = 0 To DataGridViewContacts.SelectedRows.Count - 1 ContactID = DataGridViewContacts.SelectedRows(row).Cells("ContactID").Value.ToString() If IsNumeric(ContactID) Then ال حاجة لحذف األسطر التي تم تحريرها لكنها لم تُحفظ obj.ContactID = ContactID التابعة لهاContactID أي أن قيمة.في القاعدة بعد strRet = obj.Delete() هذه األسطر إذا.Nothing ليست عددًا ألنها تكون If strRet <> "" Then كانت ُمختارة للحذف فانها ستختفي بعد القيام بتعبئة MessageBox.Show(strRet) .FillGrid األداة من جديد من خالل استدعاء Return End If End If Next MessageBox.Show("Deleted successfully") FillGrid() Catch ex As Exception MessageBox.Show(ex.Message)
212
End Try End Sub
برمجة معالج الحدث KeyDownالتابع لألداة DataGridView ماذا سيحصل لو اخترنا أسطرًا ُمعينة في الـ DataGridViewثم ضغطنا على المفتاح Delete الموجود على لوحة المفاتيح؟ ستالحظ أن األسطر اختفت من األداة لكنها لم تُحذف من القاعدة .وذلك ألننا لم نقم ببرمجة عملية الحذف كرد فعل على ضغط هذا المفتاح .اختفاء األسطر تم انجازه من قبل األداة ألننا اخترنا Enable Deletingلتمكين ال ُمستخدم من حذف أسطر يختارها .ماذا علينا أن نجز حذف السجالت ال ُمختارة أيضًا من القاعدة؟ علينا أن نكتب معالجًا للحدث نفعل حتى نُ ِ KeyDownالتابع لألداة .DataGridViewهذا الحدث عبارة عن حدث من األحداث ال ُمتعلقة بلوحة المفاتيح .إذا ضغط ال ُمستخدم على أي مفتاح من لوحة المفاتيح ،واألداة التي تملك الـ Focus هي األداة ،DataGridViewحينها تُرسل األداة هذا الحدث وبالتالي بامكاننا كتابة معالج لهذا الحدث يقوم بالمطلوب وهو حذف السجالت من القاعدة. البرمجة الحظ أن البارمتر eيحتوي في الخاصيّة KeyCodeعلى كود المفتاح الذي تم الضغط عليه من قبل ال ُمستخدم. إذا كان هذا الكود للمفتاح ( Keys.Deleteقيمة ثابتة ُمعرفة ضمن )VB.NETحينها نقوم باستدعاء المعالج ButtonDelete_Clickالمشروح أعاله ليقوم بحذف السجالت ال ُمختارة بعد القيام بالفحوصات الالزمة .القيم ال ُممرة لهذا االستدعاء هي senderو Nothingوكان بامكاننا تمرير Nothingللبارامترين ألننا ال نستعملهم في المعالج .ButtonDelete_Click
Private Sub DataGridViewContacts_KeyDown(sender As System.Object, e As System.Windows.Forms.KeyEventArgs) Handles DataGridViewContacts.KeyDown If e.KeyCode = Keys.Delete Then )ButtonDelete_Click(sender, Nothing End If End Sub
213
FileGroupsAdd األمر اقتصرنا في التصميم التالي على ادخال االسم.نستعمل هذا األمر الضافة مجموعة أشخاص جديدة .ونترك توسيع النموذج الدخال باقي خصائص المجموعة كتمرين للقارئ
. قابال للضغط عليهSave يُصبح الزرName بعد ادخال اسم المجموعة الى الصندوق
Save للزرClick برمجة معالج الحدث Private Sub ButtonSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSave.Click Try Dim obj As New ClassGroups() تُعيد تاريخDateTime.Now obj.GroupName = TextBoxGroupName.Text .وساعة انشاء المجموعة الجديدة obj.CreationDate = DateTime.Now.ToString() Dim strRet As String ال يُسمح ادخال أكثر من مجموعة strRet = obj.Exists() .بنفس االسم If strRet <> "" Then MessageBox.Show(strRet) Return End If هنا يتم ادخال المجموعة الجديدة strRet = obj.Insert() .الى القاعدة If strRet <> "" Then MessageBox.Show(strRet) Return End If MessageBox.Show("Successfully Saved") Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
214
ClassGroups برمجة الفئة Public Class ClassGroups Private m_GroupID, m_GroupName As String Private m_Description, m_CreationDate As String Public Sub New() 'The Default Constructor Me.m_GroupID = "" Me.m_GroupName = "" Me.m_Description = "" Me.m_CreationDate = "" End Sub Public Sub New(GroupID As String) Me.m_GroupID = GroupID Me.m_GroupName = "" Me.m_Description = "" Me.m_CreationDate = "" End Sub Public Sub New(GroupName As String, Description As String, CreationDate As String) Me.m_GroupName = GroupName Me.m_Description = Description Me.m_CreationDate = CreationDate End Sub Public Property GroupID() As String Get Return m_GroupID End Get Set(ByVal value As String) m_GroupID = value End Set End Property Public Property GroupName() As String Get Return m_GroupName End Get Set(ByVal value As String) m_GroupName = value End Set End Property Public Property Description() As String Get Return m_Description End Get Set(ByVal value As String) m_Description = value End Set End Property 215
Public Property CreationDate() As String Get Return m_CreationDate End Get Set(ByVal value As String) m_CreationDate = value End Set End Property Public Function Exists() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Groups WHERE GroupName='{0}'" strSQL = String.Format(strSQL, m_GroupName) dt = Db.SelectFromTable(strSQL) If dt.Rows.Count = 1 Then strRet = "A Group with this Name already exists" End If Catch ex As Exception strRet = "Error in ClassGroups.Exists(): " + ex.Message End Try Return strRet End Function Public Function GetByGroupID() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Groups WHERE GroupID={0}" strSQL = String.Format(strSQL, m_GroupID) dt = Db.SelectFromTable(strSQL) If dt.Rows.Count = 1 Then Dim dr As DataRow dr = dt.Rows(0) m_GroupID = dr("GroupID").ToString() m_GroupName = dr("GroupName").ToString() m_Description = dr("Description").ToString() m_CreationDate = dr("CreationDate").ToString() strRet = "" End If Catch ex As Exception
216
strRet = "Error in ClassGroups.GetByGroupID(): " + ex.Message End Try Return strRet End Function Public Function GetByName(ByRef strRet As String) As ClassGroups() strRet = "" Dim A() As ClassGroups Try Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Groups WHERE GroupName LIKE '%{0}%'" strSQL = String.Format(strSQL, m_GroupName) dt = Db.SelectFromTable(strSQL) A = New ClassGroups(dt.Rows.Count - 1) {} Dim i As Integer For i = 0 To dt.Rows.Count - 1 Dim dr As DataRow Dim obj As New ClassGroups() dr = dt.Rows(i) obj.GroupID = dr("GroupID").ToString() obj.GroupName = dr("GroupName").ToString() obj.Description = dr("Description").ToString() obj.CreationDate = dr("CreationDate").ToString() A(i) = obj Next Return A Catch ex As Exception strRet = "Error in ClassGroups.GetByName(): " + ex.Message End Try Return Nothing End Function Public Function GetAll(ByRef strRet As String) As DataTable strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim dt As New DataTable strSQL = "SELECT * FROM Groups ORDER BY [GroupName] ASC" dt = Db.SelectFromTable(strSQL) Return dt Catch ex As Exception strRet = "Error in ClassGroups.GetAll(): " + ex.Message End Try Return Nothing End Function
217
Public Function Insert() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "INSERT INTO Groups ([GroupName], [Description], [CreationDate])" strSQL += " VALUES ('{0}','{1}','{2}')" strSQL = String.Format(strSQL, m_GroupName, m_Description, m_CreationDate) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to save Group!" End If Catch ex As Exception strRet = "Error in ClassGroups.Insert(): " + ex.Message End Try Return strRet End Function Public Function Delete() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "DELETE FROM Groups WHERE [GroupID]={0}" strSQL = String.Format(strSQL, m_GroupID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to delete Group!" End If Catch ex As Exception strRet = "Error in ClassGroups.Delete(): " + ex.Message End Try Return strRet End Function
218
Public Function Update() As String Dim strRet As String strRet = "" Try Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "UPDATE [Groups] SET [GroupName]='{0}', [Description]='{1}', [CreationDate]='{2}'" strSQL += " WHERE [GroupID]={3}" strSQL = String.Format(strSQL, m_GroupName, m_Description, m_CreationDate, m_GroupID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to update Group!" End If Catch ex As Exception strRet = "Error in ClassGroups.Update(): " + ex.Message End Try Return strRet End Function End Class
219
تمارين المطلوب برمجة األمر FileGroupsShow Allبحسب التصميم التالي
• تعبئة النموذج بتفاصيل المجموعات الموجودة في الجدول Groupsتتم في معالج الحدث Loadالتابع لهذا النموذج وبمساعدة طريقة الـ DataBinding • على الجدول أن يكون قابال للتحرير بشكل كامل Enable Adding o Enable Editing o Enable Deleting o • لحذف سجالت ،نقوم باختيارها ثم الضغط على الزر Deleteأو ال ُمفتاح Delete • ولحفظ أية تغييرات قمنا بها ،نقوم باختيارها ثم الضغط على الزر Save • يجب اخفاء العامود GroupID • يجب التأكد من صحة االدخال قبل الحفظ .أي oال يجوز للقيم أن تكون فارغة oالتاريخ ال ُمدخل يجب أن يكون من الحيث المبنى صحيحًا (.)IsDate
220
برمجة األمر FileGroupsAssign يُمكنُنا هذا األمر من تكوين مجموعات خاصة من األشخاص .أي أنه يُمكنُنا من اضافة تفاصيل أشخاص معينين الى مجموعات خاصة .هذه المجموعات يتم انشاؤها بمساعدة النماذج التي ُش ِرحت سابقًا .مهمة هذا األمر هي فقط ادخال أشخاص الى المجموعات أو اخراجهم منها .الحظ أننا أمام عالقة كثير لكثير بين الجدولين Contactsو .Groupsألن الشخص الواحد قد يُضاف الى أكثر من مجموعة ،والمجموعة الواحدة قد تحتوي بطبيعة الحال أكثر من شخص. التصميم ال ُمقترح
• تعبئة األداة ComboBoxبأسماء المجموعات الموجودة في الجدول Groupsتتم في معالج الحدث Loadالتابع لهذا النموذج وبمساعدة طريقة الـ .DataBindingالعامود الخفي سوف يحتوي على الـ .GroupIDأما العامود الظاهر فسوف يعرض اسم المجموعة. • لدينا في النموذج أداتا ListBox oواحدة مكتوب فوقها .All Contactsتعرض أسماء األشخاص غير الموجودين في المجموعة ال ُمختارة في الـ ComboBox oوأخرى مكتوب فوقها .Contacts in the groupتعرض أسماء األشخاص الموجودين في المجموعة ال ُمختارة في الـ ComboBox
221
نقوم باختيار المجموعة ثم اختيار األشخاص ثم الضغط،• الدخال أشخاص الى مجموعة ما Add على الزر نقوم باختيار المجموعة ثم اختيار األشخاص ثم الضغط،• الخراج أشخاص من مجموعة ما Remove على الزر ClassGroups برمجة الفئة Public Class ClassContactsGroups Private m_ID, m_GroupID, m_ContactID As String Public Sub New() End Sub Public Property ID() As String Get Return m_ID End Get Set(ByVal value As String) m_ID = value End Set End Property Public Property GroupID() As String Get Return m_GroupID End Get Set(ByVal value As String) m_GroupID = value End Set End Property Public Property ContactID() As String Get Return m_ContactID End Get Set(ByVal value As String) m_ContactID = value End Set End Property
222
Public Function GetAllNotInGroup(ByRef strRet As String) As DataTable strRet = "" Try ألننا نريد األشخاصNOT مع النفيIN الحظ استعمال المعامل Dim strSQL As String ( المجموعة ال ُمعطاة من خاللNOT IN) الذين ليسوا في Dim Db As New Dbase ContactID Dim dt As New DataTable strSQL = "SELECT ContactID, MyName FROM Contacts WHERE ContactID NOT IN " strSQL += " (SELECT ContactID FROM ContactsGroups WHERE GroupID=" + m_GroupID.ToString strSQL += " ) " dt = Db.SelectFromTable(strSQL) Return dt Catch ex As Exception strRet = "Error in ClassContactsGroups.GetAllNotInGroup(): " + ex.Message End Try Return Nothing End Function Public Function GetAllInGroup(ByRef strRet As String) As DataTable strRet = "" Try Dim strSQL As String ( المجموعةIN) هنا نُريد أن نحصل على األشخاص الذين في Dim Db As New Dbase ContactID ال ُمعطاة من خالل Dim dt As New DataTable strSQL = "SELECT ContactID, MyName FROM Contacts WHERE ContactID IN " strSQL += " (SELECT ContactID FROM ContactsGroups WHERE GroupID=" + m_GroupID.ToString strSQL += " ) " dt = Db.SelectFromTable(strSQL) Return dt Catch ex As Exception strRet = "Error in ClassContactsGroups.GetAllNotInGroup(): " + ex.Message End Try Return Nothing End Function
223
Public Function Insert() As String Dim strRet As String strRet = "" التفاصيل يجب أن تكون.عملية الدخال تفاصيل مجموعة جديدة Try .ُمعطاة للكائن عبر خصائصه Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "INSERT INTO ContactsGroups ([GroupID], [ContactID])" strSQL += " VALUES ({0},{1})" strSQL = String.Format(strSQL, m_GroupID, m_ContactID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to save Group Assignment!" End If Catch ex As Exception strRet = "Error in ContactsGroups.Insert(): " + ex.Message End Try Return strRet End Function Public Function Delete() As String Dim strRet As String عملية لحذف تفاصيل المجموعة من الجدول التي رقمها معطى strRet = "" .ID للخاصيّة Try Dim strSQL As String Dim Db As New Dbase Dim nAffectedRows As Integer strSQL = "DELETE FROM ContactsGroups WHERE [ID]={0}" strSQL = String.Format(strSQL, m_ID) nAffectedRows = Db.ChangeTable(strSQL) If nAffectedRows = 0 Then strRet = "Failed to delete Group Assignment!" End If Catch ex As Exception strRet = "Error in ContactsGroups.Delete(): " + ex.Message End Try Return strRet End Function End Class
224
البرمجة Private Sub FormAssignContactsToGroups_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load FillComboBoxGroups() End Sub Private Sub FillComboBoxGroups() Try Dim obj As New ClassGroups() Dim strRet As String = "" بأسماء المجموعات الموجودة فيComboBox عملية لتعبئة األداة Dim dt As DataTable سوف يحتويValueMember العامود الخفي.Groups الجدول dt = obj.GetAll(strRet) فسوف DisplayMember أما العامود الظاهر.GroupID على الـ If dt Is Nothing Then .GroupName يعرض اسم المجموعة أي MessageBox.Show(strRet) Return End If ComboBoxGroups.DisplayMember = "GroupName" ComboBoxGroups.ValueMember = "GroupID" ComboBoxGroups.DataSource = dt Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub Private Sub ComboBoxGroups_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBoxGroups.SelectedIndexChanged GetAllContactsNotInGroup() GetAllContactsInGroup() End Sub
225
Private Sub GetAllContactsNotInGroup() Try
(NOT عملية لعرض جميع األشخاص الذين ليسوا في ContactID المجموعة ال ُمعطاة من خاللIN)
If ComboBoxGroups.Items.Count = 0 Then Return End If والغاء مصدرListBoxLeft هنا يتم تفريغ القائمة ListBoxLeft.DataSource = Nothing معلوماتها أي
ListBoxLeft.DataSource = Nothing ListBoxLeft.Items.Clear()
Dim obj As New ClassContactsGroups() obj.GroupID = ComboBoxGroups.SelectedValue.ToString Dim dt As New DataTable Dim strRet As String = "" dt = obj.GetAllNotInGroup(strRet) If dt Is Nothing Then MessageBox.Show(strRet) Return End If سوف يحتوي علىValueMember العامود الخفي ListBoxLeft.DisplayMember = "MyName" أما العامود الظاهر.ContactID الـ ListBoxLeft.ValueMember = "ContactID" فسوف يعرض اسم الشخص أيDisplayMember ListBoxLeft.DataSource = dt .MyName Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
226
Private Sub GetAllContactsInGroup()
( المجموعةIN) عملية لعرض جميع األشخاص الذين في ContactID ال ُمعطاة من خالل
Try If ComboBoxGroups.Items.Count = 0 Then Return End If ListBoxRight.DataSource = Nothing ListBoxRight.Items.Clear() Dim obj As New ClassContactsGroups() obj.GroupID = ComboBoxGroups.SelectedValue.ToString Dim dt As New DataTable Dim strRet As String = "" dt = obj.GetAllInGroup(strRet) If dt Is Nothing Then MessageBox.Show(strRet) Return End If ListBoxRight.DisplayMember = "MyName" ListBoxRight.ValueMember = "ContactID" ListBoxRight.DataSource = dt Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
227
تمارين تمرين رقم 1 المطلوب استبدال أداتي DataGridViewفي النموذج التابع لألمر Assignبأداتي الـ ListBox الموجودتين في النموذج بحيث نظهر جميع تفاصيل األشخاص وليس فقط االسم كما هو األمر اآلن.
تمرين رقم 2 English to Arabic Dictionary المطلوب عليكم برمجة جميع األجزاء المطلوبة إلدارة قاموس انجليزي عربي األجزاء المطلوبة • عرض محتوى القاموس في صفحة خاصة • حذف كلمات موجودة في القاموس • إضافة كلمات جديدة الى القاموس • تعديل أو تحديث كلمات موجودة في القاموس • مبنى القائمة الرئيسية oالعنصر Editيعرض جميع الكلمات بواسطة DataGridViewقابل للتحرير بشكل كامل (اضافة وخذف وتعديل) .هذا العنصر ال يكون قابال لالختيار اال إذا كان ال ُمستخدم .LoggedIn oالعنصر Searchيوفر صندوق نص للبحث عن معاني كلمة ما .البحث يتم بواسطة المعامل .LIKEعرض النتائج يتم بواسطة DataGridViewغير قابل للتحرير. ▪ ال يجوز البحث عن كلمات فارغ. ▪ يتم ابالغ ال ُمستخدم بأن الكلمة غير موجودة في القاموس في حال لم يُعثر عليها أو على كلمات شبيهة بها.
228
oالعنصر Aboutيُظهر نموذجًا فيه معلومات عن التطبيق بتصميم من اختياركم. oالعنصر Loginكما تمت برمجته في األمثلة ال ُمختلفة. oالعنصر Trainيُظهر نموذجًا فيه ▪ زر Get Random Wordيختار كلمة من الجدول Dictionary بشكل عشوائي و 3معان عشوائية خطأ .تُعرض الكلمة بواسطة Label مناسب .ثم تُعرض المعاني بواسطة RadioButtonلكل معنى. ▪ زر Check My Answerيفحص االجابة ويُبلغ ال ُمستخدم بواسطة MessageBoxعن النتيجة. ▪ يعُد ويعرض بواسطة Labelمناسب كم سؤاال أجيب عليه بشكل صحيح من العدد االجمالي لألسئلة التي أُنشئت .هذه االحصائية تُعرض دائ ًما في النموذج. ▪ من ال ُمفضل أن يتم في معالج الحدث Loadالتابع للنموذج اختيار مثال 100كلمة مع تفاصيلها بشكل عشوائي وحفظ النتيجة بمصفوفة كائنات من نوع ClassWordsثم اختيار الكلمات من هذه المصفوفة كلما ضغط ال ُمستخدم على الزر ( Get Random Wordكمثال على ذلك راجع كيفية برمجة األمر FileContactsNavigateالمشروح أعاله). الختيار سجالت بشكل عشوائي بامكانكم االستعانة باألمر التالي
*SELECT TOP N TableName. ]FROM [TableName )]ORDER BY RND([PKFiledName أي اختر أول )( N (TOPعدد طبيعي) سجل (كل الحقول) من الجدول TableNameورتبهم بشكل عشوائي ) (Randomبحسب حقل ال ُمفتاح الرئيسي .هذا األمر سيعيد لنا Nسجال مختارا بشكل عشوائي.
229
*SELECT TOP 3 Contacts. FROM Contacts )]ORDER BY RND([ContactID هذا األمر سيعيد لنا من الجدول 3 Contactsسجالت مختارة بشكل عشوائي بحسب المفتاح .ContactID *SELECT TOP 1 Words. FROM Words )]ORDER BY RND([WordID هذا األمر سيعيد لنا من الجدول Wordsكلمة واحدة مختارة بشكل عشوائي بحسب المفتاح .WordID قاعدة البيانات يجب إضافة الجدول Usersمن األمثلة السابقة إلى القاعدة يجب إضافة الجدول Wordsإلى القاعدة وفيه الحقول التالية: اسم الحقل WordID
شرح
نوع الحقل
Auto Number PKرقم أوحد لكل كلمة :مفتاح رئيسي
String, Index (No duplicates) Wordالكلمة Meanings
Stringمعاني الكلمة
230
تمرين رقم 3 المطلوب برمجة امكانيات لتحرير ال ُمستخدمين الموجودين في الجدول Usersبواسطة .DataGridView نضيف لهذا الغرض الى القائمة الرئيسية األمر FileUsers عند اختيار هذا األمر تظهر النافذة التالية
الحظ أننا أضفنا حقال جديدًا الى الجدول باسم IsAminمن نوع Yes/Noوسنعرضه على شكل CheckBoxفي الـ .DataGridViewاألداة DataGridViewتقوم خالل عملية الـ DataBindingبتجهيز كل سطر بأداة عرض مناسبة بنا ًء على نوع الحقل كما هو ُمخزن في كائن الـ DataTableالذي أعطي للخاصية DataSourceالتابعة لألداة .DataGridViewفمثال األنواع الرقمية والنصوص والتواريخ تُعرض كما رأينا بواسطة األداة TextBoxوأما النوع Yes/Noفانه يُعرض بواسطة .CheckBoxكل هذا بدون أي تدخل من قبل المبرمج. عند اختيار األمر FileUsersتظهر النافذة التالية
231
على األداة DataGridViewأن تكون قابلة للتحرير بشكل كامل وبمساعدة الزرين Saveو .Delete الدخال قيمة لحقل من نوع Yes/Noنُدخل القيمة Trueللقيمة Yesوالقيمة Falseللقيمة .No
أمثلة UPDATE [Users] SET [UserName]='Khalil', [UserPW]='01123', [IsAdmin]=True WHERE [UserID]=1
تمرين رقم 4 المطلوب عليكم برمجة جميع األجزاء المطلوبة إلدارة مركز استكماالت بسيط وفق ال ُمعطيات التالية: قاعدة البيانات الجداول التالية تم استعمالها في الفصل الذي تحدثنا فيه عن لغة ( SQLأمثلة على العالقة )M-N
العالقات هي كالتالي:
232
األجزاء المطلوبة • تحرير جدول الكورسات Lessons • تحرير جدول الطالب Students • تسجيل طالب لكورسات ُمعينة • حذف طالب من كورسات ُمعينة • عرض الكورسات التي يتعلمها طالب ما • عرض الطالب الذين يتعلمون كورس ما • مبنى القائمة الرئيسية (جميع العناصر ما عدا Loginال تكون قابلة لالختيار اال إذا كان ال ُمستخدم )LoggedIn oالعنصر EditStudentsيعرض جميع الطالب بواسطة DataGridView قابل للتحرير بشكل كامل (اضافة وخذف وتعديل). oالعنصر EditLessonsيعرض جميع الكورسات بواسطة DataGridView قابل للتحرير بشكل كامل (اضافة وخذف وتعديل). oالعنصر EditMarksيوفر امكانية الدخال عالمات الطالب في كورس ما بواسطة DataGridViewفيه أعمدة لتفاصيل الطالب وهي غير قابلة للتغيير. وعامود باسم Markالدخال العالمة لكل طالب. ▪
يجب اضافة حقل جديد باسم Markالى الجدول LessonsStudentsمن نوع Numberمجاله من 0الى ( 100يشمل).
▪ لجعل عامود ما غير قابل للتغيير نجعله ReadOnlyعلى النحو التالي – مباشرة بعد القيام بعملية الـ DataBinding •
DataGridView1.Columns("ColName").ReadOnly = True
oالعنصر Registerيوفر امكانية لتسجيل طالب ما لكورس ما أو حذفه (شبيه باألمر .)FileGroupsAssign oالعنصر SearchStudentلعرض الكورسات التي يتعلمها طالب ما .عرض النتائج يتم بواسطة DataGridViewغير قابل للتحرير.
233
oالعنصر SearchLessonلعرض الطالب الذين يتعلمون كورس ما .عرض النتائج يتم بواسطة DataGridViewغير قابل للتحرير. oالعنصر Aboutيُظهر نموذجًا فيه معلومات عن التطبيق بتصميم من اختياركم. oالعنصر Loginكما تمت برمجته في األمثلة ال ُمختلفة.
تمرين رقم 5 الهدف :استعمال DropDownListداخل الـ .DataGridView نريد تغيير صفحة تحرير المستخدمين بحيث نفرّق بين ثالثة مجموعات من المستخدمين: • مدراء للموقع Admin • معلمين Teacher • طالب Student
من أجل هذا الغرض تم تغيير الجدول Usersفي قاعدة البيانات ليصبح كالتالي:
حتى اآلن وفي جميع األمثلة تركنا أمر انشاء األدوات الالزمة لعرض وتحرير المعلومات لألداة DataGridViewورأينا كيف أنها زودتنا بأداة مناسبة لكل حقل بحسب نوعه .لكن ماذا لو أردنا أن يختار ال ُمستخدم قيمة واحدة فقط من مجموعة قيم ممكنة بحيث ال تُقبل أية قيمة أخرى من خارج مجموعة القيم هذه؟ الحل األمثل يكون في عرض هذه القيم داخل .ComboBox DataGridViewال تقوم بذلك من تلقاء نفسها وانما سنقوم بتحديد األدوات بشكل يدوي خالل عملية تصميم الـ .DataGridView
234
اضغط في عرض الـ Designعلى السهم الصغير الذي يظهر في الجهة اليمنى العليا من األداة .DataGridViewحينها ستظهر قائمة فيها األمر Edit Columnsأي تحرير األعمدة.
حينها ستظهر النافذة التالية لتحرير األعمدة
نضغط على الزر … Addلنبدأ باضافة العامود األول في النافذة التي تظهر
235
.1اسم العامود .2نوع األداة .3عنوان العامود .4تحديد بعض خصائص العامود :مثال مرئي وللقراءة فقط
بعدها نضغط على الزر Addلحفظ التغييرات. واآلن نضغط على الزر … Addلنضيف باقي األعمدة .اخترنا لجميع األعمدة TextBoxباستثناء العامود Groupالذي سيحتوي على ComboBoxلعرض األنواع ال ُممكنة لل ُمستخدمين:
236
بعد االنتهاء من اضافة جميع األعمدة نُغلق النافذة بالضغط على .Closeالنافذة التالية تُلخص جميع األعمدة الجديدة التي أضفناها الى الـ DataGridView
237
من جديد على النحو التاليDataGridView واآلن سنكتب عملية تعبئة الـ Private Sub FillGrid() Dim strRet As String = "" Dim obj As New ClassUsers نحصل على جميع ال ُمستخدمين كمصفوفة Dim A() As ClassUsers GetAllAsArray بمساعدة العملية A = obj.GetAllAsArray(strRet) If A Is Nothing Then MessageBox.Show(strRet) Return هنا نحصل على مؤشر على End If العامود الذي يحوي القائمة ال ُمنسدلة Dim ComboBoxColumn As DataGridViewComboBoxColumn ComboBoxColumn = CType(DataGridViewUsers.Columns("Group"), DataGridViewComboBoxColumn) ComboBoxColumn.Items.Add("Administrator") ComboBoxColumn.Items.Add("Teacher") ComboBoxColumn.Items.Add("Student")
هنا نضيف جميع أنواع ال ُمستخدمين الى القائمة ال ُمنسدلة التي في العامود ComboBoxColumn
الحظ أننا نمرر قيم.هنا نقوم بتعبئة األداة بشكل يدوي String كمصفوفة أحادية من نوعAdd السطر الى العملية
For i As Integer = 0 To A.Length - 1 obj = A(i) DataGridViewUsers.Rows.Add({obj.UserID, obj.UserName, obj.UserPW, obj.Group}) Next LabelNumOfRecords.Text = "Num Of Records: " + A.Length.ToString End Sub
الصورة التالية تُبين النتيجة
238
تمرين رقم 6
األداة TabControl هذه األداة TabControlعبارة عن نموذج يحتوي على عدة تبويبات أو ما يُسمى باالنجليزية Tabs وهي عبارة عن نماذج فرعيّة أو أجزاء ضمن نفس النموذج .األداة متوفرة ضمن الـ .Toolboxفي التطبيق التالي سوف نبني نموذجًا فيه 3تبويبات لفحص الفئة .Dbase.vbالتطبيق يُمكنُنا من كتابة أوامر SQLوتنفيذها على قاعدة بيانات ُمعينة ومن ثم عرض النتائج وهو ما يُسمة بمحرر SQL أي .SQL Editor • التبويب األول وعنوانه Select From Tableلتنفيذ أوامر SELECTتعيد جدوال يتم عرضه بواسطة األداة .DataGridView • التبويب الثاني وعنوانه Change Tableلتنفيذ أوامر UPDATE, DELETE, INSERT INTOتعيد عدد السجالت التي تأثرت بتنفيذ األمر ويتم عرض النتيجة بواسطة .MessageBox • التبويب الثالث وعنوانه Execute Scalarلتنفيذ أوامر تحتوي عمليات احصائية تعيد قيمة واحدة. اختر قاعدة بيانات مناسبة فيها على األقل جدوالن.
هذه هي التبويبات الثالثة
التصميم ال ُمقترح
هنا نكتب األمر .األداة عبارة عن TextBoxمتعددة األسطر أي MultiLine = True
هنا نعرض نتيجة تنفيذ األمر .األداة عبارة عن DataGridView بدون امكانيات تحرير
239
لتنفيذ األمر نضغط على الزر .Execute التبويب الثاني
هنا نكتب األمر .األداة عبارة عن TextBoxمتعددة األسطر أي = MultiLine .Trueنتيجة التنفيذ تُعرض بواسطة MessageBox
مبنى التبويب الثالث كمبنى التبويب األول. اضافة األداة تتم من خالل سحبها من الـ Toolboxورميها على النموذج ومن ثم تحديد بعض الخصائص .عدد التبويبات االفتراضي .2الضافة تبويبات نضغط على السهم الصغير الذي يظهر في الزاوية اليمنى العليا ومن ثم اختيار األمر Add Tab
240
احرص على اعطاء األدوات أسماء ال تتكرر مثل وButtonExecuteTab3 وButtonExecuteTab2 وButtonExecuteTab1 وTextBoxSQLStatementTab2 وTextBoxSQLStatementTab1 .TextBoxSQLStatementTab3
241
الملفات تعتبر الملفات الهياكل األساسية لتخزين المعلومات بشكل دائم على القرص الصلب للحاسوب .يوجد أنواع عدة من الملفات تبعا لألنواع المختلفة من التطبيقات .فهنالك على سبيل المثال ملفات األوفيس مثل ملفات الورد واإلكسيل والبوربوينت وما شابه ذلك .وملفات الـ PDFوغيرها .وال يمكن تصور العمل مع الحواسيب بدون استعمال هذه الهياكل لحفظ المعلومات. الملف ) (Fileعبارة عن مجموعة من المعلومات ال ُمخزنة في مجلد ما )(Folder or Directory في القرص الصلب للحاسوب ) .(Hard Diskبعد فتح الملف من قبل ال ُمبرمج (أي بآليات برمجية) يُسمى الملف حينها Streamأي انسياب أو تدفق أي تدفق مجموعة من البيانات من مكان الى آخر عبر قناة اتصال .يُمكن النظر الى الـ Streamعلى أنه عبارة عن سلسلة البايتات (A Sequence ) Of Bytesالتي نحصل عليها بعد فتح الملف .هنالك نوعان من الـ Streams Input Streamيُستعمل لقراءة معلومات من الملف )(Reading Data from a File Output Streamيُستعمل لكتابة معلومات في الملف )(Writing Data into a File هنالك نوعان أساسيان من الملفات • ملفات نصوص ) (Text Filesعبارة عن عبارة عن أسطر غير مشفرة ويمكن فتحها بأي ُمحرر نصوص بسيط مثل Note Padأو ُمتقدم مثل .MS Word • ملفات بنارية ُمشفرة ) (Binary Filesفتحها وفهم ال ُمحتوى يحتاج الى معرفة كيفية تخزينها. في هذا الفصل سنشرح فقط كيفية التعامل مع ملفات النصوص.
VB.Net I/O Classes توفر VB.NETضمن فضاء األسماء ( System.IOأي )System Input Outptمجموعة من الفئات التي تُمكن ال ُمستخدم من التعامل مع الملفات .والمقصود بالتعامل مع الملفات • انشاء مفات جديدة • حذف ملفات موجودة 242
• فتح ملفات للقراءة أو للكتابة • الكتابة داخل ملفات ما • اغالق ملفات مفتوحة • وما الى ذلك الجدول التالي يُلخص بعض الفئات ال ُمهمة ضمن System.IO الشرح
الفئة BinaryReader
لقراءة المعلومات من ملفات بينارية (أي مشفرة)
BinaryWriter
لكتابة المعلومات في ملفات بينارية
BufferedStream
ذاكرة مؤقتة من البايتات لحفظ Stream
Directory / DirectoryInfo
للتعامل مع المجلدات
File / FileInfo
للتعامل مع الملفات
FileStream / MemoryStream
لقراءة وكتابة معلومات من والى أي مكان في الملف
Path
للتعامل مع المسارات
StreamReader
لقراءة رموز ) (charactersمن Stream
StreamWriter
لكتابة رموز ) (charactersالى Stream
StringReader
للقراءة من String Buffer
StringWriter
للكتابة في String Buffer
الفئة FileStream قلنا بأن هذه الفئة تُمكننا من قراءة وكتابة معلومات من والى أي مكان في ملف ما .هذه الفئة ُمشتقة من الفئة Streamالتي هي عبارة عن فئة مجردة وبالتالي ال يمكن التعامل معها بشكل مباشر وانما فقط من خالل فئات مشتقة منها .من أجل فتح ملف موجود أو انشاء ملف جديد سوف ننشئ كائنًا من الفئة .FileStream
243
مثال األمر التالي ينشئ كائنًا لفتح الملف ( Data.txtالبارامتر األول) للقراءة والكتابة (البارمتر األخير) ان كان موجودًا .خالف ذلك يتم انشاء الملف (البارامتر الثاني). Dim fs As FileStream = New FileStream("Data.txt", FileMode.OpenOrCreate, )FileAccess.ReadWrite وبشكل عام تتلقى العملية البنائية 3بارمترات الشرح
البارمتر األول FileName
يُحدد اسم الملف
الثاني من نوع FileMode
نوع الفتح مثال فتح ان كان موجودًا أو انشاء ان لم يكن موجودًا .ممكن أن يكون واحدا من القيم التالية: :FileMode.Appendيفتح الملف بحيث تتم اضافة المعلومات الى نهاية الملف. :FileMode.Createانشاء ملف جديد. :FileMode.Openفتح ملف موجود. :FileMode. OpenOrCreateفتح الملف ان كان موجودًا أو انشاؤه ان لم يكن موجودًا. :FileMode. Truncateفتح ملف موجود وتفريغه من محتواه.
الثالث الحقوق من نوع FileAccess
هل قراءة فقط أم كتابة وقراءة وما الى ذلك .ممكن أن يكون واحدا من القيم التالية: :FileAccess.Readللقراءة فقط :FileAccess.ReadWriteللقراءة والكتابة :FileAccess.Writeللكتابة فقط
244
الستعمال هذه الفئات نريد كتابة تطبيق يفتح ملفات نصوص عادية ويعرض محتواها بواسطة األداة RichTextBoxويوفر امكانية لل ُمستخدم لحفظ أية تغييرات قام بها .سوف نتعرف أيضًا على نافذة الحوار الخاصة بفتح الملفات من مكان ُمعين في القرص الصلب وأيضًا على نافذة الحوار لحفظ ملف ما في مكان ما في القرص الصلب للحاسوب. التصميم ال ُمقترح الضغط على الزر … Browseيُظهر نافذة الحوار OpenFileDialogالمتوفرة في الـ .Toolboxهذه النافذة تُمكن ال ُمستخدم من اختيار الملف الذي يريد فتحه من أي مكان في القرص الصلب للحاسوب .الثالث نقاط بعد االسم تعني أن األمر ال يُنفذ مباشرة وانما ستظهر نافذة حوار قبل ذلك.
• لى الزر Open Fileيفتح الملف الذي اختاره ال ُمستخدم ويعرض ُمحتواه بواسطة األداة RichTextBox • الضغط على الزر Save Fileيحفظ التغييرات التي أُجريت على محتوى الملف المفتوح. • الضغط على الزر … Save Asيُظهر نافذة الحوار SaveFileDialogالمتوفرة في الـ .Toolboxهذه النافذة تُمكن ال ُمستخدم من اختيار اسم للملف ومجلد في القرص الصلب. التطبيق يقوم بعدها بحفظ نسخة من الملف المفتوح تحت االسم الذي أدخله ال ُمستخدم وفي المجلد الذي اختاره. 245
اضافة نافذة الحوار OpenFileDialog علينا سحب هذه األداة من الـ Toolboxورميها على النموذج .ال بأس بأن نبقي االسم OpenFileDialog1ألننا سوف نستعمل أداة واحدة فقط في النموذج .الحظ أن هذه األداة تظهر أسفل النموذج بالضبط مثل القائمة الرئيسية .MenuStripبامكاننا تحديد قيم للخصائص التالية التابعة لألداة OpenFileDialog1وهي :Filterتُحدد نوع الملفات التي ستظهرها النافذة .الفلتر عبارة سلسلة من األزواج التي يفصل بينها الرمز | .كل زوج مكون من نصين يفصل بينهم أيضا الرمز | .النص األول يظهر لل ُمستخدم في جزء خاص .مثال .Txt Files :والنص الثاني عبارة عن عادة الرمز * متبوعة بنقطة ثم ملحق الملفات التي نريد اظهارها مثال .*.txtالسلسلة تعني أنه بامكاننا فلترة واظهار أكثر من نوع من الملفات .الظهار كل الملفات نكتب * .All Files|*.هذه األجزاء ستظهر بداخل ComboBoxكما هو ظاهر بالصورة التالية. :FileNameالنص االبتدائي الذي سيظهر في الصندوق الذي يظهر فيه اسم الملف ال ُمختار .هنا سيظهر النص .Select your file
246
Browse… للزرClick برمجة معالج الحدث Private Sub ButtonBrowse_Click(sender As System.Object, e As System.EventArgs) Handles ButtonBrowse.Click لنافذة الحوارShowDialog() نستدعي العملية OpenFileDialog1.ShowDialog() OpenFileDialog1 End Sub
هذا األمر يُظهر النافذة التالية
Filter
كيف نعرف هل قام ال ُمستخدم باختيار ملف مناسب أم ال؟ في المعالج سوف نظهر المسار الكامل.OpenFileDialog التابع للـFileOK من خالل الحدث الحظ أن هذا المسار سيكون ُمخزنًا في الخاصية.للملف الذي اختاره ال ُمستخدم بمساعدة النافذة OpenFileDialog1.FileName التابعة لألداة أيFileName Private Sub OpenFileDialog1_FileOk(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles OpenFileDialog1.FileOk TextBoxFileName.Text = OpenFileDialog1.FileName.ToString End Sub
247
Open File للزرClick برمجة معالج الحدث الحظ أنه علينا.RichTextBoxContent يقوم هذا ال ُمعالج بفتح الملف واظهار محتواه في األداة في بداية ملف البرمجةImports System.IO اضافة فضاء األسماء
Public Function Exists(ByVal Path As String) As Boolean .( لملف ما يشمل اسم الملف مع الملحقFull Path) تتلقى كبارامتر المسار الكامل
ادعاء الدخول
خالف ذلك تعيد العملية. إذا وجدت أن الملف حقًا موجود في المكان ال ُممرَّر كبارامترtrue تعيد
ادعاء الخروج
: العملية موجودة ضمن الفئة.false System.IO.File
Private Sub ButtonOpenFile_Click(sender As System.Object, e As System.EventArgs) Handles ButtonOpenFile.Click Try
ثم نتأكد من أن الملف حقًا.أوال نحذف أي فراغ موجود في بداية أو في نهاية المسار . التي تتلقى المسار الكامل للملفFile.Exists موجود بمساعدة العملية
TextBoxFileName.Text = TextBoxFileName.Text.Trim() If TextBoxFileName.Text = "" Or Not File.Exists(TextBoxFileName.Text) Then MessageBox.Show("Error: No file selected!") Return End If . للملف الذي اختاره ال ُمستخدم ويتم فتحه للقراءة والكتابةFileStream هنا نقوم بانشاء كائن Dim fs As FileStream = New FileStream(TextBoxFileName.Text, FileMode.Open, FileAccess.ReadWrite) Dim sr As New StreamReader(fs) RichTextBoxContent.Text = sr.ReadToEnd() sr.Close() fs.Close()
في النهاية نُغلق الـكائنين
Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
248
يُمكنناStreamReader ثم نقوم بانشاء كائن من قراءة محتوى الملف بالكامل بمساعدة ReadToEnd العملية
بعد ذلك سيظهر محتوى الملف كالتالي
مثال لسنا بحاجة الى. ونحن نناقش هنا أبسطها.طبعًا هنالك عدة امكانيات لفتح الملفات والقراءة منها على النحوStreamReader وانما يكفي استعمال كائن من نوعFileStream انشاء كائن من الفئة التالي Private Sub ButtonOpenFile_Click(sender As System.Object, e As System.EventArgs) Handles ButtonOpenFile.Click Try TextBoxFileName.Text = TextBoxFileName.Text.Trim() If TextBoxFileName.Text = "" Or Not File.Exists(TextBoxFileName.Text) Then MessageBox.Show("Error: No file selected!") Return End If StreamReader ثم نقوم بانشاء كائن Dim sr As New StreamReader(TextBoxFileName.Text) يُمكننا من قراءة محتوى الملف بالكامل RichTextBoxContent.Text = sr.ReadToEnd() ReadToEnd بمساعدة العملية sr.Close()
Stream في النهاية نُغلق الـ
Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
249
عمليات بنائية وبالتالي امكانيات ُمختلفة للتعامل مع10 لديها أكثر منStreamReader الفئة .الملفات Save للزرClick برمجة معالج الحدث . في الملف ال ُمختارRichTextBoxContent يقوم هذا ال ُمعالج بحفظ محتوى األداة Private Sub ButtonSaveFile_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSaveFile.Click Try TextBoxFileName.Text = TextBoxFileName.Text.Trim() If TextBoxFileName.Text = "" Or Not File.Exists(TextBoxFileName.Text) Then MessageBox.Show("Error: No file selected!") Return End If RichTextBoxContent.Text = RichTextBoxContent.Text.Trim() If RichTextBoxContent.Text = "" Then MessageBox.Show("Empty Text!") Return End If للملف الذي اختاره ال ُمستخدم ويتم فتحه للكتابةFileStream هنا نقوم بانشاء كائن Truncate وتفريغ محتواه األصلي Dim fs As FileStream = New FileStream(TextBoxFileName.Text, FileMode.Truncate, FileAccess.Write) Dim sw As New StreamWriter(fs) sw.WriteLine(RichTextBoxContent.Text) ' Apply the update to the file sw.Flush() ' Close the stream writer sw.Close()
ننشئ كائنًا من نوع ثمStreamWriter نستدعي بمساعدته العملية ونُمرر لهاWriteLine محتوى األداة RichTextBoxContent sw.Flush() االستدعاء يقوم بالحفظ الحقيقي لما كتبته العملية sw.WriteLine()
' Close the file stream object fs.Close()
Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
250
Save As… للزرClick برمجة معالج الحدث اختيار المجلد. في الملف ال ُمختارRichTextBoxContent يقوم هذا ال ُمعالج بحفظ محتوى األداة وما علينا اال سحبهاToolbox ال ُمتوفرة في الـSaveFileDialog واسم الملف سيتم بمساعدة األداة .ورميها على النموذج البرمجة Private Sub ButtonSaveAs_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSaveAs.Click Dim Res As DialogResult Res = SaveFileDialog1.ShowDialog() If Res = Windows.Forms.DialogResult.OK Then MessageBox.Show("Saved Successfully") End If End Sub Private Sub SaveFileDialog1_FileOk(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles SaveFileDialog1.FileOk Try TextBoxFileName.Text = SaveFileDialog1.FileName TextBoxFileName.Text = TextBoxFileName.Text.Trim() RichTextBoxContent.Text = RichTextBoxContent.Text.Trim() If RichTextBoxContent.Text = "" Then MessageBox.Show("Empty Text!") Return End If Dim sw As New StreamWriter(TextBoxFileName.Text) sw.WriteLine(RichTextBoxContent.Text) sw.Close() Me.DialogResult = DialogResult.OK Catch ex As Exception MessageBox.Show(ex.Message) End Try End Sub
251
األداة RichTextBox حتى اآلن استعملنا هذه األداة وكأنها .TextBoxالحقيقة أن هذه األداة لها قدرات عالية جدا لتحرير وتنسيق النصوص .من هنا جاء اسمها بأنها غنية (أي .)Richفي المثال التالي سنتعرف على بعض امكانيات التحرير والتنسيق التي تُوفرها هذه األداة. التطبيق التالي يحتوي على قائمة رئيسية واألداة .RichTextBox
العنصر EditUndoيتراجع عن تنفيذ العملية األخيرة العنصر EditRedoيتراجع عن التراجع األخير العنصر EditCopyينسخ النص ال ُمختار في األداة العنصر EditPasteيلصق النص المنسوخ في األداة في المكان الذي تتواجد فيه عالمة الكتابة العنصر EditCutيقص النص ال ُمختار في األداة العنصر EditSelect Allيختار كل محتوى األداة العنصر FormatSelected ForeColorيُظهر نافذة اختيار األلوان ثم يُلون النص ال ُمختار في األداة باللون الذي اختاره ال ُمستخدم الستخدام نافذة اختيار األلوان علينا سحب األداة من الـ Toolboxورميها على النموذج. 252
برمجة العناصر Public Class FormMain Private Sub UndoToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles UndoToolStripMenuItem.Click RichTextBox1.Undo() End Sub Private Sub RedoToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles RedoToolStripMenuItem.Click RichTextBox1.Redo() End Sub Private Sub CopyToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles CopyToolStripMenuItem.Click RichTextBox1.Copy() End Sub Private Sub PasteToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles PasteToolStripMenuItem.Click RichTextBox1.Paste() End Sub Private Sub CutToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles CutToolStripMenuItem.Click RichTextBox1.Cut() End Sub Private Sub SelectAllToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SelectAllToolStripMenuItem.Click RichTextBox1.SelectAll() End Sub Private Sub CloseToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles CloseToolStripMenuItem.Click Me.Close() End Sub Private Sub SelectedForColorToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SelectedForColorToolStripMenuItem.Click ColorDialog1.ShowDialog() RichTextBox1.SelectionColor = ColorDialog1.Color End Sub End Class
253
تمارين تمرين رقم 1 أضف الى التطبيق األخير عنصرًا جديدًا في القائمة الرئيسية FileSave Asيُظهر SaveFileDialogلحفظ محتوى الـ RichTextBoxفي ملف خاص.
تمرين رقم 2 صمم تطبيقًا يفتح ملفات ( CSVأي ُ .)Comma Seperated Valuesملحق هذه الملفات هو .csv األسطر في هذه الملفات تحتوي على نصوص مكونة من أجزاء يفصل بين هذه األجزاء الرمز ;. السطر األول عبارة عن عناوين لألجزاء. مثال StudentName;Year;Semester;Subject;Mark Sami Abu Sami;2014-2105;Sem A;VB.NET;95 Ali Abu Ali;2014-2105;Sem A;VB.NET;93 … على التطبيق أن يعرض هذا المحتوى بواسطة DataGridViewقابل للتحرير بشكل كامل .بامكان ال ُمستخدم حفظ التغييرات التي أجراها على محتوى الملف من خالل الضغط على زر خاص للحفظ.
254