الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

السلسلة الذهبية في تعلم البرمجة

رائج
بدأه فواز الشمري في 29 نوفمبر 2005 · 59 رد · 15,981 مشاهدة · في لغة Ms Visual Basic 6 وما قبلها من إصدارات
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

في هذا الموضوع لن أخوض بشرح نظري ولكن سيكون التركيز في الشرح عن الأدوات التي سيتم استخدامها عمليا في الكود :

ما هي البرمجة باستخدام OOP أو البرمجة الهدفيه :

كلمة OOP اختصار من Object Oriented Programming وهي عبارة عن أسلوب برمجي تم ابتكاره على أساس مساعدة المبرمجين في ترتيب الشفرة المصدرية والاستفادة من إمكانية إعادة الترتيب والاستخدام في برامج أخرى.

كانت اكبر مشكلة يعاني منها المبرمجين وهي مشكلة التكرار الذي يصاحب البرنامج فمثلا لو أردنا أن نكتب برنامج ما وليكن مثلا أننا نريد أن نظهر نافذة ما في منتصف الشاشة مع الميلان قليلا ناحية اليسار :

سنحتاج في هذا إلى إنشاء الكود التالي :

Private Sub Command1_Click()
  Dim H As Long, W As Long
    
    H = ((Screen.Height - Form1.Height) / 2)
    W = ((Screen.Width - (Form1.Width + 2145)) / 2)
       
    Form1.Move W, H
    Form1.Show vbModal
End Sub

في الكود السابق تم تعريف متغيرين من نوع Long وذلك لأخذ موقع النافذة بالنسبة للشاشة ولقسمة على الرقم 2 ليجعل ظهور النافذة في المنتصف.

نلاحظ أيضا استخدام الدالة Move التابعة للنافذة Form1 ووظيفة هذه لداله هوا تحريك النافذة على المستوى الأفقي والعمودي وهي تقوم بعمل الخصائص Top و Left . ولإستخدامها في هذه الحالة اسرع بكثير من الدوال Top و Left . في تنفيذ التحريك .

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

Private Sub Command1_Click()
  Dim H As Long, W As Long
    
    H = ((Screen.Height – Form2.Height) / 2)
    W = ((Screen.Width - (Form2.Width + 2145)) / 2)
       
    Form2.Move W, H
    Form2.Show vbModal
End Sub

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

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

لم يتم تغير أي شي في الشفرة سوى اسم النافذة .

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

تتمثل هذه الفكرة بما يلي :

الإجراء :

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

وبالتالي فهي تساعد في أن تشارك في إعطاء العديد من المهام في البرنامج دون الحاجة لإعادة كتابه الكود أو الشفرة :

في لغة Visual Basic يتم التعريف عن الإجراء على النحو التالي :

[Private | Public | Protected | ] Sub Sub_Name ([Parameters])


End Sub

الأنواع [Private | Public | Protected | ] :

هما عبارة عن أدوات الحماية للإجراء . كيف :

قلت في الأعلى أن الإجراء والد اله هما عبارة عن كتله برمجية يمكن الوصول إليهما من أي مكان في البرنامج . ولكن هنا نوع الوصول قد يتم منعه . كيف :

كلمة Private :

تعني أن هذا الإجراء يمكن أن استخدمه في المكان الحالي الذي أنا فيه . فمثلا لو عرفت إجراء على النحو التالي :

 Private   Sub Sub_Name ()

  End Sub

وكان هذا التعريف في النافذة Form1 :

كلمة Private تعني انه ممكن أن استخدم هذا الإجراء بعني استدعيه من داخل Form1 فقط ولا يمكن أن أستدعيه من Form أخر غير Form1 . مثلا :

لو كنت في Form2 وأردت أن أستخدمه كما يلي :

Form1.Sub_name

لاحظ عند وضع علامة Dot أي النقطة بعد كلمة Form1 لم يظهر اسم الإجراء Sub_name وذلك لأن كلمة Private قد منعت استدعاء هذا الإجراء من خارج Form1 وأنا في هذه الحالة قمت باستدعائه من Form2 .

طيب لو دعت الحاجة أن استخدم هذا الإجراء في Form2 كيف الحل :

أقوم بتغير كلمة Private إلى Public وهي بمعني يمكن من أي مكان في البرنامج أن استدعي هذا الإجراء وبالتالي يمكن أن أستدعيه من أي نافذة في البرنامج . وهوا يظهر في نافذة الإكمال التلقائي بمجر وضع Dot بعد Form1 .

الكلمة Protected : تعني لايمكن الوصول إلى الإجراء إلى داخل الفئات المشتقة أو Class المشتق او المورث من Class أخر ولا يمكن الوصول إليه عند الاستخدام . دعك من هذا الآن سوف أتحدث عنه عند البدء باستخدام الـ Classes في لغة Visual Basic .

يجب عليك أن تختار احد الثلاثة المذكورة إما Private أو Public أو Protected .

بعد ذكر نوع محدد الوصول إلى الإجراء نذكر كلمة Sub وهي تعني أن هذه الكتلة هي عبارة عن إجراء ثم بعد هذه الكلمة أسم الإجراء بعد ذلك الوسطاء أو Parameters التي تمرر للإجراء .

مثال :

اكتب التالي في النافذة Form1

 Private   Sub SetName ()

  End Sub

هذا الإجراء أسمه SetName و عند استدعائه نكتب التالي :

ضع زر أمر وضع به التالي :

 
 Private Sub Command1_Click()
    SetName
 End Sub

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

 Private   Sub SetName ()
          MsgBox "hi"
  End Sub

الآن قم بالضغط على زر الأمر ماذا تلاحظ , ظهور رسالة تحمل نص Hi .

انتقل إلى النافذة Form2 وحاول أن تستدعي الإجراء . سوف تلاحظ أنه لا يمكنك الوصول إلى الإجراء لأنه هنا خاص بالنافذة Form1 لذلك غير محدد الوصول أو نوع حماية الإجراء من Private إلى Public . ثم حاول أن تستدعيه من Form2 .

ملاحظة مهمة :

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

فلو كنت في Form2 وأود أن أصل لأجزاء من النافذة Form1 يجب أن أذكر اسم النافذة Form1 وبعد ذلك اسم الإجراء أو الشئ الذي أريده .

	Form1.SetName

طيب ما هي آل Parameters أو الوسطاء :

نحن فعلا استفدنا من الإجراء ولكن ما ذا لو أردنا أن نغير الرسالة التي تظهر وهي نص Hi

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

 Private   Sub SetName (txt As String)
          MsgBox txt
  End Sub

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

من داخل Form1

	SetName "My Name"

من خارج الـ Form1

	Form1. SetName "My Name"

تم ذكر اسم الإجراء ثم بين علامة تنصيص تم وضع كلمة My Name وهي البيانات التي سيستقبلها المتغير txt في الإجراء يعني المتغير txt في الإجراء هوا أي نص ذكر بعد اسم الإجراء . وسيكون ناتج استدعاء الإجراء هوا ظهور رسالة بها نص My Name .

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

Public Sub CenterForm(frm As Form)
  Dim H As Long, W As Long
    
    H = ((Screen.Height - frm.Height) / 2)
    W = ((Screen.Width - (frm.Width + 2145)) / 2)
       
    frm.Move W, H
    frm.Show vbModal
End Sub

هذا عبارة عن إجراء كتلة Block لها بداية من Public Sub ونهاية عند End Sub . اسم الإجراء CenterForm يحتوى على وسيط واحد وهوا frm ولكن يجب النظر إلى نوع البيانات التي أخذها المتغير frm .

