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. نافذة بسيطة

انظر إلى الكود التالي:


' options
Option Strict On
Option Explicit On 
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
' the form class
Public Class Form1
    Inherits Form
 
    ' the manufacturer
    Public Sub New()
        ' window title
        Me.Text = "Mon premier formulaire"
        ' window dimensions
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub
 
    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' the form is displayed
        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()
        ' window title
        Me.Text = "Mon premier formulaire"
        ' window dimensions
        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. نموذج مع زر

الآن دعونا نضيف زرًا إلى نافذتنا:


' options
Option Strict On
Option Explicit On 
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
' the form class
Public Class Form1
    Inherits Form
 
    ' attributes
    Private cmdTest As Button
 
    ' the manufacturer
    Public Sub New()
        ' the title
        Me.Text = "Mon premier formulaire"
        ' dimensions
        Me.Size = New System.Drawing.Size(300, 100)
        ' a button
        ' creation
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' size
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' wording
        cmdTest.Text = "Test"
        ' event manager
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' add button to form
        Me.Controls.Add(cmdTest)
    End Sub
 
    ' event manager
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' there was a click on the button - we say it
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub
 
    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' the form is displayed
        Application.Run(New Form1)
    End Sub
End Class

لقد أضفنا زرًا إلى النموذج:


        ' un bouton
        ' création
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' taille
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' libellé
        cmdTest.Text = "Test"
        ' gestionnaire d'évt
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' ajout bouton au formulaire
        Me.Controls.Add(cmdTest)

تحدد الخاصية Location إحداثيات (110,20) للزاوية العلوية اليسرى للزر باستخدام بنية Point. يتم تعيين عرض الزر وارتفاعه إلى (80,30) باستخدام بنية Size. تحدد الخاصية Text للزر تسمية الزر. تحتوي فئة Button على حدث 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 السابق:


    ' event manager
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' there was a click on the button - we say it
        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 قيمًا من الثوابت التالية:

الثابت
buttons
   إلغاء إعادة المحاولة تجاهل
  موافق
    موافقإلغاء
    إعادة المحاولةإلغاء
    نعملا
    نعملاإلغاء

يمكن أن تأخذ معلمة الرمز قيمًا من الثوابت التالية:

علامة النجمة
خطأ
كما هو مذكور أعلاه توقف
تعجب
مثل التحذير
اليد
معلومات
مثل 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
' he pressed the yes button
...
end if

5.2. إنشاء واجهة مستخدم رسومية باستخدام Visual Studio.NET

سنعيد النظر في بعض الأمثلة التي رأيناها سابقًا، وسنقوم هذه المرة بإنشائها باستخدام Visual Studio.NET.

5.2.1. إنشاء المشروع الأولي

  1. قم بتشغيل VS.NET واختر ملف/جديد/مشروع

