Skip to content

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

يتم ترجمة الكود السابق ثم تنفيذه

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm1.vb

dos>frm1

يؤدي التنفيذ إلى عرض النافذة التالية:

Image

عادةً ما تُشتق واجهة المستخدم الرسومية من الفئة الأساسية System.Windows.Forms.Form:

Public Class Form1
    Inherits 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 محدد على النحو التالي:

Public Event Click As EventHandler

حيث EventHandler هي دالة «مفوضة» لها التوقيع التالي:

Public Delegate Sub EventHandler(ByVal sender As Object,ByVal e As EventArgs)

وهذا يعني أن معالج الحدث [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

يقتصر الأمر على عرض رسالة:

Image

يتم ترجمة الفئة وتنفيذها:

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 قيمها من بين الثوابت التالية:

الثابت
الأزرار
   AbortRetryIgnore
  OK
    OKCancel
    RetryCancel
    YesNo
    YesNoCancel

يمكن أن تأخذ المعلمة icon قيمها من بين الثوابت التالية:

Asterisk
Error
نفس الشيء توقف
Exclamation
نفس الشيء تحذير
Hand
Information
كما هو الحال مع Asterisk
None
Question
Stop
نفس الشيء يدويًا
Warning

 

الطريقة 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. الإنشاء الأولي للمشروع

  1. قم بتشغيل VS.NET واختر الخيار Fichier/Nouveau/Projet

Image

  1. وأدخل مواصفات مشروعك
    1. اختر نوع المشروع الذي تريد إنشاؤه، وهنا مشروع VB.NET (1)
    2. حدد نوع التطبيق الذي تريد إنشاؤه، هنا تطبيق ويندوز (2)
    3. حدد المجلد الذي تريد وضع المجلد الفرعي للمشروع فيه (3)
    4. حدد اسم المشروع (4). سيكون هذا أيضًا اسم المجلد الذي سيحتوي على ملفات المشروع
    5. يتم تكرار اسم هذا المجلد في (5)
  1. يتم بعد ذلك إنشاء عدد من المجلدات والملفات ضمن المجلد i4:
المجلدات الفرعية لمجلد «project1»

من بين هذه الملفات، هناك ملف واحد فقط مهم، وهو الملف form1.cs الذي يمثل الملف المصدر المرتبط بالنموذج الذي تم إنشاؤه بواسطة VS.NET. سنعود إلى هذا الموضوع لاحقًا.

5.2.2. نوافذ واجهة VS.NET

تُظهر واجهة VS.NET الآن بعض عناصر مشروعنا i4:

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

من خلال سحب عناصر التحكم من شريط الأدوات (toolbox 2) ووضعها على سطح النافذة (1)، يمكننا إنشاء واجهة رسومية. إذا حركنا الماوس فوق «toolbox»، فإنها تتوسع وتظهر عددًا من عناصر التحكم:

Image

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

Image

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

Image

يمكن الوصول إلى هذين العنصرين مباشرةً من نافذة «Solution Explorer»:

 

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

Image

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

Image

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

Image

تتضمن هذه القائمة النوافذ الرئيسية التي تم وصفها للتو بالإضافة إلى اختصارات لوحة المفاتيح الخاصة بها.

5.2.3. تنفيذ مشروع

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

Image

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

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:

Public Class Form1
    Inherits 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]:

Image

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

Image

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]. دعونا نضيف التعليمات التالية:

Imports System.Windows.Forms

ثم نعيد التجميع:

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

هذه المرة، نجحت العملية. لنقم بتنفيذ:

dos>dir
14/03/2004  11:53               514 Form1.vb
14/03/2004  12:15             3 584 Form1.exe

dos>form1

Image

يتم إنشاء نموذج من النوع Form1 وعرضه. يمكن تجنب إضافة الإجراء [Main] باستخدام الخيار /m للمترجم، الذي يسمح بتحديد الفئة المراد تنفيذها في حالة ما إذا كانت هذه الفئة ترث من System.Windows.Form:

dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /m:form2 form2.vb

تشير الخيار /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. وللقيام بذلك، نتبع الإجراء الموضح سابقًا لإنشاء مشروع:

Image

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

الحقول هي كما يلي:

رقم
الاسم
النوع
الدور
1
lblSaisie
العلامة
وصف
2
txtSaisie
TextBox
حقل إدخال
3
btnAfficher
زر
لعرض محتوى حقل الإدخال في مربع حوار txtSaisie

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

Image

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

Image

تجدر الإشارة إلى بعض هذه الخصائص:

BackColor
لتحديد لون خلفية النافذة
ForeColor
لتحديد لون الرسومات أو النص في النافذة
Menu
لربط قائمة بالنافذة
Text
لتسمية النافذة
FormBorderStyle
لتحديد نوع النافذة
Font
لتحديد خط النص في النافذة
Name
لتحديد اسم النافذة

هنا، نقوم بتعيين الخصائص Text و Name:

Text
حقول الإدخال والأزرار - 1
Name
frmSaisiesBoutons

باستخدام شريط «صندوق الأدوات»

  • حدد المكونات التي تحتاجها
  • ضعها في النافذة واضبط أبعادها بشكل صحيح
بمجرد اختيار المكون من "صندوق الأدوات"، استخدم المفتاح
"Echap" لإخفاء شريط الأدوات، ثم قم بسحب
المكون وقم بتحديد أبعاده. كرر هذه الخطوات للمكونات الثلاثة
المطلوبة: Label، TextBox، Button. لمحاذاة و
تحديد أبعاد المكونات بشكل صحيح، استخدم القائمة Format:
مبدأ التنسيق هو كما يلي:
  1. حدد المكونات المختلفة المراد تنسيقها معًا (مع الضغط على مفتاح Ctrl)
  2. حدد نوع التنسيق المطلوب
يتيح لك الخيار Align محاذاة المكونات
تتيح لك الخيار Make Same Size جعل
المكونات بنفس الارتفاع أو بنفس
العرض:
تتيح خيار «Horizontal Spacing» (المسافة الأفقية)، على سبيل المثال
محاذاة المكونات أفقيًا مع
بفواصل متساوية الحجم بينها. وينطبق الأمر نفسه
بالنسبة لخيار «المسافة الرأسية» (Vertical Spacing) للمحاذاة الرأسية.
تتيح خيار «Center in Form» (التوسيط في النموذج) توسيط
مكونًا أفقيًا أو
عموديًا في النافذة:
بمجرد وضع المكونات بشكل صحيح
في النافذة، قم بتحديد خصائصها.
للقيام بذلك، انقر بزر الماوس الأيمن على المكون و
اختر الخيار Properties:
  • التسمية 1 (Label)
حدد المكون لفتح
نافذة خصائصه. وفي هذه النافذة،
قم بتعديل الخصائص التالية:
name: lblSaisie، text: إدخال
  • حقل الإدخال 2 (TextBox)
حدد المكون لعرض
نافذة الخصائص الخاصة به. في هذه النافذة، قم بتعديل
الخصائص التالية:
الاسم: txtSaisie، النص: لا تدخل أي شيء
  • الزر 3 (Button):
name: cmdAfficher، text: عرض
  • النافذة نفسها: الاسم: frmSaisies&الأزرار،
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) هي قائمة مكونات النموذج:

Image

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

Image