يجب أن نعرف انه من أنواع البيانات بيانات قياسية مثل integer , string ,long وغيرها وبيانات اخرى من نوع Object من هذه الأنواع Form,Picture,Command وغيرمن هذه الأنواع .

حيث أنه تم تعريف المتغير frm من نوع Form أي من نوع نافذه وبالتالي فإن القيمة التي يجب ان يستقبلها المتغير frm يجب أن تكون اسم نافذة . ويكون استدعاء الإجراء كما يلي :

	CenterForm Form5

من خارج الـ Form1

	Form1. CenterForm Form5

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

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

سير الدرس سيكون حول الإجراءات Procedures و الدوال Functions و الخصائص Properties ثم الوحدات النمطية Module ثم الفئات Classes . والخوض في تفصيلها وكيفية التعامل معها حتى يتحول الدرس إلى دروس متقدمه وذلك بالتطبيق .

ملاحظة جميع هذه الدروس مرجعي الوحيد هوا ما يحتويه عقلي وتسلسل الدرس من تدفقات التعبير لدي لذا اعتذر لمن لم يفهم الشرح جيدا وأتمنا للجميع التوفيق وفي انتظار التعليقات . وللدرس بقية ؟

#2

السلام عليكم

ماشاء الله عليك اخى الكريم فواز,,

موضوع اكثر من رائع بالفعل

ننتظر البقية,,,

Hany Saad Mostafa

ITI (Information Technology Institute) l

SD Dept.-Teaching Assistant

---

اسألكم الدعاء بظهر الغيب

#3

الموضوع جميل جدا اخي فواز

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

#4

يا سلام عليك يا أخي فواز

انت معلم بالشرح .....

عن جد ولا احلى من هيك

اتمنى ان تستمر على هذه الطريقة المبسطة والجميلة

منتظرين بفارغ الصبر يا معلم

#5
لازلنا منتظرين باقية شرحك الجميل اخي فواز
#6

شكرا اخي hanysaad على مرورك , وكذلك yasserstars أتمنا ان تستفيد من كل كلمة في هذا الموضوع فهوا هدفي الإفادة والأجر من الله , اخي rsh76 اشكرك على كلماتك والحمد لله ان وفقت في اسلوب عرض الموضوع (الشرح ) . :rolleyes:

سوف أتابع السلسلة التي بدأت الحديث عنها وهي كيفية الاستفادة من هندسة البرمجيات وطريقة الاستخدام الأمثل لها في برنامج Visual Basic :

تصنيف هذا الدرس:

يزيد من مهارة المتعلم ويطلع المتقدم على أساليب متنوعة وأنواع مختلفة في البرمجة :

بعد أن تعرفنا على الإجراء وكيفية استدعائه سوف أخذ الآن بعض الأمثلة :

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

جميع ما يكتب في Form من إجراءات ودوال وغيرها من متغيرات وغيرة تعتبر كتله برمجية أي أن Form هوا عبارة عن كتلة برمجية تحتوى على كتل برمجية . يمكن أن أقول أن Form حاوية تحتوى على كتل برمجية (إجراءات – دوال – متغيرات – أحداث)

مثال :

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

سوف نستخدم لمثل هذه الحالة TextBox :

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

Private Sub Text1_KeyPress(KeyAscii As Integer)

End Sub

الكود الموضح هوا عبارة عن إجراء حدث تابع للكائن Text1 , وهذا الحدث هوا KeyPress وهوا القسم المسؤل عن تحديد بعض مفاتيح الـ Keyboard .

Private Sub Text1_KeyPress(KeyAscii As Integer)
    Dim str As String
    str = "0123456789" & vbBack
    If InStr(str, Chr(KeyAscii)) = 0 Then
    KeyAscii = 0
    End If
End Sub

هذا الإجراء المسمى Text1_KeyPress له وسيط KeyAscii من نوع بيانات رقمية نوعها Integer :

هذا الإجراء يمرر للوسيط KeyAscii من قبل Visual Basic قيمة رقمية تمثل رقم المفتاح الذي قام المستخدم بالضغط علية .

شرح الكود :

الفكرة في الكود تتمثل بتحويل الرقم الذي يحتويه المتغير KeyAscii إلى الحرف المقابل لهذا الرقم . إذا كان الحرف واقع بين النص Str دل على أن القيمة التي يحملها المتغير KeyAscii هي عبارة عن رقم وإلا فإن القيمة التي يحملها هي حرف أبجدي لذلك تم تصفير المتغير KeyAscii حتى يمنع من الكتابة على صندوق النص Text1.

نعود لمربط الفرس من هذا المثال :

لدي في النافذة Form1 أكثر من 5 TextBox وأريد أن أمنع الكتابة لكل هذه الصناديق سيكون الحل بتكرار الكود المذكور 5 مرات لكل صندوق نص .

وبالتالي سوف يظهر لديك تكرار كبير في الكود وستزدحم اسطر صفحة Form بكودات مكررة , كيف يمكننا حل هذه المشكلة .

سوف استخدم في هذه الحالة الدوال لحل هذه المشكلة :

ماهي الدالة :

الدالة هي عبارة عن كتلة برمجية وهي مشابه للإجراء ولكن الفرق في أن الدالة تعيد قيمة بينما الإجراء لا يعيد قيمة .

التعريف : يتم تميز الدالة عن الإجراء بذكر Function بدل من Sub كما يلي :

تعريف دالة

