5. واجهات المستخدم الرسومية باستخدام VB.NET و VS.NET
نهدف هنا إلى توضيح كيفية إنشاء واجهات رسومية باستخدام VB.NET. سنبدأ أولاً باستعراض الفئات الأساسية لمنصة .NET التي تتيح لنا إنشاء واجهة رسومية. لن نستخدم في البداية أي أداة للتوليد التلقائي. ثم سنستخدم Visual Studio.NET (VS.NET)، وهي أداة تطوير من Microsoft تسهل تطوير التطبيقات باستخدام لغات .NET، ولا سيما إنشاء واجهات المستخدم الرسومية. الإصدار المستخدم من VS.NET هو الإصدار باللغة الإنجليزية.
5.1. أساسيات الواجهات الرسومية
5.1.1. نافذة بسيطة
لننظر إلى الكود التالي:
' خيارات
Option Strict On
Option Explicit On
' مساحات الأسماء
Imports System
Imports System.Drawing
Imports System.Windows.Forms
' فئة النموذج
Public Class Form1
Inherits Form
' المنشئ
Public Sub New()
' عنوان النافذة
Me.Text = "Mon premier formulaire"
' أبعاد النافذة
Me.Size = New System.Drawing.Size(300, 100)
End Sub
' دالة الاختبار
Public Shared Sub Main(ByVal args() As String)
' عرض النموذج
Application.Run(New Form1)
End Sub
End Class
يتم ترجمة الكود السابق ثم تنفيذه
يؤدي التنفيذ إلى عرض النافذة التالية:

عادةً ما تُشتق واجهة المستخدم الرسومية من الفئة الأساسية System.Windows.Forms.Form:
تُعرّف الفئة الأساسية Form نافذة أساسية تحتوي على أزرار الإغلاق والتكبير/التصغير وحجم قابل للتعديل، ... وتدير الأحداث على هذه الكائنات الرسومية. هنا نقوم بتخصيص الفئة الأساسية عن طريق تحديد عنوان لها وعرضها (300) وارتفاعها (100). ويتم ذلك في منشئها:
Public Sub New()
' عنوان النافذة
Me.Text = "Mon premier formulaire"
' أبعاد النافذة
Me.Size = New System.Drawing.Size(300, 100)
End Sub
يتم تحديد عنوان النافذة بواسطة الخاصية Text، بينما يتم تحديد أبعادها بواسطة الخاصية Size. يتم تعريف Size في مساحة الأسماء System.Drawing، وهي عبارة عن بنية. تقوم الإجراء Main بتشغيل التطبيق الرسومي بالطريقة التالية:
Application.Run(New Form1)
يتم إنشاء نموذج من النوع Form1 وعرضه، ثم يبدأ التطبيق في مراقبة الأحداث التي تحدث على النموذج (النقرات، تحركات الماوس، ...) ويقوم بتنفيذ الأحداث التي يديرها النموذج. في هذه الحالة، لا يدير النموذج الخاص بنا أي أحداث بخلاف تلك التي تديرها الفئة الأساسية Form (النقرات على أزرار الإغلاق، والتكبير/التصغير، وتغيير حجم النافذة، وتحريك النافذة، ...).
5.1.2. نموذج مزود بزر
لنضف الآن زرًا إلى نافذتنا:
' خيارات
Option Strict On
Option Explicit On
' مساحات الأسماء
Imports System
Imports System.Drawing
Imports System.Windows.Forms
' فئة النموذج
Public Class Form1
Inherits Form
' السمات
Private cmdTest As Button
' المنشئ
Public Sub New()
' العنوان
Me.Text = "Mon premier formulaire"
' الأبعاد
Me.Size = New System.Drawing.Size(300, 100)
' زر
' الإنشاء
Me.cmdTest = New Button
' الموضع
cmdTest.Location = New System.Drawing.Point(110, 20)
' الحجم
cmdTest.Size = New System.Drawing.Size(80, 30)
' التسمية
cmdTest.Text = "Test"
' مدير الأحداث
AddHandler cmdTest.Click, AddressOf cmdTest_Click
' إضافة زر إلى النموذج
Me.Controls.Add(cmdTest)
End Sub
' مدير الأحداث
Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
' تم النقر على الزر - يتم الإبلاغ عن ذلك
MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
' وظيفة الاختبار
Public Shared Sub Main(ByVal args() As String)
' يتم عرض النموذج
Application.Run(New Form1)
End Sub
End Class
لقد أضفنا زرًا إلى النموذج:
' زر
' إنشاء
Me.cmdTest = New Button
' الموضع
cmdTest.Location = New System.Drawing.Point(110, 20)
' الحجم
cmdTest.Size = New System.Drawing.Size(80, 30)
' التسمية
cmdTest.Text = "Test"
' مدير الأحداث
AddHandler cmdTest.Click, AddressOf cmdTest_Click
' إضافة زر إلى النموذج
Me.Controls.Add(cmdTest)
تحدد الخاصية Location إحداثيات (110,20) للنقطة العلوية اليسرى للزر باستخدام بنية Point. يتم تحديد عرض الزر وارتفاعه بقيمتي (80،30) باستخدام بنية Size. تسمح الخاصية Text للزر بتحديد نص الزر. تحتوي فئة الزر على حدث Click محدد على النحو التالي:
حيث EventHandler هي دالة «مفوضة» لها التوقيع التالي:
وهذا يعني أن معالج الحدث [Click] الخاص بالزر يجب أن يكون له التوقيع الخاص بالدالة المفوضة [EventHandler]. وهنا، عند النقر على الزر cmdTest، سيتم استدعاء الأسلوب cmdTest_Click. وقد تم تعريف هذا الأسلوب على النحو التالي وفقًا للنموذج EventHandler السابق:
' مدير الأحداث
Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
' تم النقر على الزر - يتم الإبلاغ عن ذلك
MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
يقتصر الأمر على عرض رسالة:

يتم ترجمة الفئة وتنفيذها:
dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
dos>frm2
تُستخدم الفئة MessageBox لعرض الرسائل في نافذة. وقد استخدمنا هنا المنشئ
Overloads Public Shared Function Show(ByVal owner As IWin32Window,ByVal text As String,ByVal caption As String,ByVal buttons As MessageBoxButtons,ByVal icon As MessageBoxIcon) As DialogResult
text | الرسالة المراد عرضها |
caption | عنوان النافذة |
buttons | الأزرار الموجودة في النافذة |
icon | الرمز الموجود في النافذة |
يمكن أن تأخذ المعلمة buttons قيمها من بين الثوابت التالية:
الثابت | الأزرار |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
يمكن أن تأخذ المعلمة icon قيمها من بين الثوابت التالية:
![]() | نفس الشيء توقف | ||
نفس الشيء تحذير | ![]() | ||
كما هو الحال مع Asterisk | ![]() | ||
![]() | نفس الشيء يدويًا | ||
![]() |
الطريقة Show هي طريقة ثابتة تُرجع نتيجة من النوع System.Windows.Forms.DialogResult، وهي عبارة عن قائمة:
public enum System.Windows.Forms.DialogResult
{
Abort = 0x00000003,
Cancel = 0x00000002,
Ignore = 0x00000005,
No = 0x00000007,
None = 0x00000000,
OK = 0x00000001,
Retry = 0x00000004,
Yes = 0x00000006,
}
لمعرفة الزر الذي ضغط عليه المستخدم لإغلاق النافذة من النوع MessageBox، نكتب:
dim res as DialogResult=MessageBox.Show(..)
if res=DialogResult.Yes then
' ضغط على زر «نعم»
...
end if
5.2. إنشاء واجهة رسومية باستخدام Visual Studio.NET
سنستعرض بعض الأمثلة التي تناولناها سابقًا، وسنقوم الآن بإنشائها باستخدام Visual Studio.NET.
5.2.1. الإنشاء الأولي للمشروع
- قم بتشغيل VS.NET واختر الخيار Fichier/Nouveau/Projet

- وأدخل مواصفات مشروعك
![]() |
- اختر نوع المشروع الذي تريد إنشاؤه، وهنا مشروع VB.NET (1)
- حدد نوع التطبيق الذي تريد إنشاؤه، هنا تطبيق ويندوز (2)
- حدد المجلد الذي تريد وضع المجلد الفرعي للمشروع فيه (3)
- حدد اسم المشروع (4). سيكون هذا أيضًا اسم المجلد الذي سيحتوي على ملفات المشروع
- يتم تكرار اسم هذا المجلد في (5)
- يتم بعد ذلك إنشاء عدد من المجلدات والملفات ضمن المجلد i4:
المجلدات الفرعية لمجلد «project1» ![]() |
من بين هذه الملفات، هناك ملف واحد فقط مهم، وهو الملف form1.cs الذي يمثل الملف المصدر المرتبط بالنموذج الذي تم إنشاؤه بواسطة VS.NET. سنعود إلى هذا الموضوع لاحقًا.
5.2.2. نوافذ واجهة VS.NET
تُظهر واجهة VS.NET الآن بعض عناصر مشروعنا i4:
لدينا نافذة لتصميم الواجهة الرسومية:
![]() |
من خلال سحب عناصر التحكم من شريط الأدوات (toolbox 2) ووضعها على سطح النافذة (1)، يمكننا إنشاء واجهة رسومية. إذا حركنا الماوس فوق «toolbox»، فإنها تتوسع وتظهر عددًا من عناصر التحكم:

في الوقت الحالي، لا نستخدم أيًا منها. وما زلنا على شاشة VS.NET، نجد نافذة مستكشف الحلول "Explorer Solution":

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

يمكن الوصول إلى هذين العنصرين مباشرةً من نافذة «Solution Explorer»:
![]() | ![]() |
تتراكم النوافذ المفتوحة في نافذة التصميم الرئيسية:

هنا يشير Form1.vb[Design] إلى نافذة التصميم، بينما يشير Form1.vb إلى نافذة الكود. يكفي النقر على إحدى علامات التبويب للانتقال من نافذة إلى أخرى. هناك نافذة مهمة أخرى تظهر على شاشة VS.NET وهي نافذة الخصائص:

الخصائص المعروضة في النافذة هي خصائص عنصر التحكم المحدد حاليًا في نافذة التصميم الرسومي. يمكن الوصول إلى نوافذ مختلفة للمشروع من خلال القائمة Affichage:

تتضمن هذه القائمة النوافذ الرئيسية التي تم وصفها للتو بالإضافة إلى اختصارات لوحة المفاتيح الخاصة بها.
5.2.3. تنفيذ مشروع
على الرغم من أننا لم نكتب أي كود، إلا أن لدينا مشروعًا قابلاً للتنفيذ. اضغط على F5 أو Déboguer/Démarrer لتنفيذه. ستظهر لنا النافذة التالية:

يمكن تكبير هذه النافذة أو تحويلها إلى أيقونة أو تغيير حجمها أو إغلاقها.
5.2.4. الكود الذي تم إنشاؤه بواسطة VS.NET
لنلقِ نظرة على الكود (Affichage/Code) لتطبيقنا:
Public Class Form1
Inherits System.Windows.Forms.Form
#المنطقة "رمز تم إنشاؤه بواسطة مصمم نماذج Windows"
Public Sub New()
MyBase.New()
'هذا الاستدعاء مطلوب من قبل مصمم نماذج Windows.
InitializeComponent()
'أضف أي عملية تهيئة بعد الاستدعاء InitializeComponent()
End Sub
'تقوم الطريقة البديلة Dispose للنموذج بمسح قائمة المكونات.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'مطلوب من قِبل مصمم نماذج Windows
Private components As System.ComponentModel.IContainer
'REMARQUE: الإجراء التالي مطلوب من قبل مصمم نماذج Windows
'يمكن تعديلها باستخدام مصمم نماذج Windows.
'لا تقم بتعديلها باستخدام محرر الكود.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
Me.Text = "Form2"
End Sub
#نهاية المنطقة
End Class
تُشتق واجهة المستخدم الرسومية من الفئة الأساسية System.Windows.Forms.Form:
تُعرّف الفئة الأساسية Form نافذة أساسية مزودة بأزرار الإغلاق والتكبير/التصغير وحجم قابل للتعديل، ... وتدير الأحداث على هذه الكائنات الرسومية. يستخدم مُنشئ النموذج طريقة InitializeComponent التي يتم فيها إنشاء عناصر التحكم في النموذج وتهيئتها.
Public Sub New()
MyBase.New()
'هذا الاستدعاء مطلوب من قبل مصمم Windows Form.
InitializeComponent()
'أضف أي تهيئة بعد الاستدعاء InitializeComponent()
End Sub
يمكن تنفيذ أي مهام أخرى في المُنشئ بعد استدعاء InitializeComponent. الطريقة InitializeComponent
Private Sub InitializeComponent()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 53)
Me.Name = "Form1"
Me.Text = "Form1"
End Sub
يحدد عنوان النافذة "Form1" وعرضها (292) وارتفاعها (53). يتم تحديد عنوان النافذة بواسطة الخاصية Text، بينما يتم تحديد الأبعاد بواسطة الخاصية Size. يتم تعريف Size في مساحة الأسماء System.Drawing وهو عبارة عن بنية. لتشغيل هذا التطبيق، يتعين علينا تعريف الوحدة النمطية الرئيسية للمشروع. وللقيام بذلك، نستخدم الخيار [Projets/Propriétés]:

في [Objet de démarrage]، نشير إلى [Form1] وهو النموذج الذي أنشأناه للتو. لبدء التنفيذ، نستخدم الخيار [Déboguer/Démarrer]:

5.2.5. التجميع في نافذة DOS
الآن، دعونا نحاول ترجمة هذا التطبيق وتشغيله في نافذة DOS:
dos>dir form1.vb
14/03/2004 11:53 514 Form1.vb
dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb
vbc : error BC30420: 'Sub Main' est introuvable dans 'Form1'.
يشير المُجمِّع إلى أنه لا يجد الإجراء [Main]. في الواقع، لم يقم الإجراء VS.NET بإنشائه. ومع ذلك، فقد صادفناه بالفعل في الأمثلة السابقة. وهو يأخذ الشكل التالي:
Shared Sub Main()
' يتم تشغيل التطبيق
Application.Run(New Form1) ' où Form1 est le formulaire
End Sub
لنضف الكود السابق إلى كود [Form1.vb] ونعيد التحويل البرمجي:
dos>vbc /r:system.dll /r:system.windows.forms.dll form2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
Form2.vb(41) : error BC30451: Le nom 'Application' n'est pas déclaré.
Application.Run(New Form2)
~~~~~~~~~~~
هذه المرة، الاسم [Application] غير معروف. وهذا يعني ببساطة أننا لم نستورد مساحة الأسماء الخاصة به [System.Windows.Forms]. دعونا نضيف التعليمات التالية:
ثم نعيد التجميع:
هذه المرة، نجحت العملية. لنقم بتنفيذ:

يتم إنشاء نموذج من النوع Form1 وعرضه. يمكن تجنب إضافة الإجراء [Main] باستخدام الخيار /m للمترجم، الذي يسمح بتحديد الفئة المراد تنفيذها في حالة ما إذا كانت هذه الفئة ترث من System.Windows.Form:
تشير الخيار /m:form2 إلى أن الفئة المطلوب تنفيذها هي الفئة التي تحمل الاسم [form2].
5.2.6. إدارة الأحداث
بمجرد عرض النموذج، يبدأ التطبيق في مراقبة الأحداث التي تحدث على النموذج (النقرات، تحركات الماوس، ...) ويقوم بتنفيذ تلك التي يديرها النموذج. في هذه الحالة، لا يدير النموذج الخاص بنا أي أحداث بخلاف تلك التي تديرها الفئة الأساسية Form (النقرات على أزرار الإغلاق، والتكبير/التصغير، وتغيير حجم النافذة، وتحريك النافذة، ...). يستخدم النموذج الذي تم إنشاؤه سمة components التي لا تُستخدم في أي مكان. كما أن الطريقة dispose لا فائدة منها هنا. وينطبق الأمر نفسه على بعض مساحات الأسماء (Collections، ComponentModel، Data) المستخدمة، وكذلك تلك المُعرَّفة للمشروع projet1. لذلك، في هذا المثال، يمكن تبسيط الكود إلى ما يلي:
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1
Inherits System.Windows.Forms.Form
' منشئ
Public Sub New()
' إنشاء النموذج بمكوناته
InitializeComponent()
End Sub
Private Sub InitializeComponent()
' حجم النافذة
Me.Size = New System.Drawing.Size(300, 300)
' عنوان النافذة
Me.Text = "Form1"
End Sub
Shared Sub Main()
' يتم تشغيل التطبيق
Application.Run(New Form1)
End Sub
End Class
5.2.7. الخلاصة
سنقبل الآن الكود الذي تم إنشاؤه بواسطة VS.NET كما هو، وسنكتفي بإضافة كودنا إليه، لا سيما لإدارة الأحداث المرتبطة بعناصر التحكم المختلفة في النموذج.
5.3. نافذة تحتوي على حقل إدخال وزر وتسمية
5.3.1. التصميم الجرافيكي
في المثال السابق، لم نضع أي مكونات في النافذة. نبدأ مشروعًا جديدًا باسم interface2. وللقيام بذلك، نتبع الإجراء الموضح سابقًا لإنشاء مشروع:

لنقم الآن بإنشاء نافذة تحتوي على زر وتسمية وحقل إدخال:
![]() |
الحقول هي كما يلي:
رقم | الاسم | النوع | الدور |
1 | lblSaisie | العلامة | وصف |
2 | txtSaisie | TextBox | حقل إدخال |
3 | btnAfficher | زر | لعرض محتوى حقل الإدخال في مربع حوار txtSaisie |
يمكن اتباع الخطوات التالية لإنشاء هذه النافذة: انقر بزر الماوس الأيمن داخل النافذة خارج أي مكون واختر الخيار Properties للوصول إلى خصائص النافذة:

تظهر نافذة الخصائص عندئذٍ على اليمين:

تجدر الإشارة إلى بعض هذه الخصائص:
لتحديد لون خلفية النافذة | |
لتحديد لون الرسومات أو النص في النافذة | |
لربط قائمة بالنافذة | |
لتسمية النافذة | |
لتحديد نوع النافذة | |
لتحديد خط النص في النافذة | |
لتحديد اسم النافذة |
هنا، نقوم بتعيين الخصائص Text و Name:
حقول الإدخال والأزرار - 1 | |
frmSaisiesBoutons |
باستخدام شريط «صندوق الأدوات»
- حدد المكونات التي تحتاجها
- ضعها في النافذة واضبط أبعادها بشكل صحيح
![]() | ![]() |
بمجرد اختيار المكون من "صندوق الأدوات"، استخدم المفتاح "Echap" لإخفاء شريط الأدوات، ثم قم بسحب المكون وقم بتحديد أبعاده. كرر هذه الخطوات للمكونات الثلاثة المطلوبة: Label، TextBox، Button. لمحاذاة و تحديد أبعاد المكونات بشكل صحيح، استخدم القائمة Format: | ![]() |
مبدأ التنسيق هو كما يلي:
يتيح لك الخيار Align محاذاة المكونات | ![]() |
تتيح لك الخيار Make Same Size جعل المكونات بنفس الارتفاع أو بنفس العرض: | ![]() |
تتيح خيار «Horizontal Spacing» (المسافة الأفقية)، على سبيل المثال محاذاة المكونات أفقيًا مع بفواصل متساوية الحجم بينها. وينطبق الأمر نفسه بالنسبة لخيار «المسافة الرأسية» (Vertical Spacing) للمحاذاة الرأسية. تتيح خيار «Center in Form» (التوسيط في النموذج) توسيط مكونًا أفقيًا أو عموديًا في النافذة: | ![]() |
بمجرد وضع المكونات بشكل صحيح في النافذة، قم بتحديد خصائصها. للقيام بذلك، انقر بزر الماوس الأيمن على المكون و اختر الخيار Properties:
حدد المكون لفتح نافذة خصائصه. وفي هذه النافذة، قم بتعديل الخصائص التالية: name: lblSaisie، text: إدخال
حدد المكون لعرض نافذة الخصائص الخاصة به. في هذه النافذة، قم بتعديل الخصائص التالية: الاسم: txtSaisie، النص: لا تدخل أي شيء
name: cmdAfficher، text: عرض
text: المدخلات والأزرار - 1 | ![]() |
يمكننا تشغيل (ctrl-F5) مشروعنا للحصول على نظرة أولية على النافذة وهي تعمل: | ![]() |
أغلق النافذة. يتبقى لنا الآن كتابة الإجراء المرتبط بالنقر على الزر Afficher.
5.3.2. إدارة أحداث النموذج
لنلقِ نظرة على الكود الذي تم إنشاؤه بواسطة مصمم الواجهة الرسومية:
...
Public Class frmSaisiesBoutons
Inherits System.Windows.Forms.Form
' المكونات
Private components As System.ComponentModel.Container = Nothing
Friend WithEvents btnAfficher As System.Windows.Forms.Button
Friend WithEvents lblsaisie As System.Windows.Forms.Label
Friend WithEvents txtsaisie As System.Windows.Forms.TextBox
' المنشئ
Public Sub New()
InitializeComponent()
End Sub
...
' تهيئة المكونات
Private Sub InitializeComponent()
...
End Sub
End Class
تجدر الإشارة أولاً إلى التحديد الخاص للمكونات:
- تشير الكلمة الرئيسية Friend إلى أن رؤية المكون تمتد لتشمل جميع فئات المشروع
- تشير الكلمة الرئيسية WithEvents إلى أن المكون يولد أحداثًا. سنركز الآن على كيفية إدارة هذه الأحداث
اعرض نافذة كود النموذج (عرض/كود أو F7):
![]() |
تحتوي النافذة أعلاه على قائمتين منسدلتين (1) و(2). القائمة (1) هي قائمة مكونات النموذج:

أما القائمة (2) فهي قائمة بالأحداث المرتبطة بالمكون المحدد في (1):

يتم عرض أحد الأحداث المرتبطة بالمكون بخط عريض (هنا Click). هذا هو الحدث الافتراضي للمكون. يمكن الوصول إلى معالج هذا الحدث المحدد بالنقر المزدوج على المكون في نافذة التصميم. عندئذٍ يقوم VB.net تلقائيًا بإنشاء الهيكل الأساسي لمعالج الحدث في نافذة الكود ويضع المؤشر عليه. أما الوصول إلى معالجات الأحداث الأخرى فيتم من خلال نافذة الكود، عن طريق تحديد المكون في القائمة (1) والحدث في (2). عندئذٍ يقوم VB.net بإنشاء الهيكل الأساسي لمعالج الحدث أو يضع المؤشر عليه إذا كان قد تم إنشاؤه مسبقًا:
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
End Sub
بشكل افتراضي، يقوم VB.net C_E بتسمية معالج الحدث E للمكون C. يمكن تغيير هذا الاسم إذا كان ذلك مناسبًا. لكن لا يُنصح بذلك. عادةً ما يحتفظ مطورو VB بالاسم الذي تم إنشاؤه بواسطة VB، مما يضمن اتساق التسمية في جميع برامج VB. لا يتم ربط الإجراء btnAfficher_Click بحدث Click للمكون btnAfficher من خلال اسم الإجراء، بل من خلال الكلمة الرئيسية handles:
Handles btnAfficher.Click
في المثال أعلاه، تحدد الكلمة الرئيسية handles أن الإجراء يدير حدث «Click» للمكون btnAfficher. يحتوي معالج الحدث السابق على معلمتين:
الكائن الذي تسبب في الحدث (في هذه الحالة الزر) | |
كائن EventArgs الذي يوضح تفاصيل الحدث الذي وقع |
لن نستخدم أيًا من هذه المعلمات هنا. لم يتبق لنا سوى إكمال الكود. هنا، نريد عرض مربع حوار يحتوي على محتوى الحقل txtSaisie:
Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' عرض النص الذي تم إدخاله في مربع الإدخال TxtSaisie
MessageBox.Show("texte saisi= " + txtsaisie.Text, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
عند تشغيل التطبيق، نحصل على النتيجة التالية:

5.3.3. طريقة أخرى لإدارة الأحداث
بالنسبة للزر btnAfficher، قام VS.NET بإنشاء الكود التالي:
Private Sub InitializeComponent()
...
'
'btnAfficher
'
Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
Me.btnAfficher.Name = "btnAfficher"
Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
Me.btnAfficher.TabIndex = 2
Me.btnAfficher.Text = "Afficher"
...
End Sub
...
' معالج أحداث النقر على الزر cmdAfficher
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
End Sub
يمكننا ربط الإجراء btnAfficher_Click بحدث النقر (Click) الخاص بالزر btnAfficher بطريقة أخرى:
Private Sub InitializeComponent()
...
'
'btnAfficher
'
Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
Me.btnAfficher.Name = "btnAfficher"
Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
Me.btnAfficher.TabIndex = 2
Me.btnAfficher.Text = "Afficher"
AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
...
End Sub
...
' مدير أحداث النقر على الزر cmdAfficher
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
End Sub
فقدت الإجراء btnAfficher_Click الكلمة الرئيسية Handles، وبالتالي فقدت ارتباطها بالحدث btnAfficher.Click. ويتم هذا الارتباط الآن باستخدام الكلمة الرئيسية AddHandler:
AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
يربط الكود أعلاه، الذي سيتم وضعه في الإجراء InitializeComponent في النموذج، الإجراء الذي يحمل الاسم btnAfficher_Click بالحدث btnAfficher.Click. من ناحية أخرى، لم يعد المكون btnAfficher بحاجة إلى الكلمة الرئيسية WithEvents:
Friend btnAfficher As System.Windows.Forms.Button
ما الفرق بين الطريقتين؟
- الكلمة الرئيسية handles لا تسمح بربط حدث بإجراء إلا في مرحلة التصميم. يعرف المصمم مسبقًا أن الإجراء P يجب أن يدير الأحداث E1 و E2، ... فيكتب الكود
Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En
من الممكن بالفعل أن تدير إجراء واحد عدة أحداث.
- تسمح الكلمة الرئيسية addhandler بربط حدث بإجراء أثناء التنفيذ. وهذا مفيد في سياق «المنتج والمستهلك» للأحداث. تُنتج كائن ما حدثًا معينًا قد يهم كائنات أخرى. فتقوم هذه الكائنات بالاشتراك لدى المنتج لتلقي الحدث (مثل تجاوز درجة الحرارة لعتبة حرجة، على سبيل المثال). وأثناء تشغيل التطبيق، سيُطلب من منتج الحدث تنفيذ تعليمات مختلفة:
حيث E هو الحدث الذي ينتجه المنتج، وPi هي الإجراءات التي تنتمي إلى الكائنات المختلفة المستهلكة لهذا الحدث. سنعود إلى تطبيق «المنتج-المستهلك» للأحداث في فصل لاحق.
5.3.4. الخلاصة
من المشروعين اللذين درسناهما، يمكننا أن نستنتج أنه بمجرد إنشاء الواجهة الرسومية باستخدام VS.NET، فإن عمل المطور يتمثل في كتابة معالجات الأحداث التي يرغب في إدارتها لهذه الواجهة الرسومية. ولن نعرض من الآن فصاعدًا سوى كود هذه المعالجات.
5.4. بعض المكونات المفيدة
نقدم الآن تطبيقات متنوعة تستخدم المكونات الأكثر شيوعًا من أجل اكتشاف الطرق والخصائص الرئيسية لهذه المكونات. بالنسبة لكل تطبيق، نقدم الواجهة الرسومية والكود ذي الصلة، ولا سيما معالجات الأحداث.
5.4.1. نموذج Form
نبدأ بعرض المكون الأساسي، وهو النموذج الذي يتم وضع المكونات عليه. وقد سبق أن عرضنا بعض خصائصه الأساسية. وسنركز هنا على بعض الأحداث المهمة في النموذج.
يتم تحميل النموذج حالياً | |
النموذج قيد الإغلاق | |
النموذج مغلق |
يحدث الحدث Load قبل حتى عرض النموذج. ويحدث الحدث Closing أثناء إغلاق النموذج. ولا يزال من الممكن إيقاف هذا الإغلاق برمجيًا. نقوم بإنشاء نموذج باسم Form1 بدون مكونات:

نقوم بمعالجة الأحداث الثلاثة السابقة:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' التحميل الأولي للنموذج
MessageBox.Show("Evt Load", "Load")
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
' النموذج قيد الإغلاق
MessageBox.Show("Evt Closing", "Closing")
' يُطلب التأكيد
Dim réponse As DialogResult = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If réponse = DialogResult.No Then
e.Cancel = True
End If
End Sub
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
' النموذج قيد الإغلاق
MessageBox.Show("Evt Closed", "Closed")
End Sub
نستخدم الدالة MessageBox لتلقي إشعارات بالأحداث المختلفة الأحداث. سيحدث الحدث Closing عندما يقوم المستخدم يغلق النافذة. | ![]() |
ثم نسأله عما إذا كان يريد حقًا الخروج من التطبيق: | ![]() |
إذا أجاب بـ «لا»، فإننا نعيّن الخاصية Cancel للحدث CancelEventArgs التي تلقتها الطريقة كمعلمة. إذا قمنا بتعيين هذه الخاصية إلى False، يتم إلغاء النافذة، وإلا فسيستمر: | ![]() |
5.4.2. تسميات Label ومربعات الإدخال TextBox
لقد سبق أن تعرفنا على هذين المكونين. Label هو مكون نصي وTextBox هو مكون حقل إدخال. خاصيتهما الرئيسية هي Text التي تشير إما إلى محتوى حقل الإدخال أو نص التسمية. هذه الخاصية قابلة للقراءة والكتابة. الحدث الذي يُستخدم عادةً مع TextBox هو TextChanged الذي يشير إلى أن المستخدم قد قام بتعديل حقل الإدخال. فيما يلي مثال يستخدم الحدث TextChanged لتتبع التغييرات التي تطرأ على حقل الإدخال:
![]() |
رقم | النوع | الاسم | الدور |
1 | TextBox | txtSaisie | حقل الإدخال |
2 | التسمية | lblControle | يعرض نص 1 في الوقت الفعلي |
3 | زر | cmdEffacer | لمسح الحقلين 1 و2 |
4 | زر | cmdQuitter | للخروج من التطبيق |
الرمز ذو الصلة بهذا التطبيق هو رمز معالجات الأحداث:
' النقر على زر «خروج»
Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmdQuitter.Click
' النقر على زر «خروج» - يتم الخروج من التطبيق
Application.Exit()
End Sub
' تعديل الحقل txtSaisie
Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSaisie.TextChanged
' تغير محتوى TextBox - يتم نسخه إلى التسمية lblControle
lblControle.Text = txtSaisie.Text
End Sub
' النقر على زر «مسح»
Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmdEffacer.Click
' تم مسح محتوى مربع الإدخال
txtSaisie.Text = ""
End Sub
' تم الضغط على مفتاح
Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles txtSaisie.KeyPress
' يتم تحديد المفتاح الذي تم الضغط عليه
Dim touche As Char = e.KeyChar
If touche = ControlChars.Cr Then
MessageBox.Show(txtSaisie.Text, "Contrôle", MessageBoxButtons.OK, MessageBoxIcon.Information)
e.Handled = True
End If
End Sub
تجدر الإشارة إلى طريقة إنهاء التطبيق في الإجراء cmdQuitter_Click: Application.Exit(). يستخدم المثال التالي TextBox متعدد الأسطر:
![]() |
قائمة عناصر التحكم هي كما يلي:
رقم | النوع | الاسم | الدور |
1 | TextBox | txtMultiLignes | حقل إدخال متعدد الأسطر |
2 | TextBox | txtAjout | حقل إدخال أحادي السطر |
3 | زر | btnAjouter | يضيف المحتوى من 2 إلى 1 |
لجعل TextBox متعدد الأسطر، يتم ضبط الخصائص التالية لعنصر التحكم:
لقبول عدة أسطر من النص | |
لطلب أن يحتوي عنصر التحكم على أشرطة تمرير (أفقي، عمودي، كلاهما) أو لا (لا شيء) | |
إذا كانت القيمة «true»، فستؤدي مفتاح «Enter» إلى الانتقال إلى السطر التالي | |
إذا كانت القيمة "true"، فستؤدي مفتاح Tab إلى إنشاء فاصل في النص |
الرمز المفيد هو الذي يعالج النقر على الزر [Ajouter] والرمز الذي يعالج تعديل حقل الإدخال [txtAjout]:
' ربما btnAjouter_Click
Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnAjouter.Click
' إضافة محتوى txtAjout إلى محتوى txtMultilignes
txtMultilignes.Text &= txtAjout.Text
txtAjout.Text = ""
End Sub
' أو txtAjout_TextChanged
Private Sub txtAjout_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtAjout.TextChanged
' يتم تحديد حالة زر «إضافة»
btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
End Sub
5.4.3. القوائم المنسدلة ComboBox
![]() | ![]() |
المكون ComboBox عبارة عن قائمة منسدلة مصحوبة بحقل إدخال: يمكن للمستخدم إما اختيار عنصر من (2) أو كتابة نص في (1). هناك ثلاثة أنواع من ComboBox يتم تحديدها بواسطة الخاصية Style:
قائمة غير منسدلة مع منطقة تحرير | |
قائمة منسدلة مع منطقة تحرير | |
قائمة منسدلة بدون منطقة تحرير |
بشكل افتراضي، يكون نوع ComboBox هو DropDown. للاطلاع على الفئة ComboBox، اكتب ComboBox في فهرس المساعدة (Aide/Index). تحتوي الفئة ComboBox على منشئ واحد:
Public Sub New() | يُنشئ كائن ComboBox فارغًا |
تتوفر عناصر ComboBox في الخاصية Items:
وهي خاصية مفهرسة، حيث يشير Items(i) إلى العنصر i في قائمة الاختيار. لنفترض أن C هي قائمة اختيار وC.Items هي قائمة عناصرها. لدينا الخصائص التالية:
عدد عناصر قائمة الاختيار | |
العنصر i في قائمة الاختيار | |
يضيف الكائن o كآخر عنصر في القائمة المنسدلة | |
يضيف مصفوفة من الكائنات في نهاية القائمة المنسدلة | |
يضيف الكائن o إلى الموضع i في القائمة المنسدلة | |
يحذف العنصر i من القائمة المنسدلة | |
يزيل الكائن o من القائمة المنسدلة | |
حذف جميع العناصر من القائمة المنسدلة | |
يعرض الموضع i للعنصر o في قائمة الاختيار |
قد يثير الدهشة أن قائمة منسدلة يمكن أن تحتوي على كائنات في حين أنها عادةً ما تحتوي على سلاسل أحرف. من الناحية المرئية، سيكون هذا هو الحال. إذا كانت قائمة منسدلة ComboBox تحتوي على كائن obj، فإنها تعرض السلسلة obj.ToString(). نتذكر أن كل كائن لديه طريقة ToString موروثة من الفئة object والتي تُرجع سلسلة أحرف «تمثل» الكائن. العنصر المحدد في القائمة المنسدلة C هو C.SelectedItem أو C.Items(C.SelectedIndex) حيث C.SelectedIndex هو رقم العنصر المحدد، ويبدأ هذا الرقم من الصفر بالنسبة للعنصر الأول.
عند اختيار عنصر من القائمة المنسدلة، يحدث الحدث SelectedIndexChanged الذي يمكن استخدامه للتنبيه بتغيير الاختيار في القائمة المنسدلة. في التطبيق التالي، نستخدم هذا الحدث لعرض العنصر الذي تم تحديده في القائمة.

نقدم فقط الكود ذي الصلة بالنافذة. في مُنشئ النموذج، نملأ القائمة المنسدلة:
Public Sub New()
' إنشاء النموذج
InitializeComponent()
' ملء القائمة المنسدلة
cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
' نختار العنصر الأول من القائمة
cmbNombres.SelectedIndex = 0
End Sub
نقوم بمعالجة الحدث SelectedIndexChanged الخاص بقائمة الاختيار، والذي يشير إلى عنصر جديد تم تحديده:
Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cmbNombres.SelectedIndexChanged
' تغير العنصر المحدد - نعرضه
MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
5.4.4. المكون ListBox
نقترح إنشاء الواجهة التالية:
![]() |
مكونات هذه النافذة هي كما يلي:
رقم | النوع | الاسم | الدور/الخصائص |
0 | نموذج | Form1 | نموذج - BorderStyle=FixedSingle |
1 | TextBox | txtSaisie | حقل الإدخال |
2 | زر | btnAjouter | زر يسمح بإضافة محتوى حقل الإدخال 1 إلى القائمة 3 |
3 | ListBox | listBox1 | القائمة 1 |
4 | ListBox | listBox2 | القائمة 2 |
5 | زر | btn1TO2 | ينقل العناصر المحددة من القائمة 1 إلى القائمة 2 |
6 | زر | cmd2T0 | يقوم بالعكس |
7 | زر | btnEffacer1 | إفراغ القائمة 1 |
8 | زر | btnEffacer2 | إفراغ القائمة 2 |
- يقوم المستخدم بكتابة نص في الحقل 1. ثم يضيفه إلى القائمة 1 باستخدام الزر Ajouter (2). بعد ذلك، يتم مسح الحقل (1) ويمكن للمستخدم إضافة عنصر جديد.
- يمكنه نقل العناصر من قائمة إلى أخرى عن طريق تحديد العنصر المراد نقله في إحدى القوائم واختيار زر النقل المناسب 5 أو 6. يتم إضافة العنصر المنقول إلى نهاية قائمة الوجهة وإزالته من قائمة المصدر.
- يمكنه النقر مرتين على عنصر في القائمة 1. عندئذٍ يُنقل هذا العنصر إلى مربع الإدخال لتعديله ويُحذف من القائمة 1.
تُضاء الأزرار أو تُطفأ وفقًا للقواعد التالية:
- لا يُضاء الزر Ajouter إلا إذا كان حقل الإدخال يحتوي على نص غير فارغ
- لا يضيء الزر 5 الخاص بالنقل من القائمة 1 إلى القائمة 2 إلا إذا كان هناك عنصر محدد في القائمة 1
- لا يضيء الزر 6 الخاص بالنقل من القائمة 2 إلى القائمة 1 إلا إذا كان هناك عنصر محدد في القائمة 2
- لا تضيء الأزرار 7 و8 الخاصة بمسح القائمتين 1 و2 إلا إذا كانت القائمة المراد مسحها تحتوي على عناصر.
في ظل الشروط السابقة، يجب أن تكون جميع الأزرار مطفأة عند بدء تشغيل التطبيق. ولذلك، يجب ضبط الخاصية Enabled للأزرار على القيمة false. يمكن القيام بذلك أثناء التصميم، مما سيؤدي إلى إنشاء الكود المقابل في الأسلوب InitializeComponent، أو القيام بذلك بأنفسنا في المنشئ كما يلي:
Public Sub New()
' الإنشاء الأولي للنموذج
InitializeComponent()
' عمليات تهيئة إضافية
' يتم تعطيل عدد معين من الأزرار
btnAjouter.Enabled = False
btn1TO2.Enabled = False
btn2TO1.Enabled = False
btnEffacer1.Enabled = False
btnEffacer2.Enabled = False
End Sub
يتم التحكم في حالة الزر Ajouter من خلال محتوى حقل الإدخال. ويتيح لنا الحدث TextChanged تتبع التغييرات التي تطرأ على هذا المحتوى:
' تغيير في حقل الإدخال النصي
Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSaisie.TextChanged
' تغير محتوى txtSaisie
' لا يتم تمييز زر «إضافة» إلا إذا كان الحقل غير فارغ
btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
End Sub
يعتمد حالة أزرار النقل على ما إذا كان قد تم تحديد عنصر ما أم لا في القائمة التي تتحكم فيها:
' تغيير العنصر المحدد بدون listbox1
Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles listBox1.SelectedIndexChanged
' تم تحديد عنصر
' يتم تفعيل زر النقل من 1 إلى 2
btn1TO2.Enabled = True
End Sub
' تغيير العنصر المحدد بدون listbox2
Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles listBox2.SelectedIndexChanged
' تم تحديد عنصر
' يتم تنشيط زر النقل من 2 إلى 1
btn2TO1.Enabled = True
End Sub
الرمز المرتبط بالنقر على الزر Ajouter هو التالي:
' النقر على زر «إضافة»
Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnAjouter.Click
' إضافة عنصر جديد إلى القائمة 1
listBox1.Items.Add(txtSaisie.Text.Trim())
' مسح ما تم إدخاله
txtSaisie.Text = ""
' القائمة 1 ليست فارغة
btnEffacer1.Enabled = True
' إعادة التركيز إلى مربع الإدخال
txtSaisie.Focus()
End Sub
تجدر الإشارة إلى الطريقة Focus التي تسمح بوضع «التركيز» على عنصر تحكم في النموذج. الكود المرتبط بالنقر على الأزرار Effacer:
' النقر على زر «مسح 1»
Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer1.Click
' تم مسح القائمة 1
listBox1.Items.Clear()
btnEffacer1.Enabled = False
End Sub
' النقر على زر «مسح 2»
Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' يتم مسح القائمة 2
listBox2.Items.Clear()
btnEffacer2.Enabled = False
End Sub
الكود الخاص بنقل العناصر المحددة من قائمة إلى أخرى:
' النقر على الزر btn1to2
Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btn1TO2.Click
' نقل العنصر المحدد من القائمة 1 إلى القائمة 2
transfert(listBox1, listBox2)
' أزرار «حذف»
btnEffacer2.Enabled = True
btnEffacer1.Enabled = listBox1.Items.Count <> 0
' أزرار النقل
btn1TO2.Enabled = False
btn2TO1.Enabled = False
End Sub
' النقر على الزر btn2to1
Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btn2TO1.Click
' نقل العنصر المحدد من القائمة 2 إلى القائمة 1
transfert(listBox2, listBox1)
' أزرار «مسح»
btnEffacer1.Enabled = True
btnEffacer2.Enabled = listBox2.Items.Count <> 0
' أزرار النقل
btn1TO2.Enabled = False
btn2TO1.Enabled = False
End Sub
' النقل
Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
' نقل العنصر المحدد من القائمة 1 إلى القائمة l2
' عنصر محدد؟
If l1.SelectedIndex = -1 Then Return
' إضافة إلى l2
l2.Items.Add(l1.SelectedItem)
' حذف في l1
l1.Items.RemoveAt(l1.SelectedIndex)
End Sub
أولاً، نقوم بإنشاء دالة
Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
التي تنقل العنصر المحدد في القائمة l1 إلى القائمة l2. وهذا يتيح لنا استخدام طريقة واحدة بدلاً من طريقتين لنقل عنصر من القائمة listBox1 إلى القائمة listBox2 أو من القائمة listBox2 إلى القائمة listBox1. قبل إجراء النقل، نتأكد من وجود عنصر محدد بالفعل في القائمة l1:
' عنصر محدد؟
If l1.SelectedIndex = -1 Then Return
تكون قيمة الخاصية SelectedIndex هي -1 إذا لم يكن هناك عنصر محدد حاليًا. في الإجراءات
Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnXTOY.Click
يتم نقل القائمة X إلى القائمة Y وتغيير حالة بعض الأزرار لتعكس الحالة الجديدة للقوائم.
5.4.5. مربعات الاختيار CheckBox، وأزرار الاختيار ButtonRadio
نقترح كتابة التطبيق التالي:
![]() |
مكونات النافذة هي كما يلي:
رقم | النوع | الاسم | الدور |
1 | RadioButton | radioButton1 radioButton2 radioButton3 | 3 أزرار اختيار |
2 | CheckBox | chechBox1 chechBox2 chechBox3 | 3 خانات اختيار |
3 | ListBox | lstValeurs | قائمة |
إذا تم إنشاء أزرار الاختيار الثلاثة واحدًا تلو الآخر، فإنها تصبح جزءًا من نفس المجموعة بشكل افتراضي. لذلك، عندما يتم تحديد أحدها، لا يتم تحديد الباقي. الحدث الذي يهمنا بالنسبة لهذه العناصر الستة هو الحدث CheckChanged الذي يشير إلى أن حالة مربع الاختيار أو زر الاختيار قد تغيرت. يتم تمثيل هذه الحالة في كلتا الحالتين بالخاصية المنطقية Check التي تعني، عند قيمتها «صحيح»، أن عنصر التحكم محدد. وقد استخدمنا هنا طريقة واحدة لمعالجة الأحداث الستة CheckChanged، وهي الطريقة affiche:
' يعرض
Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles checkBox1.CheckedChanged, checkBox2.CheckedChanged, checkBox3.CheckedChanged, _
radioButton1.CheckedChanged, radioButton2.CheckedChanged, radioButton3.CheckedChanged
' يعرض حالة زر الاختيار أو مربع الاختيار
' هل هذا مربع اختيار؟
If (TypeOf (sender) Is CheckBox) Then
Dim chk As CheckBox = CType(sender, CheckBox)
lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
End If
' هل هو زر اختيار؟
If (TypeOf (sender) Is RadioButton) Then
Dim rdb As RadioButton = CType(sender, RadioButton)
lstValeurs.Items.Insert(0, rdb.Name & "=" & rdb.Checked)
End If
End Sub
تسمح صيغة TypeOf (المرسل) Is CheckBox بالتحقق مما إذا كان الكائن sender من النوع CheckBox. وهذا يتيح لنا بعد ذلك إجراء تحويل إلى النوع الدقيق لـ sender. تقوم الطريقة affiche بتسجيل اسم المكون الذي تسبب في الحدث وقيمة خاصيته Checked في القائمة lstValeurs. عند التنفيذ، نلاحظ أن النقر على زر الاختيار يؤدي إلى حدوث حدثين CheckChanged: أحدهما على زر الاختيار القديم الذي يتحول إلى «غير محدد»، والآخر على الزر الجديد الذي يتحول إلى «محدد».
5.4.6. مغيرات ScrollBar
هناك عدة أنواع من المتغيرات: المتغير الأفقي (hScrollBar)، المغير الرأسي (vScrollBar)، والمزيد (NumericUpDown). | ![]() |
لنقم بتنفيذ التطبيق التالي:
![]() |
رقم | النوع | الاسم | الدور |
1 | hScrollBar | hScrollBar1 | محول أفقي |
2 | hScrollBar | hScrollBar2 | محول أفقي يتبع التغيرات في المحول 1 |
3 | TextBox | txtValeur | يعرض قيمة المنظم الأفقي ReadOnly=true لمنع أي إدخال |
4 | NumericUpDown | المُزيد | يسمح بتحديد قيمة المتغير 2 |
- يتيح المنظم ScrollBar للمستخدم اختيار قيمة ضمن نطاق من القيم الصحيحة التي يرمز لها "شريط" المنظم الذي يتحرك عليه المؤشر. تتوفر قيمة المنظم في خاصيته Value.
- بالنسبة لمزلاج أفقي، يمثل الطرف الأيسر القيمة الدنيا للنطاق، والطرف الأيمن القيمة القصوى، بينما يمثل المؤشر القيمة الحالية المختارة. أما بالنسبة لمزلاج عمودي، فيُمثَّل الحد الأدنى بالطرف العلوي، والحد الأقصى بالطرف السفلي. وتُمثَّل هذه القيم بخصائص Minimum و Maximum، وتبلغ قيمتها الافتراضية 0 و 100.
- يؤدي النقر على طرفي شريط التمرير إلى تغيير القيمة بمقدار زيادة (إيجابية أو سلبية) وفقًا للطرف الذي تم النقر عليه، والذي يُسمى SmallChange ويكون قيمته الافتراضية 1.
- يؤدي النقر على جانبي المؤشر إلى تغيير القيمة بمقدار زيادة (إيجابية أو سلبية) وفقًا للطرف الذي تم النقر عليه، والمسمى LargeChange، والذي تبلغ قيمته الافتراضية 10.
- عند النقر على الطرف العلوي لمزلاج عمودي، تنخفض قيمته. وقد يفاجئ هذا المستخدم العادي الذي يتوقع عادةً أن «ترتفع» القيمة. يتم حل هذه المشكلة عن طريق تعيين قيمة سالبة للخصائص SmallChange و LargeChange
- هذه الخصائص الخمس (Value، Minimum، Maximum، SmallChange، LargeChange) قابلة للقراءة والكتابة.
- الحدث الرئيسي للمحرك هو الذي يشير إلى تغيير في القيمة: وهو حدث Scroll.
يوجد مكون NumericUpDown قريبًا من المحرك: وهو أيضًا يحتوي على الخصائص Minimum و Maximum و Value، بقيم افتراضية هي 0 و 100 و 0. ولكن هنا، تُعرض الخاصية Value في مربع إدخال يُشكل جزءًا لا يتجزأ من عنصر التحكم. يمكن للمستخدم تعديل هذه القيمة بنفسه ما لم يتم تعيين الخاصية ReadOnly لعنصر التحكم على «صحيح». يتم تحديد قيمة الزيادة بواسطة الخاصية Increment، والقيمة الافتراضية هي 1. الحدث الرئيسي للمكون NumericUpDown هو الذي يشير إلى تغيير في القيمة: الحدث ValueChanged. فيما يلي الكود المستخدم في تطبيقنا:
يتم تنسيق النموذج عند إنشائه:
' منشئ
Public Sub New()
' الإنشاء الأولي للنموذج
InitializeComponent()
' يتم إعطاء المحرك 2 نفس خصائص المحرك 1
hScrollBar2.Minimum = hScrollBar1.Value
hScrollBar2.Minimum = hScrollBar1.Minimum
hScrollBar2.Maximum = hScrollBar1.Maximum
hScrollBar2.LargeChange = hScrollBar1.LargeChange
hScrollBar2.SmallChange = hScrollBar1.SmallChange
' وكذلك بالنسبة للمُزيد
incrémenteur.Minimum = hScrollBar1.Value
incrémenteur.Minimum = hScrollBar1.Minimum
incrémenteur.Maximum = hScrollBar1.Maximum
incrémenteur.Increment = hScrollBar1.SmallChange
' يتم تعيين قيمة المحرك 1 على TextBox
txtValeur.Text = "" & hScrollBar1.Value
End Sub
المدير الذي يتتبع تغيرات قيمة المحرك 1:
' إدارة محرك hscrollbar1
Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
Handles hScrollBar1.Scroll
' تغيير قيمة المتغير 1
' يتم تمرير قيمته إلى المتغير 2 وإلى مربع النص TxtValeur
hScrollBar2.Value = hScrollBar1.Value
txtValeur.Text = "" & hScrollBar1.Value
End Sub
المدير الذي يتتبع تغيرات قيمة المحول 2:
' إدارة شريط التمرير الأفقي hscrollbar2
Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
Handles hScrollBar2.Scroll
' يتم منع أي تغيير في المتغير 2
' بإجباره على الاحتفاظ بقيمة المتغير 1
e.NewValue = hScrollBar1.Value
End Sub
المدير الذي يتتبع التغيرات في التحكم incrémenteur:
' إدارة عداد التزايد
Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles incrémenteur.ValueChanged
' تثبيت قيمة المتغير 2
hScrollBar2.Value = CType(incrémenteur.Value, Integer)
End Sub
5.5. أحداث الماوس
عند الرسم داخل حاوية، من المهم معرفة موضع الماوس لعرض نقطة عند النقر، على سبيل المثال. تؤدي تحركات الماوس إلى حدوث أحداث داخل الحاوية التي يتحرك فيها.
![]() | ![]() |
دخلت الماوس للتو إلى نطاق عنصر التحكم | |
لقد غادرت الماوس للتو نطاق عنصر التحكم | |
الماوس يتحرك داخل منطقة التحكم | |
الضغط على الزر الأيسر للماوس | |
تحرير الزر الأيسر للماوس | |
يقوم المستخدم بإفلات عنصر على عنصر التحكم | |
يقوم المستخدم بالدخول إلى منطقة عنصر التحكم عن طريق سحب عنصر | |
يخرج المستخدم من منطقة عنصر التحكم عن طريق سحب كائن | |
يمر المستخدم فوق منطقة عنصر التحكم عن طريق سحب كائن |
فيما يلي برنامج يتيح فهمًا أفضل للأوقات التي تحدث فيها أحداث الماوس المختلفة:
![]() |
رقم | النوع | الاسم | الدور |
1 | العلامة | lblPosition | لعرض موضع الماوس في النموذج 1 أو القائمة 2 أو الزر 3 |
2 | ListBox | lstEvts | لعرض أحداث الماوس بخلاف MouseMove |
3 | زر | btnEffacer | لمسح محتوى 2 |
فيما يلي معالجات الأحداث. لتتبع تحركات الماوس على عناصر التحكم الثلاثة، نكتب معالجًا واحدًا فقط:
' في حالة حدوث form1_mousemove
Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseMove, lstEvts.MouseMove, btnEffacer.MouseMove, lstEvts.MouseMove
' حركة الماوس - عرض إحداثياته (X، Y)
lblPosition.Text = "(" & e.X & "," & e.Y & ")"
End Sub
يجب معرفة أنه في كل مرة تدخل فيها الماوس إلى نطاق عنصر تحكم ما، يتغير نظام إحداثياته. ونقطة أصله (0,0) هي الزاوية العلوية اليسرى لعنصر التحكم الذي توجد عليه الماوس. وهكذا عند التنفيذ، عندما نمرر الماوس من النموذج إلى الزر، نرى بوضوح تغير الإحداثيات. ولرؤية هذه التغييرات في نطاق الماوس بشكل أفضل، يمكن استخدام الخاصية Cursor الخاصة بعناصر التحكم:

تسمح هذه الخاصية بتحديد شكل مؤشر الماوس عند دخوله إلى نطاق عنصر التحكم. وهكذا، في مثالنا هذا، قمنا بتعيين المؤشر على «Default» للنموذج نفسه، و«Hand» للقائمة 2، و«No» للزر 3، كما توضح لقطات الشاشة أدناه.



في الأسلوب [InitializeComponent]، يكون الكود الناتج عن هذه الاختيارات كما يلي:
Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No
من ناحية أخرى، لاكتشاف عمليات الدخول والخروج للماوس على القائمة 2، نقوم بمعالجة الأحداث MouseEnter و MouseLeave من هذه القائمة نفسها:
' حدث lstEvts_MouseEnter
Private Sub lstEvts_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lstEvts.MouseEnter
affiche("MouseEnter sur liste")
End Sub
' حدث lstEvts_MouseLeave
Private Sub lstEvts_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lstEvts.MouseLeave
affiche("MouseLeave sur liste")
End Sub
' يعرض
Private Sub affiche(ByVal message As String)
' يتم عرض الرسالة في أعلى قائمة الأحداث
lstEvts.Items.Insert(0, message)
End Sub

ولمعالجة النقرات على النموذج، نقوم بمعالجة الأحداث MouseDown و MouseUp:
' حدث Form1_MouseDown
Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
affiche("MouseDown sur formulaire")
End Sub
' حدث Form1_MouseUp
Private Sub Form1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseUp
affiche("MouseUp sur formulaire")
End Sub

وأخيرًا، رمز معالج النقر على الزر Effacer:
' حدث btnEffacer_Click
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' يمسح قائمة الأحداث
lstEvts.Items.Clear()
End Sub
5.6. إنشاء نافذة مع قائمة
لنرى الآن كيفية إنشاء نافذة مع قائمة. سنقوم بإنشاء النافذة التالية:
![]() |
عنصر التحكم 1 هو TextBox للقراءة فقط (ReadOnly=true) واسمه txtStatut. هيكل شجرة القائمة هو كما يلي:
![]() | ![]() |
خيارات القائمة هي عناصر تحكم مثل المكونات المرئية الأخرى، ولها خصائص وأحداث. على سبيل المثال، جدول خصائص خيار القائمة A1:

يتم استخدام خاصيتين في مثالنا:
اسم عنصر التحكم الخاص بالقائمة | |
تسمية خيار القائمة |
فيما يلي خصائص خيارات القائمة المختلفة في مثالنا:
Name | النص |
mnuA | خيارات A |
mnuA1 | A1 |
mnuA2 | A2 |
mnuA3 | A3 |
mnuB | خيارات ب |
mnuB1 | B1 |
mnuSep1 | - (فاصل) |
mnuB2 | B2 |
mnuB3 | B3 |
mnuB31 | B31 |
mnuB32 | B32 |
لإنشاء قائمة، نختار المكون "MainMenu" من الشريط "ToolBox":

عندها نحصل على قائمة فارغة تظهر في النموذج مع خانات فارغة بعنوان "Type Here". ما عليك سوى إدخال الخيارات المختلفة للقائمة فيها:

لإدراج فاصل بين خيارين كما هو موضح أعلاه بين الخيارين B1 و B2، ضع المؤشر في مكان الفاصل في القائمة، وانقر بزر الماوس الأيمن واختر الخيار Insert Separator:

إذا تم تشغيل التطبيق باستخدام Ctrl+F5، فسيظهر نموذج يحتوي على قائمة لا تؤدي أي وظيفة في الوقت الحالي. يتم التعامل مع خيارات القائمة كمكونات: فهي تمتلك خصائص وأحداثًا. في [fenêtre de code]، حدد المكون mnuA1 ثم حدد الأحداث المرتبطة به:

إذا تم إنشاء الحدث Click أعلاه، فإن VS.NET يقوم تلقائيًا بإنشاء الإجراء التالي:
Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
End Sub
يمكننا اتباع نفس الإجراء مع جميع خيارات القائمة. هنا، يمكن استخدام نفس الإجراء لجميع الخيارات. ولذلك، نعيد تسمية الإجراء السابق إلى affiche ونعلن الأحداث التي يديرها:
Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuA1.Click, mnuA2.Click, mnuB1.Click, mnuB2.Click, mnuB31.Click, mnuB32.Click
' يعرض في TextBox اسم القائمة الفرعية المختارة
txtStatut.Text = (CType(sender, MenuItem)).Text
End Sub
في هذه الطريقة، نكتفي بعرض الخاصية Text لخيار القائمة في مصدر الحدث. مصدر الحدث sender هو من النوع object. أما خيارات القائمة فهي من النوع MenuItem، لذا نضطر هنا إلى إجراء تحويل من النوع object إلى MenuItem. قم بتشغيل التطبيق واختر الخيار A1 للحصول على الرسالة التالية:

الرمز المفيد في هذا التطبيق، إلى جانب رمز الطريقة affiche، هو رمز إنشاء القائمة في مُنشئ النموذج (InitializeComponent):
Private Sub InitializeComponent()
Me.mainMenu1 = New System.Windows.Forms.MainMenu
Me.mnuA = New System.Windows.Forms.MenuItem
Me.mnuA1 = New System.Windows.Forms.MenuItem
Me.mnuA2 = New System.Windows.Forms.MenuItem
Me.mnuA3 = New System.Windows.Forms.MenuItem
Me.mnuB = New System.Windows.Forms.MenuItem
Me.mnuB1 = New System.Windows.Forms.MenuItem
Me.mnuB2 = New System.Windows.Forms.MenuItem
Me.mnuB3 = New System.Windows.Forms.MenuItem
Me.mnuB31 = New System.Windows.Forms.MenuItem
Me.mnuB32 = New System.Windows.Forms.MenuItem
Me.txtStatut = New System.Windows.Forms.TextBox
Me.mnuSep1 = New System.Windows.Forms.MenuItem
Me.SuspendLayout()
'
' mainMenu1
'
Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA, Me.mnuB})
'
' mnuA
'
Me.mnuA.Index = 0
Me.mnuA.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA1, Me.mnuA2, Me.mnuA3})
Me.mnuA.Text = "Options A"
'
' mnuA1
'
Me.mnuA1.Index = 0
Me.mnuA1.Text = "A1"
'
' mnuA2
'
Me.mnuA2.Index = 1
Me.mnuA2.Text = "A2"
'
' mnuA3
'
Me.mnuA3.Index = 2
Me.mnuA3.Text = "A3"
'
' mnuB
'
Me.mnuB.Index = 1
Me.mnuB.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB1, Me.mnuSep1, Me.mnuB2, Me.mnuB3})
Me.mnuB.Text = "Options B"
'
' mnuB1
'
Me.mnuB1.Index = 0
Me.mnuB1.Text = "B1"
'
' mnuB2
'
Me.mnuB2.Index = 2
Me.mnuB2.Text = "B2"
'
' mnuB3
'
Me.mnuB3.Index = 3
Me.mnuB3.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB31, Me.mnuB32})
Me.mnuB3.Text = "B3"
'
' mnuB31
'
Me.mnuB31.Index = 0
Me.mnuB31.Text = "B31"
'
' mnuB32
'
Me.mnuB32.Index = 1
Me.mnuB32.Text = "B32"
'
' txtStatut
'
Me.txtStatut.Location = New System.Drawing.Point(8, 8)
Me.txtStatut.Name = "txtStatut"
Me.txtStatut.ReadOnly = True
Me.txtStatut.Size = New System.Drawing.Size(112, 20)
Me.txtStatut.TabIndex = 0
Me.txtStatut.Text = ""
'
' mnuSep1
'
Me.mnuSep1.Index = 1
Me.mnuSep1.Text = "-"
'
' Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(136, 42)
Me.Controls.Add(txtStatut)
Me.Menu = Me.mainMenu1
Me.Name = "Form1"
Me.Text = "Menus"
Me.ResumeLayout(False)
End Sub
تجدر الإشارة إلى التعليمات التي تربط القائمة بالنموذج:
Me.Menu = Me.mainMenu1
5.7. المكونات غير المرئية
ننتقل الآن إلى عدد من المكونات غير المرئية: تُستخدم هذه المكونات أثناء التصميم ولكنها لا تظهر أثناء التنفيذ.
5.7.1. مربعات الحوار OpenFileDialog و SaveFileDialog
سنقوم بإنشاء التطبيق التالي:
![]() |
العناصر التالية:
رقم | النوع | الاسم | الدور |
1 | TextBox متعدد الأسطر | txtTexte | نص مكتوب بواسطة المستخدم أو تم تحميله من ملف |
2 | زر | btnSauvegarder | يسمح بحفظ النص الموجود في الحقل 1 في ملف نصي |
3 | زر | btnCharger | يسمح بتحميل محتوى ملف نصي إلى 1 |
4 | زر | btnEffacer | يمسح محتوى 1 |
يتم استخدام عنصرين غير مرئيين:

عند سحبهما من "ToolBox" ووضعهما في النموذج، يتم وضعهما في منطقة منفصلة أسفل النموذج. يتم سحب مكونات "Dialog" من "ToolBox":

رمز الزر Effacer بسيط:
Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' يتم مسح مربع الإدخال
txtTexte.Text = ""
End Sub
يتم تعريف الفئة SaveFileDialog على النحو التالي:

وهي مشتقة من عدة مستويات من الفئات. ومن بين هذه الخصائص والطرق العديدة، سنستعرض ما يلي:
أنواع الملفات المتوفرة في القائمة المنسدلة لأنواع الملفات في مربع الحوار | |
رقم نوع الملف المقترح افتراضيًا في القائمة أعلاه. يبدأ من 0. | |
المجلد المحدد مسبقًا لحفظ الملف | |
اسم ملف النسخ الاحتياطي الذي حدده المستخدم | |
الطريقة التي تعرض مربع حوار النسخ الاحتياطي. تُرجع نتيجة من النوع DialogResult. |
تعرض الطريقة ShowDialog مربع حوار مشابهًا لما يلي:
![]() |
قائمة منسدلة تم إنشاؤها استنادًا إلى الخاصية Filter. يتم تحديد نوع الملف المقترح افتراضيًّا بواسطة FilterIndex | |
المجلد الحالي، ويتم تحديده بواسطة InitialDirectory إذا تم تعبئة هذه الخاصية | |
اسم الملف الذي تم اختياره أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName | |
أزرار «حفظ»/«إلغاء». إذا تم استخدام الزر Enregistrer، فإن الدالة ShowDialog تُرجع النتيجة DialogResult.OK |
يمكن كتابة إجراء الحفظ على النحو التالي:
Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnSauvegarder.Click
' يتم حفظ مربع الإدخال في ملف نصي
' يتم تهيئة مربع الحوار savefileDialog1
saveFileDialog1.InitialDirectory = Application.ExecutablePath
saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
saveFileDialog1.FilterIndex = 0
' عرض مربع الحوار واسترداد نتيجته
If saveFileDialog1.ShowDialog() = DialogResult.OK Then
' يتم استرداد اسم الملف
Dim nomFichier As String = saveFileDialog1.FileName
Dim fichier As StreamWriter = Nothing
Try
' يتم فتح الملف في وضع الكتابة
fichier = New StreamWriter(nomFichier)
' نكتب النص فيه
fichier.Write(txtTexte.Text)
Catch ex As Exception
' مشكلة
MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
Finally
' يتم إغلاق الملف
Try
fichier.Close()
Catch
End Try
End Try
End If
End Sub
- يتم تعيين المجلد الأولي على المجلد الذي يحتوي على الملف التنفيذي للتطبيق:
- نحدد أنواع الملفات المطلوب عرضها
يُلاحظ أن صيغة المرشحات هي filtre1|filtre2|..|filtren حيث filtrei = نص|نموذج ملف. هنا سيكون للمستخدم الخيار بين الملفات *.txt و *.*.
- نحدد نوع الملف المراد عرضه في البداية
هنا، سيتم عرض الملفات من النوع *.txt أولاً للمستخدم.
- يتم عرض مربع الحوار واسترداد نتيجته
If saveFileDialog1.ShowDialog() = DialogResult.OK Then
- أثناء عرض مربع الحوار، لا يستطيع المستخدم الوصول إلى النموذج الرئيسي (ما يُعرف بمربع الحوار «الوضع الموضعي»). يحدد المستخدم اسم الملف المراد حفظه ويغادر مربع الحوار إما عن طريق الزر «حفظ»، أو الزر «إلغاء»، أو بإغلاق مربع الحوار. تكون نتيجة الأسلوب ShowDialog هي DialogResult.OK فقط إذا استخدم المستخدم الزر Enregistrer لمغادرة مربع الحوار.
- وبذلك، يصبح اسم الملف المراد إنشاؤه موجودًا الآن في الخاصية FileName للكائن saveFileDialog1. وبذلك نعود إلى الطريقة التقليدية لإنشاء ملف نصي. نكتب فيه محتوى TextBox: txtTexte.Text مع معالجة الاستثناءات التي قد تحدث.
تشبه الفئة OpenFileDialog إلى حد كبير الفئة SaveFileDialog، وهي مشتقة من نفس سلسلة الفئات. ومن بين هذه الخصائص والأساليب، سنستعرض ما يلي:
أنواع الملفات المعروضة في القائمة المنسدلة لأنواع الملفات في مربع الحوار | |
رقم نوع الملف المقترح افتراضيًا في القائمة أعلاه. يبدأ من 0. | |
المجلد المعروض مبدئيًا للبحث عن الملف المراد فتحه | |
اسم الملف المراد فتحه الذي حدده المستخدم | |
الطريقة التي تعرض مربع حوار الحفظ. تُرجع نتيجة من النوع DialogResult. |
تعرض الطريقة ShowDialog مربع حوار مشابهًا لما يلي:
![]() |
قائمة منسدلة مبنية على أساس الخاصية Filter. يتم تحديد نوع الملف المقترح افتراضيًا بواسطة FilterIndex | |
المجلد الحالي، الذي يتم تحديده بواسطة InitialDirectory إذا تم ملء هذه الخاصية | |
اسم الملف الذي تم اختياره أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName | |
أزرار «فتح»/«إلغاء». إذا تم استخدام الزر Ouvrir، فإن الدالة ShowDialog تُرجع النتيجة DialogResult.OK |
يمكن كتابة إجراء الفتح على النحو التالي:
Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCharger.Click
' نقوم بتحميل ملف نصي في مربع الإدخال
' يتم تهيئة مربع الحوار openfileDialog1
openFileDialog1.InitialDirectory = Application.ExecutablePath
openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
openFileDialog1.FilterIndex = 0
' يتم عرض مربع الحوار واسترداد نتيجته
If openFileDialog1.ShowDialog() = DialogResult.OK Then
' يتم استرداد اسم الملف
Dim nomFichier As String = openFileDialog1.FileName
Dim fichier As StreamReader = Nothing
Try
' يتم فتح الملف للقراءة
fichier = New StreamReader(nomFichier)
' يتم قراءة الملف بالكامل ووضعه في TextBox
txtTexte.Text = fichier.ReadToEnd()
Catch ex As Exception
' مشكلة
MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
Finally
' يتم إغلاق الملف
Try
fichier.Close()
Catch
End Try
End Try
End If
End Sub
- نحدد المجلد الأولي بالمجلد الذي يحتوي على الملف التنفيذي للتطبيق:
- نحدد أنواع الملفات المطلوب عرضها
- يتم تحديد نوع الملف الذي سيتم عرضه في البداية
في هذه الحالة، سيتم عرض الملفات من النوع *.txt أولاً للمستخدم.
- يتم عرض مربع الحوار واسترداد نتيجته
If openFileDialog1.ShowDialog() = DialogResult.OK Then
أثناء عرض مربع الحوار، لا يمكن للمستخدم الوصول إلى النموذج الرئيسي (ما يُعرف بمربع الحوار "الوضع"). يحدد المستخدم اسم الملف المراد فتحه ويغادر مربع الحوار إما عن طريق الزر "فتح" أو الزر "إلغاء" أو بإغلاق مربع الحوار. تكون نتيجة الدالة ShowDialog هي DialogResult.OK فقط إذا استخدم المستخدم الزر Ouvrir لمغادرة مربع الحوار.
- وبذلك، يصبح اسم الملف المراد إنشاؤه موجودًا الآن في الخاصية FileName للكائن openFileDialog1. ونعود عندئذٍ إلى القراءة التقليدية لملف نصي. تجدر الإشارة إلى الطريقة التي تسمح بقراءة الملف بالكامل:
- يتم وضع محتوى الملف في TextBox و txtTexte. يتم التعامل مع الاستثناءات التي قد تحدث.
5.7.2. مربعات الحوار FontColor و ColorDialog
نواصل المثال السابق بعرض زرين جديدين:
![]() |
رقم | النوع | الاسم | الدور |
6 | زر | btnCouleur | لتحديد لون أحرف TextBox |
7 | زر | btnPolice | لتحديد خط الحروف في TextBox |
نقوم بإضافة عنصر تحكم ColorDialog وعنصر تحكم FontDialog إلى النموذج:

تحتوي الفئتان FontDialog و ColorDialog على طريقة ShowDialog مماثلة للطريقة ShowDialog الموجودة في الفئتين OpenFileDialog و SaveFileDialog. تتيح الطريقة ShowDialog الخاصة بالفئة ColorDialog اختيار لون:

إذا غادر المستخدم مربع الحوار باستخدام الزر OK، فإن نتيجة الطريقة ShowDialog تكون DialogResult.OK ويكون اللون المختار موجودًا في الخاصية Color للكائن ColorDialog المستخدم. تسمح الطريقة ShowDialog للفئة FontDialog باختيار خط:

إذا غادر المستخدم مربع الحوار باستخدام الزر OK، فإن نتيجة الطريقة ShowDialog هي DialogResult.OK ويكون الخط المختار موجودًا في الخاصية Font للكائن FontDialog المستخدم. لدينا العناصر اللازمة لمعالجة النقرات على الأزرار Couleur و Police:
Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCouleur.Click
' اختيار لون النص
If colorDialog1.ShowDialog() = DialogResult.OK Then
' يتم تغيير خاصية forecolor في TextBox
txtTexte.ForeColor = colorDialog1.Color
End If
End Sub
Private Sub btnPolice_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnPolice.Click
' اختيار خط
If fontDialog1.ShowDialog() = DialogResult.OK Then
' تغيير خاصية font في TextBox
txtTexte.Font = fontDialog1.Font
End If
End Sub
5.7.3. Timer
نقترح هنا كتابة التطبيق التالي:
![]() |
رقم | النوع | الاسم | الدور |
1 | TextBox، ReadOnly=true | txtChrono | يعرض ساعة توقيت |
2 | زر | btnArretMarche | زر تشغيل/إيقاف ساعة التوقيت |
3 | المؤقت | timer1 | مكون يصدر هنا حدثًا كل ثانية |
العداد قيد التشغيل:

العداد متوقف:

لتغيير محتوى TextBox و txtChrono كل ثانية، نحتاج إلى مكون يولد حدثًا كل ثانية، وهو حدث يمكننا اعتراضه لتحديث عرض ساعة التوقيت. وهذا المكون هو Timer:

بمجرد تثبيت هذا المكون على النموذج (في قسم المكونات غير المرئية)، يتم إنشاء كائن من النوع Timer في مُنشئ النموذج. يتم تعريف الفئة System.Windows.Forms.Timer على النحو التالي:

ومن بين خصائصها، سنكتفي بالآتي:
عدد المللي ثانية التي يتم بعدها إصدار حدث Tick. | |
الحدث الذي يحدث بعد مرور Interval ميلي ثانية | |
يجعل المؤقت نشطًا (true) أو غير نشط (false) |
في مثالنا، يُسمى المؤقت timer1، وقد تم ضبط timer1.Interval على 1000 مللي ثانية (1 ثانية). وبالتالي، سيحدث الحدث Tick كل ثانية. تتم معالجة النقر على زر «إيقاف/تشغيل» من خلال الإجراء التالي:
Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnArretMarche.Click
' إيقاف أم تشغيل؟
If btnArretMarche.Text = "Marche" Then
' تدوين وقت البدء
début = DateTime.Now
' عرضه
txtChrono.Text = "00:00:00"
' يتم تشغيل المؤقت
timer1.Enabled = True
' يتم تغيير نص الزر
btnArretMarche.Text = "Arrêt"
' النهاية
Return
End If '
If btnArretMarche.Text = "Arrêt" Then
' إيقاف المؤقت
timer1.Enabled = False
' تغيير نص الزر
btnArretMarche.Text = "Marche"
' النهاية
Return
End If
End Sub
Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles timer1.Tick
' مرت ثانية واحدة
Dim maintenant As DateTime = DateTime.Now
Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
End Sub
تكون تسمية زر «إيقاف/تشغيل» إما «إيقاف» أو «تشغيل». لذا، لا بد من إجراء اختبار على هذه التسمية لمعرفة الإجراء المطلوب.
- في حالة «تشغيل»، يتم تسجيل وقت البدء في متغير يمثل متغيرًا عامًا لكائن النموذج، ويتم تشغيل المؤقت (Enabled=true) ويتغير نص الزر إلى «إيقاف».
- في حالة «إيقاف»، يتم إيقاف المؤقت (Enabled=false) وتغيير نص الزر إلى «تشغيل».
Public Class Timer1
Inherits System.Windows.Forms.Form
Private WithEvents timer1 As System.Windows.Forms.Timer
Private WithEvents btnArretMarche As System.Windows.Forms.Button
Private components As System.ComponentModel.IContainer
Private WithEvents txtChrono As System.Windows.Forms.TextBox
Private WithEvents label1 As System.Windows.Forms.Label
' متغيرات المثيل
Private début As DateTime
السمة début المذكورة أعلاه معروفة في جميع أساليب الفئة. يبقى لنا معالجة الحدث Tick على الكائن timer1، وهو حدث يحدث كل ثانية:
Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles timer1.Tick
' مرت ثانية واحدة
Dim maintenant As DateTime = DateTime.Now
Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
End Sub
نحسب الوقت المنقضي منذ لحظة بدء تشغيل عداد الوقت. ونحصل على كائن من النوع TimeSpan يمثل مدة زمنية. يجب عرض هذه المدة في عداد الوقت بالصيغة hh:mm:ss. وللقيام بذلك، نستخدم الخصائص Hours و Minutes، Seconds الخاصة بالكائن TimeSPan، والتي تمثل على التوالي الساعات والدقائق والثواني للمدة التي نعرضها بتنسيق ToString("d2") للحصول على عرض مكون من رقمين.
5.8. المثال IMPOTS
نعود إلى التطبيق IMPOTS الذي تمت معالجته مرتين من قبل. نضيف إليه الآن واجهة رسومية:
![]() |
العناصر التالية هي عناصر التحكم
رقم | النوع | الاسم | الدور |
RadioButton | rdOui | يُوضع علامة إذا كان متزوجًا | |
RadioButton | rdNon | ضع علامة إذا لم تكن متزوجًا | |
NumericUpDown | incEnfants | عدد أطفال المكلف الحد الأدنى=0، الحد الأقصى=20، الزيادة=1 | |
TextBox | txtSalaire | الراتب السنوي للمكلف بالضريبة بالفرنك الفرنسي | |
TextBox | txtImpots | مبلغ الضريبة المستحقة ReadOnly=true | |
زر | btnCalculer | يبدأ حساب الضريبة | |
زر | btnEffacer | يعيد النموذج إلى حالته الأصلية عند التحميل | |
زر | btnQuitter | للخروج من التطبيق |
قواعد التشغيل
- يظل زر «حساب» معطلاً طالما لم يتم إدخال أي قيمة في حقل الراتب
- إذا تبين عند بدء الحساب أن الراتب غير صحيح، يتم الإبلاغ عن الخطأ:

البرنامج موضح أدناه. ويستخدم الفئة impot التي تم إنشاؤها في الفصل الخاص بالفئات. ولم يتم هنا إعادة كتابة جزء من الكود الذي تم إنشاؤه تلقائيًا بواسطة VS.NET.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' خيارات
Option Explicit On
Option Strict On
' مساحات الأسماء
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
' فئة النموذج
Public Class frmImpots
Inherits System.Windows.Forms.Form
Private WithEvents label1 As System.Windows.Forms.Label
Private WithEvents rdOui As System.Windows.Forms.RadioButton
Private WithEvents rdNon As System.Windows.Forms.RadioButton
Private WithEvents label2 As System.Windows.Forms.Label
Private WithEvents txtSalaire As System.Windows.Forms.TextBox
Private WithEvents label3 As System.Windows.Forms.Label
Private WithEvents label4 As System.Windows.Forms.Label
Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
Private WithEvents btnCalculer As System.Windows.Forms.Button
Private WithEvents btnEffacer As System.Windows.Forms.Button
Private WithEvents btnQuitter As System.Windows.Forms.Button
Private WithEvents txtImpots As System.Windows.Forms.TextBox
Private components As System.ComponentModel.Container = Nothing
Private WithEvents incEnfants As System.Windows.Forms.NumericUpDown
' جداول البيانات اللازمة لحساب الضريبة
Private limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
Private coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.55D, 0.5D, 0.6D, 0.65D}
Private coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
' كائن الضريبة
Private objImpôt As impot = Nothing
Public Sub New()
InitializeComponent()
' تهيئة النموذج
btnEffacer_Click(Nothing, Nothing)
btnCalculer.Enabled = False
' إنشاء كائن ضريبي
Try
objImpôt = New impot(limites, coeffR, coeffN)
Catch ex As Exception
MessageBox.Show("Impossible de créer l'objet impôt (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
' تعطيل حقل إدخال الراتب
txtSalaire.Enabled = False
End Try 'try-catch
End Sub
Protected Overloads Sub Dispose(ByVal disposing As Boolean)
....
End Sub
Private Sub InitializeComponent()
Me.btnQuitter = New System.Windows.Forms.Button
Me.groupBox1 = New System.Windows.Forms.GroupBox
Me.btnEffacer = New System.Windows.Forms.Button
Me.btnCalculer = New System.Windows.Forms.Button
Me.txtSalaire = New System.Windows.Forms.TextBox
Me.label1 = New System.Windows.Forms.Label
Me.label2 = New System.Windows.Forms.Label
Me.label3 = New System.Windows.Forms.Label
Me.rdNon = New System.Windows.Forms.RadioButton
Me.txtImpots = New System.Windows.Forms.TextBox
Me.label4 = New System.Windows.Forms.Label
Me.rdOui = New System.Windows.Forms.RadioButton
Me.incEnfants = New System.Windows.Forms.NumericUpDown
Me.groupBox1.SuspendLayout()
CType(Me.incEnfants, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
....
End Sub 'InitializeComponent
Public Shared Sub Main()
Application.Run(New frmImpots)
End Sub 'Main
Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnEffacer.Click
' مسح النموذج
incEnfants.Value = 0
txtSalaire.Text = ""
txtImpots.Text = ""
rdNon.Checked = True
End Sub 'btnEffacer_Click
Private Sub txtSalaire_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles txtSalaire.TextChanged
' حالة زر «حساب»
btnCalculer.Enabled = txtSalaire.Text.Trim() <> ""
End Sub 'txtSalaire_TextChanged
Private Sub btnQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnQuitter.Click
' إنهاء التطبيق
Application.Exit()
End Sub 'btnQuitter_Click
Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles btnCalculer.Click
' هل الراتب صحيح؟
Dim intSalaire As Integer = 0
Try
' استرداد الراتب
intSalaire = Integer.Parse(txtSalaire.Text)
' يجب أن يكون >=0
If intSalaire < 0 Then
Throw New Exception("")
End If
Catch ex As Exception
' رسالة خطأ
MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
' التركيز على الحقل الخاطئ
txtSalaire.Focus()
' تحديد نص حقل الإدخال
txtSalaire.SelectAll()
' العودة إلى الواجهة المرئية
Return
End Try 'try-catch
' الراتب صحيح - يتم حساب الضريبة
txtImpots.Text = "" & CLng(objImpôt.calculer(rdOui.Checked, CInt(incEnfants.Value), intSalaire))
End Sub 'btnCalculer_Click
End Class
نستخدم هنا ملف التجميع impots.dll الناتج عن ترجمة فئة impots الواردة في الفصل 2. تجدر الإشارة إلى أنه يمكن إنتاج ملف التجميع هذا في وضع وحدة التحكم باستخدام الأمر
يُنتج هذا الأمر الملف impots.dll المسمى «التجميع». ويمكن استخدام هذا التجميع لاحقًا في مشاريع مختلفة. هنا، في مشروعنا تحت VS.NET، نستخدم نافذة خصائص المشروع:

لإضافة مرجع (تجميع)، نضغط بزر الفأرة الأيمن على المجموعة الرئيسية «References» أعلاه، ونختار الخيار [Ajouter une référence]، ثم نحدد التجميع [impots.dll] الذي حرصنا على وضعه في مجلد المشروع:
dos>dir
01/03/2004 14:39 9 250 gui_impots.vb
01/03/2004 14:37 4 096 impots.dll
01/03/2004 14:41 12 288 gui_impots.exe
بمجرد تضمين التجميع [impots.dll] في المشروع، تصبح الفئة [impots] معروفة للمشروع. في السابق لم تكن معروفة. وهناك طريقة أخرى تتمثل في تضمين الملف المصدري impots.vb في المشروع. للقيام بذلك، في نافذة خصائص المشروع، انقر بزر الماوس الأيمن على المشروع واختر الخيار [Ajouter/Ajouter un élément existant] ثم حدد الملف impots.vb.















