Image

  1. حدد خصائص مشروعك
    1. حدد نوع المشروع الذي تريد إنشاءه؛ هنا، مشروع VB.NET (1)
    2. حدد نوع التطبيق الذي تريد إنشاءه؛ هنا، تطبيق Windows (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 إلى ظهور قائمة تتيح لك الوصول إما إلى كود المصدر لواجهة المستخدم الرسومية (View Code) أو إلى واجهة المستخدم الرسومية نفسها (Form Designer):

Image

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

 

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

Image

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

Image

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

Image

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

5.2.3. تشغيل مشروع

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

Image

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

5.2.4. الكود الذي تم إنشاؤه بواسطة VS.NET

دعونا نلقي نظرة على الكود (View/Code) لتطبيقنا:


Public Class Form1
    Inherits System.Windows.Forms.Form
 
#Region " Code généré par le Concepteur Windows Form "
 
    Public Sub New()
        MyBase.New()
 
        'This call is required by the Windows Form Designer.
        InitializeComponent()
 
        'Add any initialization after InitializeComponent() call
 
    End Sub
 
    'The substituted method Disposes of the form to clean up the list of components.
    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
 
    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer
 
    'REMARQUE: the following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.Text = "Form2"
    End Sub
 
#End Region
 
End Class

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

Public Class Form1
    Inherits System.Windows.Forms.Form

تُعرّف الفئة الأساسية Form نافذة أساسية تحتوي على أزرار الإغلاق والتكبير/التصغير، وحجم قابل للتعديل، والمزيد، وتتعامل مع الأحداث على هذه الكائنات الرسومية. يستخدم مُنشئ النموذج طريقة InitializeComponent التي يتم فيها إنشاء عناصر التحكم في النموذج وتهيئتها.


    Public Sub New()
        MyBase.New()
 
        'This call is required by the Windows Form Designer.
        InitializeComponent()
 
        'Add any initialization after InitializeComponent() call
    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 وهي عبارة عن بنية. لتشغيل هذا التطبيق، نحتاج إلى تعريف الوحدة النمطية الرئيسية للمشروع. للقيام بذلك، نستخدم الخيار [Projects/Properties]:

Image

في [Startup Object]، نحدد [Form1]، وهو النموذج الذي أنشأناه للتو. لتشغيل التطبيق، نستخدم الخيار [Debug/Start]:

Image

5.2.5. التجميع في نافذة موجه الأوامر

الآن، دعونا نحاول ترجمة هذا التطبيق وتشغيله في نافذة 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' cannot be found in 'Form1'.

يشير المُجمِّع إلى أنه لا يمكنه العثور على الإجراء [Main]. وبالفعل، لم يقم VS.NET بإنشائه. ومع ذلك، فقد صادفناه بالفعل في الأمثلة السابقة. وهو يأتي بالشكل التالي:


    Shared Sub Main()
        ' launch application
        Application.Run(New Form1) ' where Form1 is the form
    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.Forms:

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 الأساسية (النقرات على أزرار الإغلاق، وأزرار التكبير/التصغير، وتغيير حجم النافذة، وحركة النافذة، وما إلى ذلك). يستخدم النموذج الذي تم إنشاؤه سمة مكونات لا يتم استخدامها في أي مكان. كما أن طريقة Dispose غير ضرورية هنا. وينطبق الأمر نفسه على بعض مساحات الأسماء (Collections، ComponentModel، Data) المستخدمة وتلك المحددة لمشروع projet1. لذلك، في هذا المثال، يمكن تبسيط الكود إلى ما يلي:


Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
Public Class Form1
    Inherits System.Windows.Forms.Form
 
    ' manufacturer
    Public Sub New()
        ' building the form with its components
        InitializeComponent()
    End Sub
 
    Private Sub InitializeComponent()
        ' window size
        Me.Size = New System.Drawing.Size(300, 300)
        ' window title
        Me.Text = "Form1"
    End Sub
 
    Shared Sub Main()
        ' launch application
        Application.Run(New Form1)
    End Sub
End Class

5.2.7. الخلاصة

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

5.3. نافذة تحتوي على حقل إدخال وزر وتسمية

5.3.1. التصميم الرسومي

في المثال السابق، لم نضع أي مكونات في النافذة. سنبدأ مشروعًا جديدًا باسم interface2. للقيام بذلك، سنتبع الإجراء الموصوف سابقًا لإنشاء مشروع:

Image

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

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

رقم
الاسم
النوع
الدور
1
lblInput
تسمية
تسمية
2
txtInput
مربع نص
حقل إدخال
3
btnDisplay
زر
لعرض محتويات مربع النص txtSaisie في مربع حوار

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

Image

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

Image

ومن الجدير بالذكر بعض هذه الخصائص:

BackColor
لتعيين لون خلفية النافذة
ForeColor
لتعيين لون الرسومات أو النص في النافذة
Menu
لربط قائمة بالنافذة
نص
لإعطاء النافذة عنوانًا
FormBorderStyle
لتعيين نوع النافذة
Font
لتعيين الخط للنص في النافذة
الاسم
لتعيين اسم النافذة

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

النص
السحابات والأزرار - 1
الاسم
frmInputButtons

استخدام شريط "Toolbox"

  • حدد المكونات التي تحتاجها
  • أسقطها في النافذة وقم بتعيين أبعادها الصحيحة
بمجرد تحديد المكون من "صندوق الأدوات"، استخدم
مفتاح "Esc" لإخفاء شريط الأدوات، ثم قم بإسقاط
وتغيير حجم المكون. قم بذلك بالنسبة للمكونات الثلاثة
التي تحتاجها: Label، TextBox، Button. لمحاذاة
تحديد حجم المكونات بشكل صحيح، استخدم قائمة "التنسيق":
تعمل عملية التنسيق على النحو التالي:
  1. حدد المكونات المختلفة التي تريد تنسيقها معًا (اضغط باستمرار على مفتاح Ctrl)
  2. حدد خيار التنسيق المطلوب
يتيح لك خيار "محاذاة" محاذاة المكونات
يضمن خيار "جعل الحجم متماثلاً" أن
أن تكون المكونات بنفس الارتفاع أو بنفس
العرض:
خيار "التباعد الأفقي"، على سبيل المثال،
لمحاذاة المكونات أفقيًا مع
مسافة متساوية بينها. وينطبق الأمر نفسه
على خيار التباعد الرأسي للمحاذاة عموديًا.
يتيح لك خيار "التوسيط في النموذج" توسيط
مكونًا أفقيًا أو
عموديًا داخل النافذة:
بمجرد وضع المكونات بشكل صحيح
في النافذة، قم بتعيين خصائصها.
للقيام بذلك، انقر بزر الماوس الأيمن على المكون و
اختر خيار "الخصائص":
  • التسمية 1
حدد المكون لفتح
نافذة "الخصائص" الخاصة به. في هذه النافذة،
قم بتعديل الخصائص التالية:
الاسم: lblSaisie، النص: Saisie
  • حقل الإدخال 2 (TextBox)
حدد المكون لفتح
نافذة الخصائص. في هذه النافذة، قم بتعديل
الخصائص التالية:
الاسم: txtSaisie، النص: اتركه فارغًا
  • الزر 3 (Button):
الاسم: cmdAfficher، النص: عرض
  • النافذة نفسها: الاسم: frmSaisies&Boutons،
النص: الإدخالات والأزرار - 1
يمكننا تشغيل (Ctrl-F5) مشروعنا
لإلقاء نظرة أولية على النافذة
وهي تعمل:

أغلق النافذة. ما زلنا بحاجة إلى كتابة الإجراء المرتبط بالنقر على زر "Display".

5.3.2. معالجة أحداث النموذج

دعونا نلقي نظرة على الكود الذي أنشأه المصمم المرئي:


...
 
Public Class frmSaisiesBoutons
  Inherits System.Windows.Forms.Form
 
  ' components
    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
 
  ' manufacturer
  Public Sub New()
    InitializeComponent()
  End Sub
 
...
 
  ' component initialization
    Private Sub InitializeComponent()
...
    End Sub
End Class
 

أولاً، لاحظ الإعلان المحدد للمكونات:

  • تشير الكلمة الرئيسية Friend إلى أن المكون مرئي لجميع الفئات في المشروع
  • تشير الكلمة الرئيسية WithEvents إلى أن المكون يولد أحداثًا. سننظر الآن في كيفية التعامل مع هذه الأحداث

اعرض نافذة كود النموذج (View/Code أو F7):

تعرض النافذة أعلاه قائمتين منسدلتين (1) و(2). القائمة (1) هي قائمة مكونات النموذج:

Image

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

Image

يتم عرض أحد الأحداث المرتبطة بالمكون بخط عريض (هنا، النقر). هذا هو الحدث الافتراضي للمكون. يمكنك الوصول إلى معالج هذا الحدث المحدد عن طريق النقر المزدوج على المكون في نافذة التصميم. ثم يقوم 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 بتسمية معالج الحدث للحدث 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)
        ' the text entered in the TxtSaisie input box is displayed
        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
 