[Private | Public | Protected Function fun_name (Parameters ) As DataType 

End Function

As DataType تمثل نوع القيمة التي يجب أن تعيدها هذه الدالة وهي أي نوع من أنواع البيانات سواء كانت بيانات قياسية أو بيانات كائنيه. كما ذكرت سابقا عن أنواع البيانات .

مثال :

Private Function GetName(sec As Integer) As String
    Select Case age
        Case 1
            GetName = "علوم حاسوب"
        Case 2
            GetName = "نظم معلومات"
    End Select
End Function

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

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

GetName = "نظم معلومات"

كيف يمكن أن استخدم الدالة لحل المثال المذكور :

يجب أن تضع في بالك دائما أنه لو تكرر معاك كود برمجي أكثر من مره فإنه يحب عليك (أفضل من وجهة نضري ونظر مهندسو البرمجيات) أن تقوم بوضع هذا الكود في دالة أو إجراء بحسب الحاجة.

لحل المشكلة من تكرار الكود السابق بالنسبة لمنع الأرقام في أكثر من TextBox . سوف اعرف دالة باسم GetNumberOnly وأدع هذه الدالة تعيد قيمة من نوع رقم فيكون التعريف كما يلي :

Private Function GetNumberOnly(KAsc As Integer) As Integer
   Dim str As String   
    str = "0123456789" & vbBack
    If InStr(str, Chr(KAsc)) > 0 Then
         GetNumberOnly = KAsc
    Else
         GetNumberOnly = 0
    End If    
End Function

التغير الذي حصل :

جعلت الدالة تستقبل قيمة رقمية ثم قمت بعمل الفحص كما في الكود السابق لكن هنا السطر GetNumberOnly = KAsc يعني ان يعيد نفس القيمة التي مررت للدالة أما في حال لم يتحقق الشرط فإن الدالة تعيد قيمة صفر >

GetNumberOnly = 0 .

كيف يتم إستدعاء الدالة :

كما في الكود التالي سوف يتم استخدام الدالة في كل الصناديق TexBox1 :

Private Sub Text1_KeyPress(KeyAscii As Integer)
   KeyAscii = GetNumberOnly(KeyAscii)
End Sub

في حال استخدام الدوال فإن لابعد من معرفة التالي :

يجب ان يكون الإستدعاء مكون من طرفين مفصولين بعلامة التساوية بمعني أن الدالة يجب ان تعيد قيمة يستقبلها متغير وفي مثالنا هذا فإن المتغير KeyAscii يساوي القيمة التي تعيدها الدالة GetNumberOnly .

والقيمة التي تعيدها الدالة هي إما صفر أو نفس قيمة المتغير الذي تم تمريرة للدالة .

توضيح :

قام المستخدم بالضغط على حرف a وهوا في هذه الحالة يعيده المتغير KeyAscii بالرقم 97 (الرقم 97 هوا الحرف a في جدول Ascii) نمرر هذه القيمة التي حصل عليها المتغير KeyAscii من قبل visual basic إلى الدالة GetNumberOnly الآن داخل الدالة قمنا باستقبال هذا الرقم في المتغير KAsc وهوا من نفس نوع القيمة الممررة Integer . ثم قمنا باستخدام الدالة chr لتحويل الرقم هذا 97 إلى حرف تم الفحص هل الحرف a واقع في str الناتج بالطبع لا لذلك ستعيد الدالة قيمة صفر . الآن يعود المترجم إلى الإجراء المستدعي للدالة وهوا الحدث Text1_KeyPress نلاحظ أن الدالة أرجعت قيمة سيأخذها المتغير KeyAscii . وبالتالي صفرنا قيمة المتغير KeyAscii ولن يطبع على النص الحرف هذا .

ملاحظة :

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

Private Sub Text1_KeyPress(KeyAscii As Integer)
   KeyAscii = GetNumberOnly(KeyAscii)
End Sub

Private Sub Text2_KeyPress(KeyAscii As Integer)
   KeyAscii = GetNumberOnly(KeyAscii)
End Sub

Private Sub Text3_KeyPress(KeyAscii As Integer)
   KeyAscii = GetNumberOnly(KeyAscii)
End Sub

Private Sub Text4_KeyPress(KeyAscii As Integer)
   KeyAscii = GetNumberOnly(KeyAscii)
End Sub

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

يتم تميز الدالة والإجراء في قائمة الإكمال التلقائي بوضع أيقونه المساحة وهي تفيد بأن هذه دالة أو أجراء يمكنك ملاحظتها (اضغط Ctrl + Space )

تمرين :

ضع حل أخر لمعالجة منع طباعة الأرقام. ثم مكن زر المسافة (أتمنا من الجميع المشاركة فيه )

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

وللدرس بقية ............ B)

#7

موضوع اكثر من رائع

بارك الله فيك

#8

جزاك الله كل خير أخي فواز عاي هذا الجمال الذي تقدمه لنا من خلال شرح المميز

وارجو ان توضح لنا الفرق بين الفئة class و الاجراء عند وضعه في مديول

ولك جزيل الشكر

#9

جزاك الله خيرا أخي فواز علي هذا الشرح الجميل

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

وفقك الله

#10

السلام عليكم

الاخ فواز,

بارك الله فيك على هذه الدروس الاكثر من رائعة,

وفى انتظار باقى الدروس,,,

Hany Saad Mostafa

ITI (Information Technology Institute) l

SD Dept.-Teaching Assistant

---

اسألكم الدعاء بظهر الغيب

#11
منتظرينك اخي فواز
#12

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

الدرس الثالث :

استخدام الدوال والإجراءات على مستوى الفورم :

كيف يمكن للإجراء أن يستدعي إجراء أخر :

لنأخذ هذه الحالة :

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

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

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

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

طيب الآن أريد أن أقوم بعمل توليد اسم عشوائي الحل أن أقوم بعمل دالة سوف أسميها GetRandomName كما يلي :

Private Function GetRandomName() As String
    
End Function

لاحظ أن الدالة نوع البيانات التي يجب أن تعيدها هي String .

فكرة عمل خوارزمية توليد الاسم العشوائي :

هي فكرة تولدت لدي حاليا والرغبة لمن يريد تطويرها : وهي أن أقوم بتحديد 36 خانة تتكون من الأحرف والأرقام كما يلي :

Const str As String = "0123456789abcdefghijklmnopqrstuvwxyz"

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

Str هي عدد الأرقام + الحروف 36 خانة سوف اقوم بالمرور العشوائي بحيث اخذ الخانة رقم واحد والأخرى الخانة رقم 10 ومرة 30 وهكذا حتى يتولد لدي مجموعة من الخانات ملخبطة وبعد ذلك سوف اعيد هذه السلسلة العشوائية كنتيجة معادة من الدالة GetRandomName :

المثال كما يلي :

Private Function GetRandomName() As String
    Const str As String = "0123456789abcdefghijklmnopqrstuvwxyz"
    Dim nStr As String
    
    Dim i As Integer, OffSet As Integer
    
    For i = 1 To 36
        Randomize
        OffSet = (36 * Rnd + 1)
        nStr = nStr & Mid(str, OffSet, 1)
    Next i
    
    GetRandomName = nStr
End Function

نظرة سريعة على الشفرة :

دوران 36 مرة حتى اضمن أكثر دقة في توليد الاسم أو السلسلة العشوائية:

Randomize دالة تستخدم في تضبط التوليد العشوائي مقاسه بالملي ثانية.