يتم عرض أحد الأحداث المرتبطة بالمكون بخط عريض (هنا 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. يحتوي معالج الحدث السابق على معلمتين:

sender
الكائن الذي تسبب في الحدث (في هذه الحالة الزر)
e
كائن 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

عند تشغيل التطبيق، نحصل على النتيجة التالية:

Image

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 بربط حدث بإجراء أثناء التنفيذ. وهذا مفيد في سياق «المنتج والمستهلك» للأحداث. تُنتج كائن ما حدثًا معينًا قد يهم كائنات أخرى. فتقوم هذه الكائنات بالاشتراك لدى المنتج لتلقي الحدث (مثل تجاوز درجة الحرارة لعتبة حرجة، على سبيل المثال). وأثناء تشغيل التطبيق، سيُطلب من منتج الحدث تنفيذ تعليمات مختلفة:
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

حيث E هو الحدث الذي ينتجه المنتج، وPi هي الإجراءات التي تنتمي إلى الكائنات المختلفة المستهلكة لهذا الحدث. سنعود إلى تطبيق «المنتج-المستهلك» للأحداث في فصل لاحق.

5.3.4. الخلاصة

من المشروعين اللذين درسناهما، يمكننا أن نستنتج أنه بمجرد إنشاء الواجهة الرسومية باستخدام VS.NET، فإن عمل المطور يتمثل في كتابة معالجات الأحداث التي يرغب في إدارتها لهذه الواجهة الرسومية. ولن نعرض من الآن فصاعدًا سوى كود هذه المعالجات.

5.4. بعض المكونات المفيدة

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

5.4.1. نموذج Form

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

 
يتم تحميل النموذج حالياً
Closing
النموذج قيد الإغلاق
Closed
النموذج مغلق

يحدث الحدث Load قبل حتى عرض النموذج. ويحدث الحدث Closing أثناء إغلاق النموذج. ولا يزال من الممكن إيقاف هذا الإغلاق برمجيًا. نقوم بإنشاء نموذج باسم Form1 بدون مكونات:

Image

نقوم بمعالجة الأحداث الثلاثة السابقة:


    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 متعدد الأسطر، يتم ضبط الخصائص التالية لعنصر التحكم:

 
لقبول عدة أسطر من النص
ScrollBars=( None, Horizontal, Vertical, Both)
لطلب أن يحتوي عنصر التحكم على أشرطة تمرير (أفقي، عمودي، كلاهما) أو لا (لا شيء)
AcceptReturn=(True, False)
إذا كانت القيمة «true»، فستؤدي مفتاح «Enter» إلى الانتقال إلى السطر التالي
AcceptTab=(True, False)
إذا كانت القيمة "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:

 
قائمة غير منسدلة مع منطقة تحرير
DropDown
قائمة منسدلة مع منطقة تحرير
DropDownList
قائمة منسدلة بدون منطقة تحرير

بشكل افتراضي، يكون نوع ComboBox هو DropDown. للاطلاع على الفئة ComboBox، اكتب ComboBox في فهرس المساعدة (Aide/Index). تحتوي الفئة ComboBox على منشئ واحد:

Public Sub New()
يُنشئ كائن ComboBox فارغًا

تتوفر عناصر ComboBox في الخاصية Items:

Public ReadOnly Property Items As ComboBox.ObjectCollection

وهي خاصية مفهرسة، حيث يشير Items(i) إلى العنصر i في قائمة الاختيار. لنفترض أن C هي قائمة اختيار وC.Items هي قائمة عناصرها. لدينا الخصائص التالية:

 
عدد عناصر قائمة الاختيار
C.Items(i)
العنصر i في قائمة الاختيار
C.Add(object o)
يضيف الكائن o كآخر عنصر في القائمة المنسدلة
C.AddRange(object() objets)
يضيف مصفوفة من الكائنات في نهاية القائمة المنسدلة
C.Insert(int i, object o)
يضيف الكائن o إلى الموضع i في القائمة المنسدلة
C.RemoveAt(int i)
يحذف العنصر i من القائمة المنسدلة
C.Remove(object o)
يزيل الكائن o من القائمة المنسدلة
C.Clear()
حذف جميع العناصر من القائمة المنسدلة
C.IndexOf(object o)
يعرض الموضع i للعنصر o في قائمة الاختيار

قد يثير الدهشة أن قائمة منسدلة يمكن أن تحتوي على كائنات في حين أنها عادةً ما تحتوي على سلاسل أحرف. من الناحية المرئية، سيكون هذا هو الحال. إذا كانت قائمة منسدلة ComboBox تحتوي على كائن obj، فإنها تعرض السلسلة obj.ToString(). نتذكر أن كل كائن لديه طريقة ToString موروثة من الفئة object والتي تُرجع سلسلة أحرف «تمثل» الكائن. العنصر المحدد في القائمة المنسدلة C هو C.SelectedItem أو C.Items(C.SelectedIndex) حيث C.SelectedIndex هو رقم العنصر المحدد، ويبدأ هذا الرقم من الصفر بالنسبة للعنصر الأول.

عند اختيار عنصر من القائمة المنسدلة، يحدث الحدث SelectedIndexChanged الذي يمكن استخدامه للتنبيه بتغيير الاختيار في القائمة المنسدلة. في التطبيق التالي، نستخدم هذا الحدث لعرض العنصر الذي تم تحديده في القائمة.

Image

نقدم فقط الكود ذي الصلة بالنافذة. في مُنشئ النموذج، نملأ القائمة المنسدلة:


  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. أحداث الماوس

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

 
دخلت الماوس للتو إلى نطاق عنصر التحكم
MouseLeave
لقد غادرت الماوس للتو نطاق عنصر التحكم
MouseMove
الماوس يتحرك داخل منطقة التحكم
MouseDown
الضغط على الزر الأيسر للماوس
MouseUp
تحرير الزر الأيسر للماوس
DragDrop
يقوم المستخدم بإفلات عنصر على عنصر التحكم
DragEnter
يقوم المستخدم بالدخول إلى منطقة عنصر التحكم عن طريق سحب عنصر
DragLeave
يخرج المستخدم من منطقة عنصر التحكم عن طريق سحب كائن
DragOver
يمر المستخدم فوق منطقة عنصر التحكم عن طريق سحب كائن

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

رقم
النوع
الاسم
الدور
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 الخاصة بعناصر التحكم:

Image

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

Image

Image

Image

في الأسلوب [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

Image

ولمعالجة النقرات على النموذج، نقوم بمعالجة الأحداث 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

Image

وأخيرًا، رمز معالج النقر على الزر 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:

Image

يتم استخدام خاصيتين في مثالنا:

 
اسم عنصر التحكم الخاص بالقائمة
Text
تسمية خيار القائمة

فيما يلي خصائص خيارات القائمة المختلفة في مثالنا:

Name
النص
mnuA
خيارات A
mnuA1
A1
mnuA2
A2
mnuA3
A3
mnuB
خيارات ب
mnuB1
B1
mnuSep1
- (فاصل)
mnuB2
B2
mnuB3
B3
mnuB31
B31
mnuB32
B32

لإنشاء قائمة، نختار المكون "MainMenu" من الشريط "ToolBox":

Image

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

Image

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

Image

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

Image

إذا تم إنشاء الحدث 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 للحصول على الرسالة التالية:

Image

الرمز المفيد في هذا التطبيق، إلى جانب رمز الطريقة 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

يتم استخدام عنصرين غير مرئيين:

Image

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

Image

رمز الزر Effacer بسيط:


    Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' يتم مسح مربع الإدخال
        txtTexte.Text = ""
    End Sub

يتم تعريف الفئة SaveFileDialog على النحو التالي:

Image

وهي مشتقة من عدة مستويات من الفئات. ومن بين هذه الخصائص والطرق العديدة، سنستعرض ما يلي:

string Filter
أنواع الملفات المتوفرة في القائمة المنسدلة لأنواع الملفات في مربع الحوار
int FilterIndex
رقم نوع الملف المقترح افتراضيًا في القائمة أعلاه. يبدأ من 0.
String
InitialDirectory
المجلد المحدد مسبقًا لحفظ الملف
string FileName
اسم ملف النسخ الاحتياطي الذي حدده المستخدم
DialogResult.ShowDialog()
الطريقة التي تعرض مربع حوار النسخ الاحتياطي. تُرجع نتيجة من النوع DialogResult.

تعرض الطريقة ShowDialog مربع حوار مشابهًا لما يلي:

1
قائمة منسدلة تم إنشاؤها استنادًا إلى الخاصية Filter. يتم تحديد نوع الملف المقترح افتراضيًّا بواسطة FilterIndex
2
المجلد الحالي، ويتم تحديده بواسطة InitialDirectory إذا تم تعبئة هذه الخاصية
3
اسم الملف الذي تم اختياره أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName
4
أزرار «حفظ»/«إلغاء». إذا تم استخدام الزر 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
  • يتم تعيين المجلد الأولي على المجلد الذي يحتوي على الملف التنفيذي للتطبيق:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • نحدد أنواع الملفات المطلوب عرضها
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

يُلاحظ أن صيغة المرشحات هي filtre1|filtre2|..|filtren حيث filtrei = نص|نموذج ملف. هنا سيكون للمستخدم الخيار بين الملفات *.txt و *.*.

  • نحدد نوع الملف المراد عرضه في البداية
        saveFileDialog1.FilterIndex = 0

هنا، سيتم عرض الملفات من النوع *.txt أولاً للمستخدم.

  • يتم عرض مربع الحوار واسترداد نتيجته

    If saveFileDialog1.ShowDialog() = DialogResult.OK Then
  • أثناء عرض مربع الحوار، لا يستطيع المستخدم الوصول إلى النموذج الرئيسي (ما يُعرف بمربع الحوار «الوضع الموضعي»). يحدد المستخدم اسم الملف المراد حفظه ويغادر مربع الحوار إما عن طريق الزر «حفظ»، أو الزر «إلغاء»، أو بإغلاق مربع الحوار. تكون نتيجة الأسلوب ShowDialog هي DialogResult.OK فقط إذا استخدم المستخدم الزر Enregistrer لمغادرة مربع الحوار.
  • وبذلك، يصبح اسم الملف المراد إنشاؤه موجودًا الآن في الخاصية FileName للكائن saveFileDialog1. وبذلك نعود إلى الطريقة التقليدية لإنشاء ملف نصي. نكتب فيه محتوى TextBox: txtTexte.Text مع معالجة الاستثناءات التي قد تحدث.

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

string Filter
أنواع الملفات المعروضة في القائمة المنسدلة لأنواع الملفات في مربع الحوار
int FilterIndex
رقم نوع الملف المقترح افتراضيًا في القائمة أعلاه. يبدأ من 0.
string InitialDirectory
المجلد المعروض مبدئيًا للبحث عن الملف المراد فتحه
string FileName
اسم الملف المراد فتحه الذي حدده المستخدم
DialogResult.ShowDialog()
الطريقة التي تعرض مربع حوار الحفظ. تُرجع نتيجة من النوع DialogResult.

تعرض الطريقة ShowDialog مربع حوار مشابهًا لما يلي:

1
قائمة منسدلة مبنية على أساس الخاصية Filter. يتم تحديد نوع الملف المقترح افتراضيًا بواسطة FilterIndex
2
المجلد الحالي، الذي يتم تحديده بواسطة InitialDirectory إذا تم ملء هذه الخاصية
3
اسم الملف الذي تم اختياره أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName
4
أزرار «فتح»/«إلغاء». إذا تم استخدام الزر 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
  • نحدد المجلد الأولي بالمجلد الذي يحتوي على الملف التنفيذي للتطبيق:
            saveFileDialog1.InitialDirectory=Application.ExecutablePath
  • نحدد أنواع الملفات المطلوب عرضها
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
  • يتم تحديد نوع الملف الذي سيتم عرضه في البداية
            saveFileDialog1.FilterIndex = 0

في هذه الحالة، سيتم عرض الملفات من النوع *.txt أولاً للمستخدم.

  • يتم عرض مربع الحوار واسترداد نتيجته

    If openFileDialog1.ShowDialog() = DialogResult.OK Then

أثناء عرض مربع الحوار، لا يمكن للمستخدم الوصول إلى النموذج الرئيسي (ما يُعرف بمربع الحوار "الوضع"). يحدد المستخدم اسم الملف المراد فتحه ويغادر مربع الحوار إما عن طريق الزر "فتح" أو الزر "إلغاء" أو بإغلاق مربع الحوار. تكون نتيجة الدالة ShowDialog هي DialogResult.OK فقط إذا استخدم المستخدم الزر Ouvrir لمغادرة مربع الحوار.

  • وبذلك، يصبح اسم الملف المراد إنشاؤه موجودًا الآن في الخاصية FileName للكائن openFileDialog1. ونعود عندئذٍ إلى القراءة التقليدية لملف نصي. تجدر الإشارة إلى الطريقة التي تسمح بقراءة الملف بالكامل:
                    txtTexte.Text=fichier.ReadToEnd
  • يتم وضع محتوى الملف في TextBox و txtTexte. يتم التعامل مع الاستثناءات التي قد تحدث.

5.7.2. مربعات الحوار FontColor و ColorDialog

نواصل المثال السابق بعرض زرين جديدين:

رقم
النوع
الاسم
الدور
6
زر
btnCouleur
لتحديد لون أحرف TextBox
7
زر
btnPolice
لتحديد خط الحروف في TextBox

نقوم بإضافة عنصر تحكم ColorDialog وعنصر تحكم FontDialog إلى النموذج:

Image

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

Image

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

Image

إذا غادر المستخدم مربع الحوار باستخدام الزر 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
مكون يصدر هنا حدثًا كل ثانية

العداد قيد التشغيل:

Image

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

Image

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

Image

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

Image

ومن بين خصائصها، سنكتفي بالآتي:

Interval
عدد المللي ثانية التي يتم بعدها إصدار حدث Tick.
Tick
الحدث الذي يحدث بعد مرور Interval ميلي ثانية
Enabled
يجعل المؤقت نشطًا (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 الذي تمت معالجته مرتين من قبل. نضيف إليه الآن واجهة رسومية:

العناصر التالية هي عناصر التحكم

رقم
النوع
الاسم
الدور
1
RadioButton
rdOui
يُوضع علامة إذا كان متزوجًا
2
RadioButton
rdNon
ضع علامة إذا لم تكن متزوجًا
3
NumericUpDown
incEnfants
عدد أطفال المكلف
الحد الأدنى=0، الحد الأقصى=20، الزيادة=1
4
TextBox
txtSalaire
الراتب السنوي للمكلف بالضريبة بالفرنك الفرنسي
5
TextBox
txtImpots
مبلغ الضريبة المستحقة
ReadOnly=true
6
زر
btnCalculer
يبدأ حساب الضريبة
7
زر
btnEffacer
يعيد النموذج إلى حالته الأصلية عند التحميل
8
زر
btnQuitter
للخروج من التطبيق

قواعد التشغيل

  • يظل زر «حساب» معطلاً طالما لم يتم إدخال أي قيمة في حقل الراتب
  • إذا تبين عند بدء الحساب أن الراتب غير صحيح، يتم الإبلاغ عن الخطأ:

Image

البرنامج موضح أدناه. ويستخدم الفئة 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. تجدر الإشارة إلى أنه يمكن إنتاج ملف التجميع هذا في وضع وحدة التحكم باستخدام الأمر

dos>vbc /t:library impots.vb

يُنتج هذا الأمر الملف impots.dll المسمى «التجميع». ويمكن استخدام هذا التجميع لاحقًا في مشاريع مختلفة. هنا، في مشروعنا تحت VS.NET، نستخدم نافذة خصائص المشروع:

Image

لإضافة مرجع (تجميع)، نضغط بزر الفأرة الأيمن على المجموعة الرئيسية «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.