...
 
    ' event manager click on cmdAfficher button
    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
 
...
 
    ' event manager click on cmdAfficher button
    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 هو الحدث الذي أنتجه المنتج و P1 هو أحد الإجراءات التي تنتمي إلى الكائنات المختلفة التي تستهلك هذا الحدث. سنحظى بفرصة لإعادة النظر في تطبيق أحداث المنتج والمستهلك في فصل لاحق.

5.3.4. الخلاصة

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

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

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

5.4.1. النموذج

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

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

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

Image

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


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' initial form loading
        MessageBox.Show("Evt Load", "Load")
    End Sub
 
    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        ' the form is closing
        MessageBox.Show("Evt Closing", "Closing")
        ' confirmation requested
        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
        ' the form is closing
        MessageBox.Show("Evt Closed", "Closed")
    End Sub
نستخدم دالة MessageBox لإعلامنا بالأحداث المختلفة
الأحداث. يحدث حدث الإغلاق عندما يقوم المستخدم
يغلق النافذة.
ثم نسألهم عما إذا كانوا يريدون حقًا الخروج من التطبيق:
إذا أجاب بـ "لا"، فإننا نعيّن خاصية Cancel في
CancelEventArgs التي تلقتها الطريقة كمعلمة.
إذا قمنا بتعيين هذه الخاصية إلى False،
يتم إلغاء النوافذ؛ وإلا، فإنها تستمر:

5.4.2. عناصر التحكم Label و TextBox

لقد تعرفنا بالفعل على هذين المكونين. Label هو مكون نصي و TextBox هو مكون حقل إدخال. الخاصية الرئيسية لهما هي Text، والتي تشير إما إلى محتوى حقل الإدخال أو نص التسمية. هذه الخاصية قابلة للقراءة/الكتابة. الحدث الذي يستخدم عادةً لـ TextBox هو TextChanged، والذي يشير إلى أن المستخدم قد عدّل حقل الإدخال. فيما يلي مثال يستخدم حدث TextChanged لتتبع التغييرات في حقل إدخال:

رقم
النوع
الاسم
الدور
1
مربع نص
txtInput
حقل الإدخال
2
Label
lblControl
يعرض النص من 1 في الوقت الفعلي
3
زر
cmdClear
لمسح الحقول 1 و 2
4
زر
cmdExit
للخروج من التطبيق

الرمز ذو الصلة بهذا التطبيق هو معالجات الأحداث:


  ' click on btn quit
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' click on the Quit button - exit the application
        Application.Exit()
    End Sub
 
    ' field modification txtSaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' the content of TextBox has changed - copy it to Label lblControle
        lblControle.Text = txtSaisie.Text
    End Sub
 
    ' click on btn delete
    Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdEffacer.Click
        ' delete the contents of the input box
        txtSaisie.Text = ""
    End Sub
 
    ' a key has been pressed
    Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' see which key has been pressed
        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(). يستخدم المثال التالي مربع نص متعدد الأسطر:

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