السطر ( OffSet = (36 * Rnd + 1

تولد رقم عشوائي عن طريق Rnd هذا الرقم الناتج يتم ضربة في 36 حتى يكون الموقع المتولد من الدالة العشوائية Rnd واقع في هذه الخانة . الزيادة بمقدار واحد لإهمال الصفر . ليش :

شوف في كل دورة احدد خانة مثلا المتغير offset كان الرقم الوقع هوا رقم 12 ماهي الخانة في السلسة str التي موقعها 12 ان لم يخني العدد فهوا الحرف b . طيب اخذت هذا الحرف عن طريق الداله Mid وهي اجتزاء حرف من السلسة str من الموقع offset وهوا 12 وبطول واحد يعني الناتج بيكون الحرف b . طيب عود مرة ثانية لنفترض ان العدد الناتج في offset أي الرقم العشوائي هو 0 هذه المره , الدالة Mid سوف تجتزء من السلسلة str من الموقع 0 وهي غير مقبولة لأن الدالة mid تعتبر أول خانة في السلسة str هي 1 وليس صفر إذا هنا سيكون لدينا خطأ عند الدالة mid لذلك زدت المتغير offset عند توليد الرقم العشوائي برقم 1 حتى يهمل الصفر من الحساب .

الآن بعد أن أصبح الإجراء جاهز للإستخدام يمكنني لزيادة الضمان أن أقوم بالتأكد من ان الإسم الجديد غير موجو من قبل يعين ليس مخزن بإسم صورة .

سوف اقوم بالتأكد من هذه العملية في داله اخرى :

Private Function UploadImageFile()

End Function

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

Private Function UploadImageFile() As String
    Dim newImageName As String, Extent As String
    Dim Path As String
    
    With CommonDialog1
        .Filter = "jpg File|*.jpg |Gif File|*.gif"
        .ShowOpen
        If .FileName <> "" Then newImageName = GetRandomName
        Extent = Mid(.FileName, InStrRev(.FileName, "."), 4)
        
        Path = IIf(Mid(App.Path, Len(App.Path) - 1) = "\", App.Path, App.Path & "\")
        
         Do While Dir(Path & newImageName & Extent) <> vbNullString
            newImageName = GetRandomName
         Loop
         FileCopy .FileName, Path & newImageName & Extent
    End With
    
    UploadImageFile = newImageName & Extent
End Function

سوف ادع المثال التالي للدراسة وسوف أعقب علية فور توفر الوقت المناسب:

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

#13

مشكور اخي فواز نرجو منك المزيد

وياريت توضح النقطة التي كنت اثرتها وهي الفرق بين الاجراء عند كتابته في مديول والاجر اء في الفئة class

#14

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

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

ارجو منك سرعة استكمال الموضوع بالاخص بناء الفئات بكل ما تشمل من(خصائص-احداث-طرق)

ولكم جزيل الشكر والعرفان

#15

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

تعقيب على الدرس الثالث:

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

وهي انه لدينا مثال يتطلب منا أن ندخل بيانات طالب مع صورة له :

في البرمجة يجب:

أولاُ أن نقوم بتحديد المهمة التي يجب علينا عملها .

ثانياً : تحديد المشاكل التي قد تصادفها هذه الطريقة أو المهمة .

ثالثاً :نقوم بتحديد مجموعة من الحلول لهذه المهمة .

رابعاً نقوم باختيار الأسلوب الأفضل من الحلول المحددة .

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

خامسا : نوع الوظيفة هل سوف استخدمها أكثر من مرة وأقصد بالوظيفة المهمة التي يجب أن أقوم بعملها.

سادساً : بناء ألأسلوب البرمجي .

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

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

تحديد الوظيفة: (تطبيق لما ذكر من الكلام ) :

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

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

سوف احدد المشاكل التي قد أعاني منها عند عمل هذه الوظيفة :

ماهية المشاكل :

أين أضع الصور – كيف أقوم بترتيبها – كيف أقوم بحمايتها . – كيف أتخلص من كبر حجمها . الخ

بعد ذلك سوق احدد مجموعة للحلول :

الفكرة الأولى :

أن أقوم بخفض الصور في قاعدة البيانات :

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

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

ملاحظة :

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

الحل المقترح الثاني :

هوا أن أقوم بحفظ الصور بمجلد بجانب البرنامج واسمية مثلا Images .

لهذا المقترح نقاط في الحل وهي :

نسح الصورة التي قام المستخدم باختيارها ووضعها في مجلد images الموجود بجانب دليل البرنامج .

بعد أن أقوم بهذا العمل أقوم بتخزين كافة بينان الطالب وأنشأ حقل في جدول الطلاب باسم img اخزن فيه اسم الصورة التي نسختها في مجلد images .

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

لكن في هذه الحالة كما ذكرت سيكون لدي مشكلة احتمالية تكرار اسم الصورة كيف:

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

الحل الأول :

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

الحل الثاني :

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

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

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

ملاحظة : من ناحية الأمان يمكن عمل خوارزميات تشفير للصور عند حفظها بحيث لا يمكن التعديل أو الفتح أو الحذف وغيرة من عمليات الأمان التي يتطلبها البرنامج - ويمن استخدام Sql-Server أو Oracle لحل هذا النوع من الأمان بحيث كما ذكرت تخزن الصورة في قاعدة البيانات .

خامسا :

في هذه المرحلة سوف احدد هل سوف احتاج هذه الو ضيفه أو المهمة في البرنامج ليش :

هل البرنامج يحتاج أن اعمل رفع صور اقصد نسخ صور ما ,

قد يكون نعم مثلا صور من الإنذارات و التعهدات التي يحصل عليها الطالب .

أيضا صور للمدرسين .

يتضح لدي أنه لدي 3 حالات سوف استخدم فيها وضيفة أو مهمة نسخ الصور .

نحن اتفقنا من البداية أنه في حال حصل تكرار ما لأي (كود أو وضيفة أو مهمة ) كلها نفس المعنى - يعتبر خلل في البرنامج وهذا الخل هوا من وجه نضر هندسة البرمجيات كما ذكرت سابقا في الدروس .

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

أخيرا بناء الأسلوب البرمجي :

1- بناء دالة تستخدم لعمل اسم عشوائي

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

3- تحديد الأدوات والدوال التي سوف استخدمها والتي هي معدة من قبل اللغة مثل mid , FileCope وغيرها من الدوال التي سوف احتاجها في الحل .

4- بناء الوظيفة وتحديد الهيكل العام للعمل معها من بقية أجزاء البرنامج, فنحن هنا سوف نتعامل مع وضيفة أو نسميها مهمة أو عملية أي اسم القصد منها هوا كيف سأستدعي هذه المهمة التي تقوم بنسخ الصورة في البرنامج مع الثلاث الحالات وهي (إضافة طالب – مدرس – الإنذارات والتعهدات )

بمعنى : أنه لدي حاليا دالتين هي توليد الرقم العشوائي و تنفيذ النسخ . هذا معنى الهيكل العام للوظيفة .

5- مراعات الحجم في الكود والسرعة في التنفيذ و قله هدر الذاكرة .

الدالتين معاُ في الكود التالي :

Private Function GetRandomName() As String
    Const str As String = "0123456789abcdefghijklmnopqrstuvwxyz"
    Dim nStr As String
    
    Dim i As Integer, OffSet As Integer
    
    For i = 1 To 36
        Randomize
        OffSet = (36 * Rnd + 1)
        nStr = nStr & Mid(str, OffSet, 1)
    Next i
    
    GetRandomName = nStr
End Function

Private Function UploadImageFile() As String
    Dim newImageName As String, Extent As String
    Dim Path As String
    
    With CommonDialog1
        .Filter = "jpg File|*.jpg |Gif File|*.gif"
        .ShowOpen
        If .FileName <> "" Then newImageName = GetRandomName
        Extent = Mid(.FileName, InStrRev(.FileName, "."), 4)
        
        Path = IIf(Mid(App.Path, Len(App.Path) - 1) = "\", App.Path & "imges\", App.Path & "\imges\")        
         Do While Dir(Path & newImageName & Extent) <> vbNullString
            newImageName = GetRandomName
         Loop
         FileCopy .FileName, Path & newImageName & Extent
    End With
    
    UploadImageFile = newImageName & Extent
End Function

الداله : GetRandomName تم التعليق عليها في الدرس السابق :

الداله UploadImageFile :

ماهو محتوى هذه الدالة :

في بدايتها متغير newImageName سوف اخزن فيه الاسم العشوائي , متغير أخر Extent أخزن في امتداد الصورة .

CommonDialog1 معروف مربح الحوار فتح و محدد انه يفتح فقط الصور jpg , gif . عن طريق الخاصية Filter .

بعد ذلك يأتي السطر :

If .FileName <> "" Then newImageName = GetRandomName

وفيه أتحقق هل أن المستخدم قد اختار صورة فعلاً : إذا كان نعم :

استدعي الدالة GetRandomName حتى تقوم بتوليد سلسلة عشوائية بعد ذلك تعيد هذه الدالة GetRandomName السلسة العشوائية للمتغير newImageName .

في الدوال عند الاستدعاء ينفذ أولاً الطرف الأيمن ثم الناتج من الطرف الأيمن يعطى للطرف الأيسر . لاحظ ما هوا نوع البيانات التي يأخذها المتغير newImageName .

ملاحظة : لمعرفة نوع البيانات ضع الموشر فوق المتغير ثم قم بالضغظ بالزر Ctrl + I .

سوق تلاحظ أننا عرفنا المتغير newImageName من نوع String , وبالتالي فإنه يجب على الداله GetRandomName أن تعيد نوع بيانات String حتى يقبل المتغير newImageName هذه البيانات .

عد إلى تعريف الداله GetRandomName لاحظ أننا عرفناها تعيد نوع بينات String .

بعد هذا العمل :

أريد معرفة إمتداد الصور التي قام المستخدم بإختيارها : لنفرض انه اختار a11.gif أريد الحصول على .gif

لدي داله هي InStrRev مشابه لعمل الدالة InStr وضيفتها البحث من اخر خانة في النص عن نص إلى بدايه السلسلة .

لذلك سوف أبحث من نهاية السلسلة والتي تمثلها الخاصية FileName طبعا تعيد مسار الصور كما يلي :

C:\My Pic\a11.gif سوف أبحث عن النقطة (.) وأبدأ البحث من نهاية السلسة .

. الداله InStrRev : الصيغة العامة لها InstrRev(Text, Search) حيث Text يمثل النص او السلسلة النصية المراد البحث بها , Search يمثل عنصر البحث . أو الكلمة التي اود البحث عنها , تعيد الدالة موقع الكلمة التي اود البحث عنها في السلسة النصية .

في حالتنا هذه سيكون الموقع الذي تعيده ألداله هوا 14 . والذي يمثل موقع النقطة في النص.

طبعا الامتداد في الغالب يكون من 4 خانات مثل .jpg أو .gif لذلك سوف استخدم ألداله mid وضيفتها إرجاء نص من نوقع محدد وبطول محدد من نص أخر

Mid(text,Start,End) : يمثل text النص المرد القطع منه , Start الموقع الذي سوف يبدأ منه الاقتطاع , End يمثل نهاية الإقتطاع .

نحن هنا نود أن نعيد أو نقطع النص التالي .gif من النص C:\My Pic\a11.gif

Extent = Mid(.FileName, InStrRev(.FileName, "."), 4)

الداله Mid بدأت من الموقع الذي تعيد الدالة InStrRev , ورجعت نص بطول 4 خانات ابتدأ من الموقع الذي تعيده الدالة InStrRev . هذا النص المعاد من الداله Mid تم خزنه في المتغير Extent .

بعد ذلك سوف احدد مسار تواجد المجلد image والداله app.path تعيد لي المسار إلى موقع الملف التنفيذي للبرنامج . وبالتالي سوف ازيد عليها فقط اسم المجلد image حتى يكون المسار بالكامل مع اسم المجلد الخاص بالصور .

ملاحضة :

المسار التذي تعيده الداله app.path أحياننا يكون على الصيغة التاليه :

C:\My Project

أو

C:\My Project\

يعني أحياننا تعيد في نهاية المسار العلامة \ والتي تحدد نهاية المسار وأحياننا لا : لذلك كان لابد التأكد أولا من وجود هذه العلامة في حال كان موجودة أضيف اسم المجلد الخاص بالصور مباشرة :

C:\My Project\images\

أما إذا لم تكن موجود أقوم بإضافة العلامه \ ثم اسم المجلد :

C:\My Project\images\

هذا الحل متمثل بشرط استخدمت الداله iiF لعمل هذا الشرط وهي مشابه للـ If

الصيغة العامة :

Iff(الشرط ,إذا كان صحيح  , إذا كان خاطئ)

وهي تعيد إحدى الحالتين الناتجة من الشرط :

مثال لتوضيح :

	B = iif(a <1 ,"OK","No")

إذا كان a إقل من 1 فستعيد الدالة iif القيمة OK للمتغير B وإلا ستعيد القيمة No للمتغير B .

المثال في الداله الخاصة بنا أيضا :

Path = IIf(Mid(App.Path, Len(App.Path) - 1) = "\", App.Path & "imges\", App.Path & "\imges\")

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

سوف استخدم حلقة دوران Loop للتأكد من ذلك : في حال انه تم توليد اسم عشوائي موجود من قبل تعيد الدالة توليد اسم عشوائي أخر وتعيد الفحص هل هذا الاسم قد تم حفظ صورة به من قبل أم لا :

Do While Dir(Path & newImageName & Extent) <> vbNullString
            newImageName = GetRandomName
         Loop

ألداله Dir تأخذ المسار وتعيد قيمة فارغة في حال كان غير موجود ولا تعيد المسار نفسة في حال كان موجود .

لذلك شرط التكرار Do While هوا أن يغير اسم جديد إذا لم تعد الدالة Dir قيمة فارغة (القيمة الفارغة تعني ان الإسم غير موجود والبحث هنا في السمار بالكامل اقصد الملف يمرر للدالة dir المسار ثم اسم الصورة ثم الامتداد ) .

بعد ضمان ان الإسم المتولد لم يخزن من قبل بإسم صورة أو لا توجد صورة تحمل نفس هذا الإسم :

نقوم بنسخ الصورة بالاسم الجديد إلى المسار المحدد :

FileCopy .FileName, Path & newImageName & Extent

[code]

بعد هذا العمل أريد أن أنفذ أخر خطوه وهي أن أخزن اسم الصوره هذا في قاعدة البينات :

وهذا بدوره له معالجة أخرى , وهوا أن أضيف هذا الاسم في الاستعلام عند حفظ بيانات الطالب :

لذلك سوف ادع الدالة UploadImageFile تعيد فقط الإسم العشوائي الذي نسخت الصورة به مع الامتداد

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

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

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

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

وللسلسة بقية . B)

#16

مش لاقي كلام اقوله لك اخي فواز غير

جزاك الله كل خير وبالتوفيق دائما

#17

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

#18

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

#19

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

#20

السلام عليكم

بارك الله فيك اخى العزيز

اتمنى منك مواصلة هذا المجهود الرائع

جزاكم الله خير الجزاء

#21

عندما رأيت عدد القراءات لهذه السلسلة والتعليقات من الزوار الأفاضل زادني ذلك حماس في إكمال هذه السلسلة التي بدأت الجزء الأول منها عن بناء Classes أو الفئات .

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

موضوع الدرس : تمرير المتغيرات بين الإجراء والدوال :

] مقدمه للمبتدئين - والمحترفين [

المتغير أولا ما هوا المتغير :

هوا عبارة عن قيمة , قد تكون هذه القيمة رقم أو نص أو حرف أو منطق أو غيره من القيم .

كيف يتم التعامل معه في اللغات البرمجية :

المتغير هوا عبارة عن اسم يشير إلى قيمة في الذاكرة .

القيمة أو البيانات التي يأخذها المتغير.

جميع القيم أو البينانات تقسم إلى الأنواع التالية : بيانات رقمية Number وهذه البينات تقسم أيضا رياضيا إلى بيناتا رقمية كسريه , وأخرى غير كسرية , من أنواع البيانات أيضا بيانات نصية قد تكون حرفية char أو سلسلة نص String , وهناك أيضا بينانات أخرى عديدة تقسم بتصنيف دقيق حتى يسهل معالجة هذه البيانات عند الاستخدام .

هذا النوع من التصنيف (رقمي بأنواعه – نصي بأنواعه – منطقي [True-False]... الخ ) يسمى بالتصنيف القياسي Standard Data Type .

هناك تقسيم أخر لنوع البيانات Data Type التي يمكن للمتغير أن يأخذها هذا التقسيم يسمى Object أي كائني

من هذه الأنواع الـ Classess .

مثال :

عند تعريف متغير نستخدم عبارة Dim للتعريف ثم نحدد اسم المتغير Variable name ثم بعد ذلك الكلمة As والتي تحدد نوع البيانات التي سوف يأخذها المتغير (أو ما هي البيانات التي سيتم وضعها في الذاكرة هل هي نص أم حرف أم رقم أم إيش بالضبط)

Dim Fawaz As String 
Dim F
Dim Al-hassel as Integer
Dim Value as Double

جميع التعريفات السابقة تعتبر من نوع البيانات القياسية :Stander Data Type .

[String- Integer- Double ] .

ملاحظة السطر الثاني تم تعريف متغير ولكن لم يحدد نوع البيانات له لذلك سوف يقوم visual Basic بإعطاء المتغير F نوع بيانات افتراضي هذا النوع من البيانات أسمه Variant وهذا النوع من التعريفات لا يفضل لأنه يأخذ حجم كبير في الذاكرة تقريبا 16Byte .

ما هي الفائدة من تعريف وتحديد نوع البيانات للمتغيرات :

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

كيف :

لدي الرقم 10 أريد أن أضعه في متغير ما ودعت الحاجة لذلك .

سوف أقوم بتعريف متغير من نوع بيانات Data Type رقمية Number ولكن ما هوا نوع هذا الرقم لأن البيانات الرقمية تنقسم إلى قسمين الأول بيانات صحيحة وبيانات حقيقية (كسرية وغير كسريه) لذلك يجب أن أحدد هل القيمة 10 كسرية إذا كانت سوف أستخدم إحدى البينات التاليه [Double – Single] هذه الأنواع من البيانات تقبل الأرقام الكسرية أي خصصت لهذه المهام أما في حالتنا فالقم 10 غير كسري لذلك سوف أخذ بيانات رقمية غير كسرية وهذه البيانات [Byte – Integer - Long] يمكنني أن أختار واحد منها .

لكن معيار الاختيار هنا ليس عشوائي بل يجب أن يكون دقيق لماذا لأن أنواع البيانات الثلاثة المذكورة تحجز في الذاكرة مساحات متفاوتة حيث Byte حدوده من 0 إلى 255 رقم , فإن وقع معاك رقم في هذا الإطار قم بحجز نوع البيانات للمتغير من نوع Byte . أما إذا زادة القيمة فهيا في الترتيب التالي Integer يحجز في الذاكرة 2Byte نوع البيانات الأخر Long يحجز 8Byte .

هنا نقطة مهمة في اختيار أنواع البيانات :

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

ملاحضة أخرى :

بعض المبرمجين المحترفين ومهندسى البرمجيات وبالذات بانو Clasess يفضلون وبشده عدم الإكثار من تعريف المتغيرات في البرامج إلا إذا دعت الحاجة له .

ليش : :blink:

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

شوف نحن عندما نعرف متغير ماذا يحصل في الحاسوب : يتم حجز خانه أو مساحة حجم هذه المساحة مبني على نوع البيانات Integer أو String أو غيره . فلو فرضنا أنه تم حجز 2Mb في الذاكرة , ماهي العمليات التي تتم في هذه الحالة . طبعا التخزين في الذاكرة يتم بشكل عشوائي . بحيث يتم البحث عن مساحة فارغة في الذاكرة ثم بعد ذلك يتم تحديد عنوان لهذه الخانة في الذكرة بحيث يعطى عنوان من بداية الخانة وعنوان في نهاية الخانة ليوضح حدود ألخانه التي تم حجزها , طيب هذا العنوان عادة ما يتم تكوينية بالنظام الست عشري Hex Decimal . هذه العمليات ألتي تتم في ألذكره لحجز هذه الخانة تتطلب وقت في المعالجة , عند كثرتها تسبب بطئ في النظام , طبعا هذا العمليات تتم في لمح البصر لكن أقصد تكرارها يسبب بطئ خاصة إذا كانت الذاكرة شبه ممتلئة ,

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

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

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

أعرف أني عقدتها شويه بس أتمنى أن يكون القصد مفهوم وإن لم يكن كذلك قم بعمل تراجع لهذا الكلام وبدأ معي من هنا :rolleyes:

كيف أمرر المتغير عن طريق الإجراء :

Dim Pi AS  Double 
Pi = 180/360

SubName Pi

من الكود المذكور تم تعريف متغير من نوع بيانات رقمية كسرية Double ثم تم إعطاء المتغير القيمة 180/360 بعد ذلك تم استدعاء الإجراء المسمى SubName وهذا الاسم افترضت أنك قد قمت بتعريفه من قبل وكتابه الكود اللازم به , تم تمرير المتغير Pi إلى الإجراء هذا وسيقوم الإجراء باستقبال القيمة التي حملها المتغير Pi ويتعامل معها بعذ ذلك بحسب الحاجة .

Private SubName (val As Double)
	---------
	---------
	MsgBox val
End Sub

الإجراء SubName أستقبل قيمة المتغير Pi ولكن الذي استقبل هذه القيمة هوا المتغير val في الإجراء ولاحظ أن نوع البيانات للمتغير المستقبل يجب أن تكون نفس بيانات المتغير العاطي أو الممرر للإجراء .

ملاحظات مهمة :

هنا ظهرت مشكلة وهي أنه لدي متغير اسمه Pi من نوع Double تم الحجز له في الذاكرة 8Byte وتم وضع ناتج القسمة 180/360 في هذه المساحة .

في الإجراء تم تعريف المتغير val في الذاكرة من نوع Double تم الحجز له في الذاكرة 8Byte . بعد ذلك تم وضع القيمة الخاصة بالمتغير Pi وتم وضعها في الخانة التي تم حجزها للمتغير val .

لاحظ من الكلام : ماذا تستنتج : أنه تم تعريف خانتان في الذاكرة كلاهما تحمل نفس القيمة . يعني هذا تكرار في البيانات في الذاكرة , طيب أيش الحل في هذه الحالة . (المتغير Pi له ناتج القسمة في المساحة الخاصة به , والمتغير val أعطاه المتغير Pi نسخة من إلي معا ه وخزنها المتغير val في المساحة الخاصة به)

قام خبراء مهندسو البرمجيات بإيجاد حل لها على النحو التالي :

[فكر بها قبل أن ترى الحل – و شوف هل عقلك قادك للنتيجة التي تم معالجة هذه المشكلة بها .]

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

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

هذا باختصار شديد حتى لا أدع الفهم والتركيز لدى القارئ غير مشتت , (هذا إذا لم يشتت ). :rolleyes:

كيف نستفيد من هذه الحالة :

لحل مشكلة تكرار القيم أو البيانات في الذاكرة في هذه الحالة : وضع الحل التالي :

وهوا تم تعريف متغيرين Pi و val من نفس نوع البيانات ولكن الفرق أنه الأول له خانه في الذاكرة محجوزة وبها قيم عندما أود أن أعطيها للمتغير val لن أعطيه القيمة وإنما سوف أعطيه نفس العنوان الفيزيائي لمعطى للمتغير Pi لذلك سوف يشير هذا العنوان إلى مكان الخانة الخاصة بالمتغير Pi ولن يتم حجز خانه للمتغير val في الذاكرة لأنه يشير إلى خانه موجودة مسبقاً . وبالتالي فهناك متغيران يشيران لخانة واحدة وبالتالي : من هذه الملاحظة أستنتج أنه لو غير المتغير Pi وأعطى قيمة أخرى غير القيمة التي هي مخزنه في الخانة على الذاكرة والتي هي في مثالنا 180/360 سوف تتغير القيمة عند المتغير val ليش لأنه يشير لنفس الخانة أو الموقع في الذاكرة , والعكس صحيح فلو غيرت قيمة المتغير val سوف يحدث التعديل على الخانة في الذاكرة وبالتالي سيتم تغير الخانة التي يشير إليها المتغير Pi , أصلاً هي خانة واحدة الاثنين هما يشيران لها فلو وحد غير فيها شي التغير ساري لأثنين .

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

الحالة الأولى : والحالة الثانية :

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

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

كيف يمكن تطبيق هذه الحالات في Visual Basic :

التمرير بالقيمة By Value و التمرير بالمرجع By References :

يعتبر Visual Basic التمرير دائما بين الإجراءات والدوال وهو التمرير باستخدام أسلوب By References افتراضيا , يعني إذا لم تحدد نوع التمرير هل بالقيمة أم بالمرجع (العنوان)

ففي الإجراء التالي يعتبر التمرير للمتغير Name هوا بالمرجع By References:

Private Sub Command1_Click ()
	Dim Name As String 
	Name = "Fawaz M. Al-Shamri"

	SetName Name
	MsgBox Name
End Sub

Private Sub SetName (n AS String)
	MsgBox n
	n = "Sami Ali"
End Sub

لاحظ أني لم أحدد نوع التمرير لذلك سوف يفترض فيجول بيسك التمرير بالمرجع أو العنوان بدليل أنه مررت قيمة للمتغير Name إلى الإجراء ليستقبل هذه اليقمة المتغير n وبالتالي عملت MsgBox للمتغير n الناتج سيكون هوا القيمة Fawaz M. Al-Shamri . بعد ذلك غيرت القيمة التي حصل عليها المتغير n إلى Sami Ali .

لو كان التمرير في هذه الحالة بالمرجع فإن المتغير n يشير إلى الخانة التي يشير إليها المتغير Name وبالتالي وليس لدى المتغير n خانه أو مساحة في الذاكرة وإنما اعتبرت خانته هي خانه المتغير Name وبالتالي سوف يعدل البرنامج على الخانة التي يشير إليها العنوان الفيزيائي الخاص بالمتغير n وهي نفس الخانه التي يشير إليها العنوان الفيزيائي الخاص بالمتغير Name . من خلال هذا الكلام سيعود المترجم إلى زر الأمر Command1_Click بعد تنفيذ الإجراء SetName تتوقع ما هي نتيجة السطر التالي بعد استدعاء الإجراء إذا كان التمرير بالمرجع (أو العنوان) :

MsgBox Name

إذا كانت بالمرجع سوف تكون النتيجة هي : Sami Ali أما إذا كانت بالقيمة فستكون Fawaz M. Al-Shamri

طبعا الناتج هنا هو Sami Ali لماذا لأن التمرير قد مرر بالمرجع لأن visual Basic أعتبره افتراضي لأنه لم يحدد .

عند التمرير بالقيمة نعدل الإجراء إلى التالي عند تعريف المتغير n لنخبره أنه سيستقبل عنوان حتى لا يحجز خانه.

Private Sub SetName(ByVal n As String)
    MsgBox n
    n = "Sami Ali"
End Sub

الناتج من الإجراء بعد التعديل سيكون Fawaz M. Al-Shamri : ليش عود لبدايه الشرح :blink: .

يمكن أيضا تحديد نوع التمرير بالمرجع كما يلي :

Private Sub SetName(ByRef n As String)
    MsgBox n
    n = "Sami Ali"
End Sub

عمر المتغير :

المتغير n في الإجراء متى يتم حذفه .

يجب أن تعرف أن المتغيرات لها نوعين من التعريف

النوع الأول يسمى تعريف محلي وهوا يستخدم بين الإجراءات والدوال يعني بداخل Block برمجي أيا كان نوعه

ثانيا تعريف عام : وهوا عام يمكن لكل Block الوصول إلية أي انه عام على مستوى البرنامج ككل .

التعريف المحلي يتم التخلص منه وحذفه من الذاكرة لحظة الانتهاء من الـ Block البرمجي , ففي مثالنا تم تعريف المتغير n في الإجراء وبذلك سيتم مسحة من الذاكرة لحظة الخروج من الأجراء End Sub يعني انه هنا فقط للاستخدام الخاص .

عند عملية الحذف :

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

المتغيرات من نوع Object :

قلت أنها من نوع Clasess وهي فئات . من الأمثلة علها (CommandButton , TextBox,Form……)

جميع الأدوات في صندوق الأدوات تعتبر متغيرات من نوع Object .

مثال :

	Dim frm As Form
    Dim Button As CommandButton
     Dim Text  As TextBox

جميع هذه المتغيرات تعتبر من نوع فئات Classess أو Object وهذا الموضوع سوف نتعرف على كيفية تصميمه في بقية السلسة بإذن الله .

التمرير بنفس الفكرة التي يتم فيها تمرير المتغيرات بين الإجراءات والدوال :

مثال :

سوف أقوم بتوليد نافذه تظهر في زمن التنفيذ Run Time :

Private Sub Command2_Click()
    Dim f As Form
    Set f = New Form1
    
   With f
         .Caption = "My Form"
         .Width = 180
         .Show
         .Move 150, 150
   End With

End Sub

مثال أخر عن وضع مصفوفة TextBox في نافذة Form1 في زمن التنفيذ Run Time

ضع text Box على النافذة وغير الخاصة Index إلى 0 ثم ضع الكود التالي في زر امر Command

Private Sub Command2_Click()
   Dim index As Integer

    index = Text1.Count
    Load Text1(index)
    Text1(index).Top = Text1(index - 1).Top + _
        Text1(index - 1).Height + 30
    Text1(index).text = "Text1(" & Format$(index) & ")"
    Text1(index).Visible = True
End Sub

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

وأترك البقية لبقية الدروس بإذن الله تعالي .

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

وللسلسلة بقية إلى القاء .( أتمنى الدعاء لي بالنجاح فلدي اختبارات حالياُ). B)

#22

اخي فواز أولا يجب أن أقول لك ان شرحك أكثر من ممتاز وادعو لك الله ان يوفقك في اختبراتك ان شاء الله

واتمني منك شرح دروس الفئات بنفس المستوي ان شاء الله

وكان لي اسفسار في الجزء السابق من الموضوع وهو

المتغيرات التي تكون علي مستوي النافذة او المحلية تأخذ قيمة من الذاكرة فعند الانتقال الي نافذة أخري هل انها تأخذ قيمة من الذاكرة أيضا

واتمني لك التوفيق في اختباراتك ان شاء الله

#23

السلام عليكم/ اخى العزيز

بارك الله فيك على ما تقدم لنا من جهد وفير

عندى سؤالين:

1- كيفية استدعاء الداله uploadimagefile

فى المثال السابق

2-

Do While Dir(Path & newImageName & Extent) <> vbNullString

newImageName = GetRandomName

Loop

ما سبق يمكن ان يطبق فى حالة العوده بقيمه فارغه

لكن اذا حصل العكس الحلقه التكراريه سوف تستمر الى مالانهايه؟!

ارجو منكم الافاده على ذلك ونتمنى مواصلة جهدكم المشكور

السلام عليكم

#24

بالنسبة لسؤالك أخي yasserstars :

المتغيرات لها عمر أو مدى حياتها تكون على مستوى الـ Block البرمجي بشكل عام لذلك فهي تنتهي و يتم تدميرها من الذاكرة فور أنتها الـ Block البرمجي من الاستخدام .

كيف :

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

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

فور إلغاء تحميل النافذة بالكامل من الذاكرة .

Unload Form1
Set Form1 = Nothing

بهذه الحالة يتم إلغاء النافذة بما تحتويه من الذاكرة .

وفي نفس اللحظة يتم تحميل النافذة في الذاكرة وإنشاء المتغيرات العامة والكتل في النافذة لحظة تحميل النافذة في الذاكرة عند السطر التالي :

Form1.Show

أخي thelast_pharon :

بالنسبة لسؤالك حول الدوران Loop : في الدالة UploadImageFile :

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

الشرط طالما أن الدالة Dir تعيد نفس المسار (أي أنه موجود ) ولد أسم عشوائي جديد ثم تحقق هل الاسم العشوائي موجود عن طريق الدالة Dir في حال لم يكن هذا المسار الجديد (اسم الصورة موجود في مجلد الصور ) ستعيد الدالة Dir القيمة vbNullString وبالتالي لن يحقق شرط الدوران Loop لأن الشرط هوا لا يساوي vbNullString , وبالتالي سوف ينتقل المفسر للكود بعد Loop ولن يحدث أي Loop مالا نهاية (ولن يحدث Over Flow) .

بالنسبة لكيفية استخدام الدالة سوف أتحدث عنه في الدرس التالي :

الدرس الخامس :

استخدام Module :

مقدمة حتى الوصول إلى Module :

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

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

لنأخذ هذه الحالة :

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

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

لو أردت نافذة حسابات العملاء أو نافذة دليل الحسابات :

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

طيب كم عدد النوافذ في البرنامج ؟

هنا ظهرت مشكلة وهي أنه لا زال هناك تكرار فعليا للكود في البرنامج ككل هذا التكرار يتمثل بتكرار الـ Block البرمجي (الإجراء أو الدالة ) , بحيث أني قمت بعمل إجراء لمعالجة 6 صناديق نص في النافذة Form1 وإجراء في النافذة Form3 ليعالج 3 صناديق نص TextBox .

يعتبر هذا الإجراء مكرر في النافذتين إذا عند كبر البرنامج ظهرت مشكلة وهي في تكرار الإجراءات والدوال في البرنامج .

قام خبراء البرمجيات بوضع حل لهذه المشكلة وهوا متمثل بما يلي :

قالوا طالما أن الوظيفة لدينا هي واحدة (منع المستخدم من الكتابة بالحروف) . إذاُ يمكننا أن نستفيد من الوظيفة الموجودة في البرنامج ككل وبدون أن نكرر هذه الوظيفة يعني كيف :

أين هي الوظيفة :

لنفترض أن الوظيفة (الإجراء الخاص بمعالجة عدم الكتابة بالأحرف ) موجودة في النافذة Form1 ولدي الحالة في Form3 بها 3 صناديق نص وأريد أن امنع المستخدم من الكتابة بالحرف فيها .

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

كيق :

ضع 2 Form وضع في النفاذة الأول 3 صناديق نص والثانية 5 صناديق نص : ضع الدالة الخاصة بمعالجة منع الأرقام في النافذة From1

ولتكن الدالة كما يلي( سوف أضع الكود التالي غير المذكور في الدروس السابقة حتى تعم الفكرة في تنوع الأساليب البرمجية في كتابة الكود ) :

Private Function GetNumberOnle(Key As Integer)
    If IsNumeric(Chr(Key)) Or (Chr(Key) = vbBack) Then
        GetNumberOnle = Key
    Else
        GetNumberOnle = 0
    End If
    
End Function

طبعا في النافذة سوف استدعي الدالة كما تعودنا مباشرة في حدث KeyPress التابع للـ TextBox

Private Sub Text1_KeyPress(KeyAscii As Integer)
KeyAscii = GetNumberOnle(KeyAscii)
End Sub

وهكذا بالنسبة لبقية الـ TextBox :

طيب هذا على مستوى النافذة Form1 كيف سأقوم باستدعاء هذه الدالة أو أستفيد منها من نافذة أخرى .

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

في النافذة Form2 في حدث KeyPress التابع للـ TextBox استدعي الدالة كما يلي :

Private Sub Text1_KeyPress(KeyAscii As Integer)
KeyAscii = Form1.GetNumberOnle(KeyAscii)
End Sub

هنا تمكنا من استخدام الدالة الموجودة في النافذة Form1 لتعمل لنا المعالجة المطلوبة وبذلك تخلصنا من مشكلة تكرار الدالة (يعني ما فيش داعي أني أعملها مره ثانية في النافذة Form2 يكفي أن أستدعيها فقط )

وفي هذه الحالة يجب أن أحدد نوع الوصول للدالة GetNumberOnle ليعرف المفسر انه يمكن أن تستخدم هذه الدالة في النافذة Form1 من أي مكان في البرنامج .

وبذلك سوف أغير تعريف الدالة GetNumberOnle من Private إلى Public .

هنا عملنا انجاز وتخلصنا من مشكلة تكرار الدوال والإجراءات , ولكن لتم تتم الفرحة بل ظهرت مشكلة أخرى تعتبر مشكلة كبيرة نوعاً ما :

تحدث في ردي على سؤال الأخ yasserstars بالنسبة لعمر المتغيرات عن الـForm وما تحتوية من كتل ومتى يتم إنهائها من الذاكرة :

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

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

الحين انا فتحت Form1 ماذا يتم : يتم وضع ما تحتوية النافذة Form1 في الذاكرة فإن كان هناك تعريف متغيرات عامة سوف يتم تحميلها وحجز المساحات لها في الذاكرة والإجراءات والدوال كل الكتل البرمجية يتم تحميلها في الذاكرة . المشكلة الأكبر أن المتغيرات التي من نوع Object وهي الأدوات تعتبر من المثقلات في البرنامج , من هذه الأنواع الأدوات Ocx مثل Command و Picture Box و TextBox وغيرها من الأدوات التي هي موجودة في صندوق الأدوات :

ملاحظة :

جميع الأدوات هذه تنتج بتقنية ActiveX والتي يكون امتدادها Ocx وقد تكون Dll .

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

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

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

من خلال ما حصل ماذا تلاحظ :

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

الشي الثاني :

تجد بطئ وهذا البطء ناتج من عملية تحميل النفاذة Form2 إلى الذاكرة .

طيب السؤال لي يطرح نفسه الآن وبعد استخدام الدالة , أنك قمت بإغلاق النافذة Form2 هل ستضل النافذة Form1 في الذاكرة .

من خلال تجاربي وجت فعلاً أن النافذة Form1 ستضل في الذاكرة موجودة . وبالتالي يعتبر هذا خلل فاذح في هندسة البرمجيات وهوا وجود كمية كبيرة قد تكون من البرنامج في الذاكرة دون الفائدة في استخدامها .

ملاحظة :

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

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

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

لحل جميع هذه النقاط تم وضع ما يسمى Modules أو الوحدات النمطية :

ما هوا الـ Module:

عبارة عن كتله برمجية (حاوية مجموعة من الكتل) بها مجموعة من الكتل Block تتمثل بالإجراءات والدوال والأحداث والخصائص والمتغيرات .

الفرق بينها وبين Form هوا :

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

الغرض الأساسي لها هوا وضع الدوال وغيرها من العمليات البرمجية للاستخدام العام في البرنامج ككل .

كيفية الاستخدام :

من قائمة Project اختر Add Module

مثال يوضح كيف يمكن أن استفيد منه في البرنامج : قم بوضع Module في البرنامج ثم بداخلة عرف متغير من نوع عام Public كما لي :

Public SetName As String

لماذا Public ليحدد أن هذا المتغير يمكن الوصول إليه من أي مكان في البرنامج.

في النافذة Form1 ضع صندوق نص وكذلك في النافذة Form2 ضع صندوق نص

في النفاذة Form1 ضع الكود التالي في زر أمر :

SetName = Text1.Text

لاحظ كيف استخدمت المتغير في النافذة Form1 والذي تم تعريفة في الوحدة النمطية Module .

بعد ذلك توجه للنافذة Form2 وضع الكود التالي في زر أمر :

Msgbox SetName

بعد ذلك قم بتقيد البرنامج على Form1 قم بإغلاق النافذة Form1 وفتح النافذة Form2 سوف تلاحظ ظهور الرسالة بالقيمة التي تم كتابتها في النافذة From1

متى يتم إنها المتغير SetName:

يتم ذلك عند أنها البرنامج بالكامل.

وللسلسة بقية, في الدرس المقبل سوف نتعرف على Module عن قرب ونأخذ أمثلة . B)

#25

الدروس جميلة وفقك الله والي الامام دائما

مواضيع مشابهة