رقم
النوع
الاسم
الدور
1
مربع نص
txtMultiLines
حقل إدخال متعدد الأسطر
2
مربع نص
txtAdd
حقل إدخال أحادي السطر
3
زر
btnAdd
اطرح 2 من 1

لجعل مربع النص متعدد الأسطر، قم بتعيين خصائص عنصر التحكم التالية:

 
للسماح بوجود عدة أسطر من النص
ScrollBars=(None, Horizontal, Vertical, Both)
لتحديد ما إذا كان يجب أن يحتوي عنصر التحكم على أشرطة تمرير (أفقي، عمودي، كلاهما) أم لا (بلا)
AcceptReturn=(True، False)
إذا تم تعيينه على true، فسيؤدي مفتاح Enter إلى الانتقال إلى السطر التالي
AcceptTab=(صحيح، خطأ)
إذا تم تعيينه على "صحيح"، فسيؤدي مفتاح Tab إلى إدراج علامة جدولة في النص

الرمز ذو الصلة هو الذي يتعامل مع النقر على زر [Add] والرمز الذي يتعامل مع التغييرات في حقل الإدخال [txtAdd]:


    ' évt btnAjouter_Click
    Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' add the content of txtAjout to that of txtMultilignes
        txtMultilignes.Text &= txtAjout.Text
        txtAjout.Text = ""
    End Sub
 
    ' evt txtAjout_TextChanged
    Private Sub txtAjout_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtAjout.TextChanged
        ' set the state of the Add button
        btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
    End Sub

5.4.3. قوائم منسدلة ComboBox

مكون ComboBox هو قائمة منسدلة مقترنة بحقل إدخال: حيث يمكن للمستخدم إما اختيار عنصر من (2) أو كتابة نص في (1). هناك ثلاثة أنواع من مكونات ComboBox يتم تحديدها بواسطة الخاصية Style:

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

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

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

العناصر الموجودة في ComboBox متاحة في الخاصية Items:

Public ReadOnly Property Items As ComboBox.ObjectCollection

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

 
عدد العناصر في ComboBox
C.Items(i)
العنصر i في ComboBox
C.Add(object o)
يضيف الكائن o كآخر عنصر في القائمة المنسدلة
C.AddRange(مجموعة من الكائنات)
يضيف مصفوفة من الكائنات إلى نهاية القائمة المنسدلة
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 موروثة من فئة الكائنات، والتي ترجع سلسلة "تمثل" الكائن. العنصر المحدد في مربع القائمة المنسدلة C هو C.SelectedItem أو C.Items(C.SelectedIndex)، حيث C.SelectedIndex هو مؤشر العنصر المحدد، بدءًا من الصفر للعنصر الأول.

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

Image

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


  Public Sub New()
    ' création formulaire
    InitializeComponent()
    ' remplissage combo
    cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
        ' nous sélectionnons le 1er élément de la liste
    cmbNombres.SelectedIndex = 0
  End Sub

نقوم بمعالجة حدث SelectedIndexChanged الخاص بقائمة الاختيار، والذي يشير إلى أنه تم تحديد عنصر جديد:


    Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmbNombres.SelectedIndexChanged
        ' the selected item has changed - it is displayed
        MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

5.4.4. مكون ListBox

نقترح إنشاء الواجهة التالية:

مكونات هذه النافذة هي كما يلي:

رقم
النوع
الاسم
الدور/الخصائص
0
نموذج
نموذج1
نموذج - BorderStyle=FixedSingle
1
مربع نص
txtInput
حقل الإدخال
2
زر
btnAdd
زر لإضافة محتويات حقل الإدخال 1 إلى القائمة 3
3
ListBox
listBox1
القائمة 1
4
مربع القائمة
listBox2
القائمة 2
5
زر
btn1TO2
ينقل العناصر المحددة من القائمة 1 إلى القائمة 2
6
زر
cmd2T0
يقوم بالعكس
7
زر
btnClear1
يمسح القائمة 1
8
زر
btnClear2
مسح القائمة 2
  • يقوم المستخدم بكتابة النص في الحقل 1. ثم يضيفه إلى القائمة 1 باستخدام زر "إضافة" (2). بعد ذلك يتم مسح حقل الإدخال (1)، ويمكن للمستخدم إضافة عنصر جديد.
  • يمكنه نقل العناصر من قائمة إلى أخرى عن طريق تحديد العنصر المراد نقله في إحدى القوائم واختيار زر النقل المناسب 5 أو 6. يتم إضافة العنصر المنقول إلى نهاية قائمة الوجهة وإزالته من قائمة المصدر.
  • يمكنه النقر مرتين على عنصر في القائمة 1. ثم يتم نقل هذا العنصر إلى مربع الإدخال للتحرير وإزالته من القائمة 1.

يتم تمكين الأزرار أو تعطيلها وفقًا للقواعد التالية:

  • يتم تمكين زر "إضافة" فقط إذا كان حقل الإدخال يحتوي على نص غير فارغ
  • يتم تمكين الزر 5 لنقل العنصر من القائمة 1 إلى القائمة 2 فقط إذا تم تحديد عنصر في القائمة 1
  • لا يتم تفعيل الزر 6 الخاص بالنقل من القائمة 2 إلى القائمة 1 إلا إذا تم تحديد عنصر في القائمة 2
  • لا يتم تمكين الزرين 7 و 8 لمسح القائمتين 1 و 2 إلا إذا كانت القائمة المراد مسحها تحتوي على عناصر.

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


    Public Sub New()
        ' initial form creation
        InitializeComponent()
        ' additional initializations
        ' a number of buttons are disabled
        btnAjouter.Enabled = False
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
        btnEffacer1.Enabled = False
        btnEffacer2.Enabled = False
    End Sub

يتم التحكم في حالة زر "إضافة" من خلال محتوى حقل إدخال النص. يتيح لنا حدث TextChanged تتبع التغييرات التي تطرأ على هذا المحتوى:


    ' change in field txtsaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' the content of txtSaisie has changed
        ' the Add button is only lit if the entry is non-empty
        btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
    End Sub

تعتمد حالة أزرار النقل على ما إذا كان قد تم تحديد عنصر في القائمة التي تتحكم فيها أم لا:


    ' chgt selected item without listbox1
    Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox1.SelectedIndexChanged
 
        ' an item has been selected
        ' switch on the 1 to 2 transfer button
        btn1TO2.Enabled = True
    End Sub
 
    ' chgt selected item without listbox2
    Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox2.SelectedIndexChanged
        ' an item has been selected
        ' switch on the 2 to 1 transfer button
        btn2TO1.Enabled = True
    End Sub

الرمز المرتبط بالنقر فوق الزر "إضافة" هو كما يلي:


    ' click on btn Add
    Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' add a new element to list 1
        listBox1.Items.Add(txtSaisie.Text.Trim())
        ' raz de la saisie
        txtSaisie.Text = ""
        ' List 1 is not empty
        btnEffacer1.Enabled = True
        ' return focus to input box
        txtSaisie.Focus()
    End Sub

لاحظ طريقة Focus، التي تسمح لك بتعيين "التركيز" على عنصر تحكم في النموذج. الكود المرتبط بالنقر على أزرار Clear:


    ' click on btn Delete1
    Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer1.Click
        ' delete list 1
        listBox1.Items.Clear()
        btnEffacer1.Enabled = False
    End Sub
 
    ' click on btn delete2
    Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' delete list 2
        listBox2.Items.Clear()
        btnEffacer2.Enabled = False
    End Sub

الكود لنقل العناصر المحددة من قائمة إلى أخرى:


    ' click on btn btn1to2
    Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn1TO2.Click
        ' transfer the item selected in List 1 to List 2
        transfert(listBox1, listBox2)
        ' delete buttons
        btnEffacer2.Enabled = True
        btnEffacer1.Enabled = listBox1.Items.Count <> 0
        ' transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub
 
    ' click on btn btn2to1
    Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn2TO1.Click
        ' transfer item selected in List 2 to List 1
        transfert(listBox2, listBox1)
        ' delete buttons
        btnEffacer1.Enabled = True
        btnEffacer2.Enabled = listBox2.Items.Count <> 0
        ' transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' transfer
    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
        ' transfer selected item from list 1 to list l2
        ' a selected item?
        If l1.SelectedIndex = -1 Then Return
        ' addition to l2
        l2.Items.Add(l1.SelectedItem)
        ' deletion in 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:


        ' un élément sélectionné ?
        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
زر الاختيار
زر الاختيار 1
زر الاختيار 2
زر الاختيار 3
3 أزرار اختيار
2
مربع اختيار
مربع الاختيار 1
مربع الاختيار 2
مربع الاختيار 3
3 خانات اختيار
3
مربع قائمة
lstValues
قائمة

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


  ' poster
    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
        ' displays radio button or checkbox status
        ' is this a checkbox?
        If (TypeOf (sender) Is CheckBox) Then
            Dim chk As CheckBox = CType(sender, CheckBox)
            lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
        End If
        ' is it a radiobutton?
        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 (sender) Is CheckBox بالتحقق مما إذا كان كائن المرسل من نوع CheckBox. وهذا بدوره يتيح لنا تحويله إلى النوع الدقيق للمرسل. تعرض هذه الطريقة اسم المكون الذي أطلق الحدث وقيمة خاصية Checked الخاصة به في قائمة lstValeurs. أثناء وقت التشغيل، نلاحظ أن النقر فوق زر الاختيار يؤدي إلى تشغيل حدثين CheckChanged: أحدهما على الزر الذي كان محددًا سابقًا، والذي يصبح "غير محدد"، والآخر على الزر الجديد، الذي يصبح "محددًا".

5.4.6. عناصر التحكم ScrollBar

هناك عدة أنواع من أشرطة التمرير: شريط التمرير الأفقي (hScrollBar
وشريط التمرير الرأسي (vScrollBar)، وعنصر التحكم الرقمي لأعلى/لأسفل (NumericUpDown).

لنقم بإنشاء التطبيق التالي:

رقم
النوع
الاسم
الدور
1
hScrollBar
hScrollBar1
شريط تمرير أفقي
2
hScrollBar
hScrollBar2
شريط تمرير أفقي يتبع التغييرات في شريط التمرير 1
3
TextBox
txtValue
يعرض قيمة شريط التمرير الأفقي
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 لعنصر التحكم على true. يتم تعيين قيمة الزيادة بواسطة خاصية Increment، والتي تكون قيمتها الافتراضية 1. الحدث الرئيسي لمكون NumericUpDown هو الذي يشير إلى تغيير في القيمة: حدث ValueChanged. فيما يلي الكود ذو الصلة بتطبيقنا:

يتم تنسيق النموذج أثناء إنشائه:


    ' manufacturer
    Public Sub New()
        ' initial form creation
        InitializeComponent()
        ' drive 2 is given the same characteristics as drive 1
        hScrollBar2.Minimum = hScrollBar1.Value
        hScrollBar2.Minimum = hScrollBar1.Minimum
        hScrollBar2.Maximum = hScrollBar1.Maximum
        hScrollBar2.LargeChange = hScrollBar1.LargeChange
        hScrollBar2.SmallChange = hScrollBar1.SmallChange
        ' ditto for the incrementer
        incrémenteur.Minimum = hScrollBar1.Value
        incrémenteur.Minimum = hScrollBar1.Minimum
        incrémenteur.Maximum = hScrollBar1.Maximum
        incrémenteur.Increment = hScrollBar1.SmallChange
        ' give TextBox the value of drive 1
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

المعالج الذي يتتبع التغييرات في قيمة شريط التمرير 1:


    ' hscrollbar1 drive management
    Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar1.Scroll
        ' value change on drive 1
        ' its value is passed on to drive 2 and the TxtValeur textbox
        hScrollBar2.Value = hScrollBar1.Value
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

المعالج الذي يتتبع التغييرات في قيمة شريط التمرير 2:


    ' hscrollbar2 drive management
    Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar2.Scroll
        ' inhibits all changes to drive 2
        ' forcing it to keep the value of drive 1
        e.NewValue = hScrollBar1.Value
    End Sub

المعالج الذي يتتبع التغييرات في شريط التمرير:


    ' incremental management
    Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles incrémenteur.ValueChanged
        ' set the value of controller 2
        hScrollBar2.Value = CType(incrémenteur.Value, Integer)
    End Sub

5.5. أحداث الماوس

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

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

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

رقم
النوع
الاسم
الدور
1
التسمية
lblPosition
لعرض موضع الماوس في النموذج 1 أو القائمة 2 أو الزر 3
2
ListBox
lstEvts
لعرض أحداث الماوس بخلاف MouseMove
3
زر
btnClear
لمسح محتويات 2

فيما يلي معالجات الأحداث. لتتبع حركات الماوس عبر عناصر التحكم الثلاثة، نكتب معالجًا واحدًا:


    ' évt 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
        ' mvt mouse - displays its (X,Y) coordinates
        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 لتلك القائمة:


    ' evt 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
 
    ' evt 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
    
' poster
    Private Sub affiche(ByVal message As String)
        ' the message is displayed at the top of the event list
        lstEvts.Items.Insert(0, message)
    End Sub

Image

للتعامل مع النقرات على النموذج، نتعامل مع أحداث MouseDown و MouseUp:


    ' évt 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
 
    ' évt 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

وأخيرًا، رمز معالج حدث النقر على زر "حذف":


    ' évt btnEffacer_Click
    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' clears the event list
        lstEvts.Items.Clear()
    End Sub

5.6. إنشاء نافذة مع قائمة

الآن دعونا نرى كيفية إنشاء نافذة مع قائمة. سنقوم بإنشاء النافذة التالية:

العنصر 1 هو مربع نص للقراءة فقط (ReadOnly=true) باسم txtStatut. شجرة القائمة هي كما يلي:

خيارات القائمة هي عناصر تحكم تمامًا مثل المكونات المرئية الأخرى ولها خصائص وأحداث. على سبيل المثال، جدول الخصائص لخيار القائمة A1:

Image

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

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

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

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

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

Image

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

Image

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

Image

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

Image

إذا قمت بتشغيل حدث النقر أعلاه، يقوم VS.NET تلقائيًا بإنشاء الإجراء التالي:


Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
    End Sub

يمكننا المضي قدماً بهذه الطريقة لجميع خيارات القائمة. هنا، يمكن استخدام الإجراء نفسه لجميع الخيارات. لذا، نعيد تسمية الإجراء السابق *إلى display* ونعلن الأحداث التي يتعامل معها:


    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
        ' displays the name of the selected submenu in the TextBox
        txtStatut.Text = (CType(sender, MenuItem)).Text
    End Sub

في هذه الطريقة، نعرض ببساطة الخاصية Text لخيار القائمة الذي أطلق الحدث. مصدر إرسال الحدث هو من النوع Object. خيارات القائمة هي من النوع MenuItem، لذا يجب علينا هنا تحويل النوع من Object إلى MenuItem. قم بتشغيل التطبيق واختر الخيار A1 لترى الرسالة التالية:

Image

الرمز ذو الصلة بهذا التطبيق، بصرف النظر عن طريقة Display، هو الرمز الخاص بإنشاء القائمة في منشئ النموذج (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
مربع نص متعدد الأسطر
txtText
نص مكتوب من قبل المستخدم أو تم تحميله من ملف
2
زر
btnSave
يحفظ النص من 1 إلى ملف نصي
3
زر
btnLoad
يتيح لك تحميل محتويات ملف نصي في 1
4
زر
btnClear
يمسح محتويات 1

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

Image

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

Image

رمز زر "Clear" بسيط:


    Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' clear the input box
        txtTexte.Text = ""
    End Sub

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

Image

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

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

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

1
القائمة المنسدلة التي تم إنشاؤها من خاصية Filter. يتم تحديد نوع الملف الافتراضي بواسطة FilterIndex
2
الدليل الحالي، الذي يتم تعيينه بواسطة InitialDirectory إذا تم تحديد هذه الخاصية
3
اسم الملف الذي تم تحديده أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName
4
أزرار "حفظ"/"إلغاء". إذا تم استخدام زر "حفظ"، فإن الدالة ShowDialog تُرجع النتيجة DialogResult.OK

يمكن كتابة إجراء الحفظ على النحو التالي:


    Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnSauvegarder.Click
        ' save the input box in a text file
        ' set the savefileDialog1 dialog box
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        saveFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve the result
        If saveFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim nomFichier As String = saveFileDialog1.FileName
            Dim fichier As StreamWriter = Nothing
            Try
                ' open the file for writing
                fichier = New StreamWriter(nomFichier)
                ' we write the text inside
                fichier.Write(txtTexte.Text)
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' close the file
                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 (*.*)|*.*"

لاحظ صيغة الفلتر: filter1|filter2|..|filteren، حيث filteri = نص|نوع الملف. هنا، يمكن للمستخدم الاختيار بين ملفات *.txt و *.*.

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

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

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

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

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

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

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

1
قائمة منسدلة مبنية من خاصية Filter. يتم تحديد نوع الملف الافتراضي بواسطة FilterIndex
2
المجلد الحالي، الذي يتم تعيينه بواسطة InitialDirectory إذا تم تحديد هذه الخاصية
3
اسم الملف الذي تم تحديده أو كتابته مباشرةً من قبل المستخدم. سيكون متاحًا في الخاصية FileName
4
أزرار "فتح"/"إلغاء". إذا تم استخدام زر "فتح"، فإن الدالة ShowDialog تُرجع النتيجة DialogResult.OK

يمكن كتابة الإجراء المفتوح على النحو التالي:


Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCharger.Click
        ' load a text file into the input box
        ' set the openfileDialog1 dialog box
        openFileDialog1.InitialDirectory = Application.ExecutablePath
        openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        openFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve the result
        If openFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim nomFichier As String = openFileDialog1.FileName
            Dim fichier As StreamReader = Nothing
            Try
                ' open the file in read mode
                fichier = New StreamReader(nomFichier)
                ' read the entire file and put it in the TextBox
                txtTexte.Text = fichier.ReadToEnd()
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' close the file
                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 فقط إذا استخدم المستخدم الزر "فتح" للخروج من مربع الحوار.

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

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

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

رقم
النوع
الاسم
الدور
6
زر
btnColor
لتعيين لون نص مربع النص
7
زر
btnFont
لتعيين خط TextBox

نضع عنصر تحكم ColorDialog وعنصر تحكم FontDialog في النموذج:

Image

تحتوي فئتا FontDialog و ColorDialog على طريقة ShowDialog مشابهة لطريقة ShowDialog في فئتي OpenFileDialog و SaveFileDialog. تتيح لك طريقة ShowDialog في فئة ColorDialog اختيار لون:

Image

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

Image

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


    Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCouleur.Click
        ' choice of text color
        If colorDialog1.ShowDialog() = DialogResult.OK Then
            ' change the forecolor property of 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
        ' font selection
        If fontDialog1.ShowDialog() = DialogResult.OK Then
            ' change the font property of TextBox
            txtTexte.Font = fontDialog1.Font
        End If
    End Sub

5.7.3. المؤقت

هنا، نقترح كتابة التطبيق التالي:

رقم
النوع
الاسم
الدور
1
TextBox، ReadOnly=true
txtChrono
يعرض مؤقتًا
2
زر
btnStopStart
زر إيقاف/بدء لساعة التوقيت
3
المؤقت
timer1
مكون يقوم بتشغيل حدث كل ثانية

المؤقت قيد التشغيل:

Image

توقف المؤقت:

Image

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

Image

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

Image

من بين خصائصها، سنأخذ في الاعتبار ما يلي فقط:

Interval
عدد المللي ثانية التي يتم بعدها تشغيل حدث التكتكة.
Tick
الحدث الذي يتم تشغيله في نهاية الفاصل الزمني بالمللي ثانية
ممكّن
يضبط المؤقت على نشط (صحيح) أو غير نشط (خطأ)

في مثالنا، يُسمى المؤقت timer1 ويتم تعيين timer1.Interval على 1000 مللي ثانية (1 ثانية). وبالتالي، سيحدث حدث Tick كل ثانية. يتم التعامل مع النقر على زر Start/Stop من خلال الإجراء التالي:


    Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' off or on?
        If btnArretMarche.Text = "Marche" Then
            ' we note the start time
            début = DateTime.Now
            ' we display it
            txtChrono.Text = "00:00:00"
            ' start timer
            timer1.Enabled = True
            ' change the button label
            btnArretMarche.Text = "Arrêt"
            ' end
            Return
        End If        '
        If btnArretMarche.Text = "Arrêt" Then
            ' timer off
            timer1.Enabled = False
            ' change the button label
            btnArretMarche.Text = "Marche"
            ' end
            Return
        End If
    End Sub
 
    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' a second has passed
        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

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

  • إذا كانت التسمية "Start"، نقوم بتخزين وقت البدء في متغير عام لكائن النموذج، ويتم تشغيل المؤقت (Enabled=true)، وتتغير تسمية الزر إلى "Stop".
  • إذا كانت التسمية "إيقاف"، يتم إيقاف المؤقت (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
 
  ' instance variables
  Private début As DateTime

السمة start المذكورة أعلاه معروفة في جميع أساليب الفئة. لا يزال يتعين علينا معالجة حدث Tick في كائن timer1، وهو حدث يحدث كل ثانية:


    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' a second has passed
        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، الذي تناولناه مرتين من قبل. نضيف الآن واجهة مستخدم رسومية إليه:

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

رقم
النوع
الاسم
الدور
1
زر الاختيار
rdYes
محدد إذا كان متزوجًا
2
زر الاختيار
rdNo
تم تحديدها في حالة عدم الزواج
3
رقم لأعلى ولأسفل
عدد
عدد أطفال المكلف
الحد الأدنى=0، الحد الأقصى=20، الزيادة=1
4
مربع نص
txtSalary
الراتب السنوي للمكلف بالضريبة بالفرنك السويسري
5
مربع نص
txtTaxes
مبلغ الضريبة المستحقة
قراءة فقط=صحيح
6
زر
btnCalculate
يبدأ حساب الضريبة
7
زر
btnClear
يعيد تعيين النموذج إلى حالته الأولية عند التحميل
8
زر
btnExit
للخروج من التطبيق

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

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

Image

يظهر البرنامج أدناه. ويستخدم فئة Impot التي تم إنشاؤها في الفصل الخاص بالفئات. لم يتم هنا إعادة إنتاج بعض الأكواد التي تم إنشاؤها تلقائيًا بواسطة VS.NET.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
 
' form class
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
 
  ' data tables required for tax calculation
  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}
  ' tax object
  Private objImpôt As impot = Nothing
 
 
  Public Sub New()
    InitializeComponent()
    ' form initialization
    btnEffacer_Click(Nothing, Nothing)
    btnCalculer.Enabled = False
    ' tax object creation
    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)
      ' inhibit the salary entry field
      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
    ' raz du formulaire
    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
    ' calculate button status
    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
    ' end application
    Application.Exit()
  End Sub 'btnQuitter_Click
 

  Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnCalculer.Click
    ' is the salary correct?
    Dim intSalaire As Integer = 0
    Try
      ' salary recovery
      intSalaire = Integer.Parse(txtSalaire.Text)
      ' it must be >=0
      If intSalaire < 0 Then
        Throw New Exception("")
      End If
    Catch ex As Exception
      ' error msg
      MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' focus on wrong field
      txtSalaire.Focus()
      ' select text for input field
      txtSalaire.SelectAll()
      ' back to visual interface
      Return
    End Try 'try-catch
    ' salary is correct - tax is calculated
    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

لإضافة مرجع (تجميع)، نضغط بزر الفأرة الأيمن على مجموعة المراجع أعلاه، ونختار خيار [إضافة مرجع]، ونحدد التجميع [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 في المشروع. للقيام بذلك، في نافذة خصائص المشروع، انقر بزر الماوس الأيمن على المشروع، وحدد الخيار [إضافة/إضافة عنصر موجود]، وحدد ملف impots.vb.