Skip to content

7. رابط‌های کاربری گرافیکی با C# و VS.NET

7.1. مبانی رابط‌های کاربری گرافیکی

7.1.1. یک پروژهٔ اول

بیایید اولین پروژهٔ «Windows Application» خود را ایجاد کنیم:

  • [1]: ایجاد یک پروژه جدید
  • [2]: از نوع «Windows Application»
  • [3]: نام پروژه در این مرحله اهمیتی ندارد
  • [4]: پروژه ایجاد شد
  • [5]: ذخیره کردن راه‌حل فعلی
  • [6]: نام پروژه
  • [7]: پوشهٔ راه‌حل
  • [8]: نام راه‌حل
  • [9]: یک پوشه برای راه‌حل [Chap5] ایجاد خواهد شد. پروژه‌های درون آن در زیرپوشه‌ها قرار خواهند گرفت.
  • [10]: پروژه [01] در داخل راه‌حل [Chap5]:
  • [Program.cs] کلاس اصلی پروژه است
  • [Form1.cs] فایل منبعی است که رفتار پنجره [11] را مدیریت می‌کند
  • [Form1.Designer.cs] فایل منبعی است که اطلاعات مربوط به اجزای پنجره [11] را در خود جای می‌دهد
  • [11]: فایل [Form1.cs] در حالت «طراحی»
  • [12]: برنامهٔ تولیدشده را می‌توان با فشردن (Ctrl-F5) اجرا کرد. پنجرهٔ [Form1] ظاهر می‌شود. می‌توان آن را جابه‌جا کرد، اندازهٔ آن را تغییر داد و بست. بنابراین ما عناصر پایه‌ای یک پنجرهٔ گرافیکی را داریم.

کلاس اصلی [Program.cs] به شرح زیر است:


using System;
using System.Windows.Forms;

namespace Chap5 {
    static class Program {
        /// <summary>
        /// نقطه ورود اصلی برنامه.
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
  • خط ۲: برنامه‌هایی که دارای فرم هستند از فضای نام System.Windows.Forms استفاده می‌کنند.
  • خط ۴: فضای نام اصلی به Chap5 تغییر نام داده شده است.
  • خط ۱۰: وقتی پروژه اجرا می‌شود (Ctrl-F5)، متد [Main] اجرا می‌شود.
  • خطوط ۱۱–۱۳: کلاس Application متعلق به فضای نام System.Windows.Forms است. این کلاس شامل متدهای استاتیک برای راه‌اندازی و متوقف‌سازی برنامه‌های گرافیکی ویندوز است.
  • خط ۱۱: اختیاری – امکان اعمال سبک‌های بصری مختلف را به کنترل‌های قرار گرفته روی یک فرم فراهم می‌کند
  • خط ۱۲: اختیاری – موتور رندرینگ متن را برای کنترل‌ها تنظیم می‌کند: GDI+ (trueGDI (false)
  • خط ۱۳: تنها خط ضروری در متد [Main]: کلاس [Form1] را که کلاس فرم است، ایجاد نمونه می‌کند و به آن دستور اجرای برنامه را می‌دهد.

فایل منبع [Form1.cs] به شرح زیر است:


using System;
using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }
    }
}
  • خط ۵: کلاس Form1 از کلاس [System.Windows.Forms.Form] که کلاس والد همه پنجره‌ها است، ارث می‌برد. کلمه کلیدی «partial» نشان می‌دهد که کلاس جزئی است و می‌تواند توسط فایل‌های منبع دیگر تکمیل شود. در اینجا نیز همین‌طور است، جایی که کلاس Form1 در دو فایل تقسیم شده است:
  • [Form1.cs]: که شامل رفتار فرم، به‌ویژه رویدادپردازهای آن است
  • [Form1.Designer.cs]: که شامل اجزای فرم و ویژگی‌های آن‌هاست. این فایل هر بار که کاربر پنجره را در حالت [conception] تغییر می‌دهد، مجدداً تولید می‌شود.
  • خطوط ۶–۸: سازنده کلاس Form1
  • خط ۷: متد InitializeComponent را فراخوانی می‌کند. می‌توان دید که این متد در [Form1.cs] وجود ندارد. این متد در [Form1.Designer.cs] یافت می‌شود.

فایل منبع [Form1.Designer.cs] به شرح زیر است:


namespace Chap5 {
    partial class Form1 {
        /// <summary>
        /// متغیر طراح مورد نیاز.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// هرگونه منبعی را که در حال استفاده است پاکسازی کنید.
        /// </summary>
        /// <param name="disposing">true اگر منابع مدیریت‌شده باید آزاد شوند؛ در غیر این صورت، false.</param>
        protected override void Dispose(bool disposing) {
            if (disposing && (components != null)) {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #کد تولیدشده توسط طراح فرم ویندوز

        /// <summary>
        /// متد مورد نیاز برای پشتیبانی Designer – تغییر ندهید
        /// محتویات این متد را با ویرایشگر کد استفاده کنید.
        /// </summary>
        private void InitializeComponent() {
            this.SuspendLayout();
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(196, 98);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }

         #endregion

    }
}
  • خط ۲: این هنوز کلاس Form1 است. توجه داشته باشید که دیگر لازم نیست تکرار شود که از کلاس Form ارث می‌برد.
  • خطوط ۲۵–۳۷: متد InitializeComponent که توسط سازنده کلاس [Form1] فراخوانی می‌شود. این متد تمام اجزای فرم را ایجاد و مقداردهی اولیه می‌کند. هر بار که فرم در حالت [conception] تغییر داده می‌شود، مجدداً تولید می‌گردد. یک بخش به نام région برای جدا کردن آن ایجاد می‌شود (خطوط ۱۹–۳۹). توسعه‌دهنده نباید هیچ کدی به این ناحیه اضافه کند: این کد در بازتولید بعدی بازنویسی خواهد شد.

در ابتدا، ساده‌تر است که کد موجود در [Form1.Designer.cs] را نادیده بگیرید. این کد به‌طور خودکار تولید می‌شود و نمایانگر پیاده‌سازی C# انتخاب‌هایی است که توسعه‌دهنده در حالت [conception] انجام می‌دهد. بیایید به اولین مثال نگاه کنیم:

  • [1]: با دوبار کلیک روی فایل [Form1.cs]، حالت [conception] را انتخاب کنید
  • [2]: روی فرم کلیک راست کرده و [Properties] را انتخاب کنید
  • [3]: پنجرهٔ ویژگی‌ها برای [Form1]
  • [4]: ویژگی [Text] نمایانگر عنوان پنجره است
  • [5]: تغییر در ویژگی [Text] در حالت [conception] و همچنین در کد منبع [Form1.Designer.cs] منعکس می‌شود:

        private void InitializeComponent() {
            this.SuspendLayout();
...
            this.Text = "Mon 1er formulaire";
...
}

7.1.2. یک پروژه دوم

7.1.2.1. فرم

ما یک پروژه جدید به نام 02 را آغاز می‌کنیم. برای این کار، رویه‌ای را که قبلاً برای ایجاد یک پروژه تشریح شده بود، دنبال می‌کنیم. پنجره‌ای که باید ایجاد شود به شرح زیر است:

اجزای فرم به شرح زیر است:

خیر.
نام
نوع
نقش
۱
labelSaisie
برچسب
یک توضیح
2
textBoxSaisie
TextBox
یک فیلد ورودی
3
buttonAfficher
دکمه
برای نمایش محتویات فیلد ورودی در یک کادر محاوره‌ای textBoxSaisie

برای ایجاد این پنجره می‌توانید به روش زیر عمل کنید:

  • [1]: روی فرم خارج از هر کامپوننت کلیک راست کرده و گزینه [Properties] را انتخاب کنید
  • [2]: برگهٔ ویژگی‌های پنجره در گوشهٔ پایین‌راست ویژوال استودیو ظاهر می‌شود

از جمله ویژگی‌های قابل توجه فرم عبارتند از:

BackColor
برای تنظیم رنگ پس‌زمینه پنجره
ForeColor
برای تنظیم رنگ گرافیک یا متن روی پنجره
Menu
برای مرتبط کردن یک منو با پنجره
Text
برای دادن عنوان به پنجره
FormBorderStyle
برای تعیین نوع پنجره
Font
برای تنظیم قلم متن در پنجره
Name
برای تعیین نام پنجره

در اینجا، ما ویژگی‌های Text و Name را تنظیم می‌کنیم:

Text
میدان‌های ورودی و دکمه‌ها - ۱
Name
frmSaisiesBoutons
  • [1]: ابزارک [Common Controls] را از میان ابزارک‌های ارائه‌شده توسط ویژوال استودیو انتخاب کنید
  • [2, 3, 4]: به‌ترتیب روی کامپوننت‌های [Label]، [Button] و [TextBox] دوبار کلیک کنید
  • [5]: سه کامپوننت روی فرم قرار دارند

برای تراز و تغییر اندازه صحیح کامپوننت‌ها، می‌توانید از گزینه‌های نوار ابزار استفاده کنید:

 
  
   

قالب‌بندی به شرح زیر انجام می‌شود:

  1. اجزای مختلف را که می‌خواهید با هم قالب‌بندی شوند، انتخاب کنید (کلید Ctrl را نگه دارید و همزمان روی اجزا کلیک کنید)
  2. نوع قالب‌بندی مورد نظر را انتخاب کنید:
  • (ادامه)
    • گزینه‌های Align به شما امکان می‌دهند اجزا را در بالا، پایین، چپ یا راست، یا مرکز تراز کنید
    • گزینه‌های «یک‌اندازه کردن» تضمین می‌کنند که اجزا از نظر ارتفاع یا عرض یکسان باشند.
    • گزینه «فاصله افقی» به شما امکان می‌دهد اجزا را به صورت افقی با فاصله مساوی بین آن‌ها تراز کنید. همین امر برای تراز عمودی با گزینه «فاصله عمودی» نیز صدق می‌کند.
    • گزینه Center به شما امکان می‌دهد یک مؤلفه را به صورت افقی (Horizontally) یا عمودی (Vertically) در پنجره مرکزدهی کنید

پس از قرار دادن کامپوننت‌ها، ویژگی‌های آن‌ها را تنظیم می‌کنیم. برای این کار، روی کامپوننت کلیک راست کرده و گزینه Properties را انتخاب کنید:

  • [1]: مؤلفه را برای باز کردن پنجرهٔ ویژگی‌های آن انتخاب کنید. در این پنجره، ویژگی‌های زیر را اصلاح کنید: name: labelSaisie, text: Saisie
  • [2]: همین کار را انجام دهید: name: textBoxSaisie, text: خالی بگذارید
  • [3]: نام: buttonAfficher، متن: Afficher
  • [4]: خود پنجره: name: frmSaisiesBoutons, text: فیلدهای ورودی و دکمه‌ها - ۱
  • [5]: پروژه را اجرا کنید (Ctrl-F5) تا پیش‌نمایش اولیه‌ای از پنجره در عمل مشاهده شود.

آنچه در حالت [conception] انجام شد، به کد برای [Form1.Designer.cs] تبدیل شده است:


namespace Chap5 {
    partial class frmSaisiesBoutons {
...
        private System.ComponentModel.IContainer components = null;
...
        private void InitializeComponent() {
            this.labelSaisie = new System.Windows.Forms.Label();
            this.buttonAfficher = new System.Windows.Forms.Button();
            this.textBoxSaisie = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // labelSaisie
            // 
            this.labelSaisie.AutoSize = true;
            this.labelSaisie.Location = new System.Drawing.Point(12, 19);
            this.labelSaisie.Name = "labelSaisie";
            this.labelSaisie.Size = new System.Drawing.Size(35, 13);
            this.labelSaisie.TabIndex = 0;
            this.labelSaisie.Text = "Saisie";
            // 
            // buttonAfficher
            // 
            this.buttonAfficher.Location = new System.Drawing.Point(80, 49);
            this.buttonAfficher.Name = "buttonAfficher";
            this.buttonAfficher.Size = new System.Drawing.Size(75, 23);
            this.buttonAfficher.TabIndex = 1;
            this.buttonAfficher.Text = "Afficher";
            this.buttonAfficher.UseVisualStyleBackColor = true;
            this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
            // 
            // textBoxSaisie
            // 
            this.textBoxSaisie.Location = new System.Drawing.Point(80, 19);
            this.textBoxSaisie.Name = "textBoxSaisie";
            this.textBoxSaisie.Size = new System.Drawing.Size(100, 20);
            this.textBoxSaisie.TabIndex = 2;
            // 
            // frmSaisiesBoutons
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 118);
            this.Controls.Add(this.textBoxSaisie);
            this.Controls.Add(this.buttonAfficher);
            this.Controls.Add(this.labelSaisie);
            this.Name = "frmSaisiesBoutons";
            this.Text = "Saisies et boutons - 1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        private System.Windows.Forms.Label labelSaisie;
        private System.Windows.Forms.Button buttonAfficher;
        private System.Windows.Forms.TextBox textBoxSaisie;

    }
}
  • خطوط ۵۳–۵۵: این سه کامپوننت منجر به ایجاد سه فیلد خصوصی در کلاس [Form1] شده‌اند. توجه داشته باشید که نام این فیلدها، نام‌هایی است که در حالت [conception] به کامپوننت‌ها داده شده است. این موضوع در مورد فرم در خط ۲، که خود کلاس است، نیز صدق می‌کند.
  • خطوط ۷–۹: سه شیء از نوع [Label]، [TextBox] و [Button] ایجاد می‌شوند. مدیریت مؤلفه‌های بصری از طریق این اشیاء انجام می‌شود.
  • خطوط ۱۴–۱۹: پیکربندی برچسب labelSaisie
  • خطوط ۲۳–۲۹: پیکربندی دکمه buttonAfficher
  • خطوط ۳۳–۳۶: پیکربندی فیلد ورودی textBoxSaisie
  • خطوط ۴۰–۴۷: پیکربندی فرم frmSaisiesBoutons. توجه کنید که در خطوط ۴۳–۴۵ چگونه اجزا به فرم اضافه می‌شوند.

این کد ساده است. بنابراین می‌توان بدون استفاده از حالت [conception]، فرم‌ها را با استفاده از کد ساخت. نمونه‌های متعددی از این مورد در مستندات Visual Studio برای MSDN ارائه شده است. تسلط بر این کد به شما امکان می‌دهد فرم‌ها را در زمان اجرا ایجاد کنید: برای مثال، ایجاد یک فرم به صورت پویا برای به‌روزرسانی یک جدول پایگاه داده، که در آن ساختار آن جدول تنها در زمان اجرا مشخص می‌شود.

اکنون باید رویه‌ای را برای رسیدگی به کلیک روی دکمه Afficher بنویسیم. دکمه را انتخاب کنید تا پنجرهٔ ویژگی‌های آن باز شود. این پنجره چندین برگه دارد:

  • [1]: فهرست ویژگی‌ها به ترتیب حروف الفبا
  • [2]: رویدادهای مرتبط با کنترل

ویژگی‌ها و رویدادهای یک کنترل را می‌توان بر اساس دسته‌بندی یا به ترتیب الفبا دسترسی پیدا کرد:

  • [3]: ویژگی‌ها یا رویدادها بر اساس دسته‌بندی
  • [4]: ویژگی‌ها یا رویدادها به ترتیب حروف الفبا

زبانه‌ی Events در حالت Catégories برای دکمه‌ی buttonAfficher به شرح زیر است:

  • [1]: ستون سمت چپ پنجره، رویدادهای ممکن برای دکمه را فهرست می‌کند. کلیک کردن روی یک دکمه با رویداد Click مطابقت دارد.
  • [2]: ستون سمت راست حاوی نام رویه‌ای است که هنگام وقوع رویداد مربوطه فراخوانی می‌شود.
  • [3]: اگر روی سلول مربوط به رویداد Click دوبار کلیک کنید، به طور خودکار به پنجره کد هدایت می‌شوید تا رویدادپردازشگر (event handler) را برای Click روی دکمه buttonAfficher بنویسید:

using System;
using System.Windows.Forms;

namespace Chap5 {
    public partial class frmSaisiesBoutons : Form {
        public frmSaisiesBoutons() {
            InitializeComponent();
        }

        private void buttonAfficher_Click(object sender, EventArgs e) {

        }
    }
}
  • خطوط ۱۰–۱۲: اسکلت رویدادپرداز Click برای دکمه‌ای به نام buttonAfficher. نکات زیر را توجه کنید:
    • متد مطابق الگوی nomDuComposant_NomEvénement نام‌گذاری شده است
    • این متد خصوصی است. دو پارامتر می‌گیرد:
    • sender: شیئی است که رویداد را فعال کرده است. اگر این رویه پس از کلیک روی دکمه buttonAfficher اجرا شود، sender برابر با buttonAfficher خواهد بود. ممکن است رویه buttonAfficher_Click از داخل رویه دیگری اجرا شود. در این صورت، آن رویه می‌تواند پارامتر اول را به شی sender دلخواه خود تنظیم کند.
    • EventArgs: یک شیء حاوی اطلاعات مربوط به رویداد. برای رویدادی از نوع Click، این شیء حاوی هیچ اطلاعاتی نیست. برای رویدادی مرتبط با حرکات ماوس، این شیء شامل مختصات (X،Y) ماوس خواهد بود.
    • ما در اینجا از هیچ‌یک از این پارامترها استفاده نخواهیم کرد.

نوشتن یک رویدادپرداز شامل تکمیل اسکلت کد نشان داده شده در بالا است. در اینجا، می‌خواهیم یک کادر محاوره‌ای را نمایش دهیم که حاوی محتویات فیلد textBoxSaisie باشد اگر خالی نباشد ([1])، و در غیر این صورت یک پیام خطا ([2]):

کد برای رسیدن به این هدف می‌تواند به شکل زیر باشد:


        private void buttonAfficher_Click(object sender, EventArgs e) {
            // متن وارد شده در TextBox textboxSaisie
            string texte = textBoxSaisie.Text.Trim();
            if (texte.Length != 0) {
                MessageBox.Show("Texte saisi= " + texte, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information);
            } else {
                MessageBox.Show("Saissez un texte...", "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

کلاس MessageBox برای نمایش پیام‌ها در یک پنجره استفاده می‌شود. در اینجا، ما از متد زیر، Show، استفاده کرده‌ایم:


public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon);

با

text
پیامی که باید نمایش داده شود
caption
عنوان پنجره
buttons
دکمه‌ها در پنجره
icon
آیکون نمایش داده شده در پنجره

پارامتر buttons می‌تواند مقادیری از ثابت‌های زیر (که با MessageBoxButtons همان‌طور که در خط ۷ نشان داده شده است) بگیرد:

constante
دکمه‌ها
   AbortRetryIgnore 
  OK 
    OKCancel 
    RetryCancel 
    YesNo 
    YesNoCancel 

پارامتر icon می‌تواند مقادیری از ثابت‌های زیر (که با MessageBoxIcon پیش‌وند شده‌اند، همان‌طور که در خط ۱۰ نشان داده شده) داشته باشد:

Asterisk
Error
همان متوقف
Exclamation
همان هشدار
Hand
Information
همانند Asterisk
None
Question
Stop
همان دست
Warning

 

متد Show یک متد ایستا است که نتیجه‌ای از نوع [System.Windows.Forms.DialogResult] را برمی‌گرداند، که یک شمارش‌پذیر است:

Image

برای اینکه بفهمیم کاربر کدام دکمه را برای بستن پنجره از نوع MessageBox کلیک کرده است، می‌نویسیم:

DialogResult res=MessageBox.Show(..);
if (res==DialogResult.Yes){ // او دکمه «بله» را فشار داد...}

7.1.2.2. کد مربوط به مدیریت رویداد

علاوه بر تابع buttonAfficher_Click که ما نوشته‌ایم، ویژوال استودیو خط زیر را در متد InitializeComponents از کلاس [Form1.Designer.cs] که اجزای فرم را ایجاد و مقداردهی اولیه می‌کند، تولید کرده است:


            this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);

Click یک رویداد از کلاس‌های Button و [1, 2, 3] است:

  • [5]: اعلان رویداد [Control.Click] [4]. بنابراین می‌توانیم ببینیم که رویداد Click مختص کلاس [Button] نیست. این رویداد به کلاس [Control]، کلاس والد کلاس [Button]، تعلق دارد.
    • EventHandler یک پروتوتایپ متد (یک الگو) است که به آن دِلیگیت گفته می‌شود. بعداً به این موضوع باز خواهیم گشت.
    • event یک کلمه کلیدی است که قابلیت‌های delegate و EventHandler را محدود می‌کند: یک شیء delegate ویژگی‌های بیشتری نسبت به یک شیء event دارد.

delegate EventHandler به شرح زیر تعریف شده است:

 

delegate EventHandler به یک قالب متد اشاره دارد:

  • که پارامتر اول آن از نوع Object است
  • با پارامتر دوم از نوع EventArgs
  • بدون بازگشت نتیجه

این مورد برای متدی است که کلیک روی دکمه buttonAfficher را که توسط ویژوال استودیو ایجاد شده، مدیریت می‌کند:


        private void buttonAfficher_Click(object sender, EventArgs e);

بنابراین، متد buttonAfficher_Click با پروتوتایپ تعریف‌شده توسط نوع EventHandler مطابقت دارد. برای ایجاد یک شیء از نوع EventHandler، به شرح زیر عمل کنید:

EventHandler evtHandler=new EventHandler(méthode correspondant au prototype  défini par le type EventHandler);

از آنجایی که متد buttonAfficher_Click با پروتوتیپ تعریف‌شده توسط نوع EventHandler مطابقت دارد، می‌توانیم بنویسیم:

EventHandler evtHandler=new EventHandler(buttonAfficher_Click);

یک متغیر از نوع delegate در واقع یک لیست از ارجاعات به متدهای نوع delegate است. برای افزودن یک متد جدید M به متغیر evtHandler فوق، از سینتکس زیر استفاده می‌شود:

evtHandler+=new EvtHandler(M);

نشانه‌گذاری += می‌تواند حتی زمانی که evtHandler یک لیست خالی باشد، استفاده شود.

بیایید به خط مربوط به [InitializeComponent] بازگردیم که یک رویدادپرداز به رویداد Click از شیء buttonAfficher اضافه می‌کند:


            this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);

این دستور یک متد از نوع EventHandler را به لیست متدهای فیلد buttonAfficher.Click اضافه می‌کند. این متدها هرگاه رویداد Click روی کامپوننت buttonAfficher تشخیص داده شود، فراخوانی خواهند شد. اغلب تنها یک مورد وجود دارد. این به عنوان «پردازشگر رویداد» شناخته می‌شود.

بیایید دوباره به امضای EventHandler نگاهی بیندازیم:


        private delegate void EventHandler(object sender, EventArgs e);

پارامتر دوم delegate یک شیء از نوع EventArgs یا یک کلاس مشتق است. نوع EventArgs بسیار کلی است و در واقع هیچ اطلاعاتی در مورد رویدادی که رخ داده است، ارائه نمی‌دهد. برای کلیک روی دکمه، این کافی است. برای حرکت ماوس روی یک فرم، ما یک رویداد MouseMove از کلاس [Form] خواهیم داشت که به این صورت تعریف شده است:

public event MouseEventHandler MouseMove;

delegate و MouseEventHandler به صورت زیر تعریف شده‌اند:

 

این یک تابع نماینده با امضای void f(object, MouseEventArgs) است. آیا کلاس MouseEventArgs توسط تعریف شده است:

کلاس MouseEventArgs امکانات بیشتری نسبت به کلاس EventArgs دارد. برای مثال، می‌توان مختصات X و Y ماوس را در لحظه وقوع رویداد دریافت کرد.

7.1.2.3. Conclusion

از دو پروژه مورد مطالعه، می‌توان نتیجه گرفت که پس از ساخت رابط کاربری گرافیکی با استفاده از ویژوال استودیو، کار توسعه‌دهنده عمدتاً شامل نوشتن رویدادپردازها برای رویدادهایی است که می‌خواهد در آن رابط مدیریت کند. کد به طور خودکار توسط ویژوال استودیو تولید می‌شود. این کد که می‌تواند پیچیده باشد، در ابتدا می‌توان از آن چشم‌پوشی کرد. در مرحله بعد، مطالعه آن ممکن است درک بهتری از ایجاد و مدیریت فرم‌ها فراهم کند.

7.2. کامپوننت‌های پایه

اکنون برنامه‌های مختلفی را که از رایج‌ترین کامپوننت‌ها استفاده می‌کنند، ارائه می‌دهیم تا روش‌ها و ویژگی‌های اصلی آن‌ها را بررسی کنیم. برای هر برنامه، رابط کاربری گرافیکی و کد مربوطه، عمدتاً کدهای هندلرهای رویداد، را ارائه خواهیم داد.

7.2.1. فرم

ما با معرفی مؤلفه ضروری شروع می‌کنیم: فرم (Form) که مؤلفه‌ها روی آن قرار می‌گیرند. ما قبلاً برخی از ویژگی‌های پایه‌ای آن را پوشش داده‌ایم. در اینجا، بر چند رویداد مهم مرتبط با فرم تمرکز می‌کنیم.

Load
فرم در حال بارگذاری است
Closing
فرم در حال بسته شدن است
Closed
فرم بسته شد

رویداد Load حتی قبل از نمایش فرم رخ می‌دهد. رویداد Closing هنگام بسته‌شدن فرم رخ می‌دهد. هنوز هم می‌توان این بسته‌شدن را به‌صورت برنامه‌ای متوقف کرد.

ما در حال ایجاد یک فرم به نام Form1 بدون هیچ‌گونه کامپوننت هستیم:

  • [1]: فرم
  • [2]: سه رویداد رسیدگی‌شده

کد برای [Form1.cs] به شرح زیر است:


using System;
using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e) {
            // بارگذاری اولیه فرم
            MessageBox.Show("Evt Load", "Load");
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
            // فرم در حال بسته شدن است
            MessageBox.Show("Evt FormClosing", "FormClosing");
            //تأیید درخواست می‌شود
            DialogResult réponse = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (réponse == DialogResult.No)
                e.Cancel = true;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
            //فرم در شرف بسته‌شدن است
            MessageBox.Show("Evt FormClosed", "FormClosed");
        }
    }
}

ما از تابع MessageBox برای مطلع شدن از رویدادهای مختلف استفاده می‌کنیم.

خط ۱۰: رویداد Load زمانی رخ خواهد داد که
اپلیکیشن، حتی پیش از نمایش فرم:
  
خط ۱۵: رویداد FormClosing زمانی رخ خواهد داد که
کاربر پنجره را می‌بندد.
خط ۱۹: سپس از کاربر می‌پرسیم که آیا واقعاً می‌خواهد خروج کند
برنامه را:
خط ۲۰: اگر پاسخ «خیر» باشد، ویژگی Cancel از
رویداد CancelEventArgs که متد آن را به‌عنوان یک
پارامتر. اگر این خاصیت را روی False تنظیم کنیم، بستن
پنجره لغو می‌شود؛ در غیر این صورت، ادامه می‌یابد. رویداد
رویداد FormClosed سپس رخ خواهد داد:

7.2.2. برچسب‌ها و کادرهای ورودی TextBox

ما قبلاً با این دو مؤلفه مواجه شده‌ایم. Label یک مؤلفه متنی است و TextBox یک مؤلفه فیلد ورودی است. ویژگی اصلی آن‌ها Text است که به محتوای فیلد ورودی یا متن برچسب اشاره دارد. این ویژگی قابل خواندن/نوشتن است.

رویدادی که معمولاً برای TextBox استفاده می‌شود، TextChanged است که سیگنال می‌دهد کاربر فیلد ورودی را تغییر داده است. در اینجا مثالی آورده شده است که از رویداد TextChanged برای ردیابی تغییرات یک فیلد ورودی استفاده می‌کند:

خیر.
نوع
name
نقش
۱
TextBox
textBoxSaisie
میدان ورودی
2
برچسب
labelControle
متن موجود در 1 را به‌صورت بلادرنگ نمایش می‌دهد
AutoSize=False, Text=(هیچ)
3
دکمه
buttonEffacer
برای پاک کردن فیلدهای ۱ و ۲
4
دکمه
buttonQuitter
برای خروج از برنامه

کد این برنامه به شرح زیر است:


using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
            //محتوا در TextBox تغییر کرده است – در حال کپی کردن آن به برچسب labelControle
            labelControle.Text = textBoxSaisie.Text;
        }

        private void buttonEffacer_Click(object sender, System.EventArgs e) {
            //محتویات کادر ورودی پاک می‌شوند
            textBoxSaisie.Text = "";
        }

        private void buttonQuitter_Click(object sender, System.EventArgs e) {
            // روی دکمه خروج کلیک کنید – برنامه خروج می‌کند
            Application.Exit();
        }

        private void Form1_Shown(object sender, System.EventArgs e) {
            // تمرکز را روی فیلد ورودی قرار دهید
            textBoxSaisie.Focus();
        }
    }
}
  • خط ۲۴: رویداد [Form].Shown زمانی رخ می‌دهد که فرم نمایش داده می‌شود
  • خط ۲۶: سپس تمرکز (برای ورودی) روی کامپوننت textBoxSaisie تنظیم می‌شود.
  • خط ۹: رویداد [TextBox].TextChanged هر زمان که محتوای یک کامپوننت TextBox تغییر کند، رخ می‌دهد
  • خط ۱۱: محتوای کامپوننت [TextBox] در کامپوننت [Label] کپی می‌شود
  • خط ۱۴: کلیک روی دکمه [Effacer] را مدیریت می‌کند
  • خط ۱۶: کامپوننت [TextBox] را روی یک رشته خالی تنظیم می‌کند
  • خط ۱۹: کلیک روی دکمه [Quitter] را مدیریت می‌کند
  • خط ۲۱: برای متوقف کردن برنامه در حال اجرا. به یاد داشته باشید که شیء Application برای راه‌اندازی برنامه در متد [Main] از [Form1.cs] استفاده می‌شود:

        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
}

مثال زیر از یک TextBox چندخطی استفاده می‌کند:

فهرست کنترل‌ها به شرح زیر است:

خیر.
نوع
نام
نقش
۱
TextBox
textBoxLignes
میدان ورودی چندخطی
چندخطی=true، نوارهای پیمایش=هر دو، پذیرش Enter=true، پذیرش Tab=true
۲
TextBox
textBoxLigne
میدان ورودی تک‌خطی
3
دکمه
buttonAjouter
محتوا را از 2 به 1 اضافه می‌کند

برای چندخطی کردن TextBox، ویژگی‌های کنترل زیر را تنظیم کنید:

Multiline=true
برای امکان چند خطی بودن متن
ScrollBars=( None, Horizontal, Vertical, Both)
برای مشخص کردن اینکه آیا کنترل باید نوار پیمایش (افقی، عمودی، هر دو) داشته باشد یا خیر (بدون)
AcceptReturn=(True, False)
اگر روی true تنظیم شود، فشردن کلید Enter به خط بعدی می‌رود
AcceptTab=(True, False)
اگر روی true تنظیم شود، کلید Tab یک تَب در متن وارد می‌کند

این برنامه به شما امکان می‌دهد خطوط را مستقیماً در [1] تایپ کنید یا از طریق [2] و [3] اضافه کنید.

کد برنامه به شرح زیر است:


using System.Windows.Forms;
using System;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void buttonAjouter_Click(object sender, System.EventArgs e) {
            // محتویات textBoxLigne را به محتویات textBoxLignes اضافه کنید
            textBoxLignes.Text += textBoxLigne.Text+Environment.NewLine;
            textBoxLigne.Text = "";
        }

        private void Form1_Shown(object sender, EventArgs e) {
            // تمرکز بر روی فیلد ورودی تنظیم شده است
            textBoxLigne.Focus();
        }
    }
}
  • خط ۱۸: هنگامی که فرم نمایش داده می‌شود (احتمالاً Shown)، تمرکز روی فیلد ورودی textBoxLigne قرار می‌گیرد
  • خط ۱۰: کلیک روی دکمه [Ajouter] را مدیریت می‌کند
  • خط ۱۲: متن موجود در فیلد ورودی textBoxLigne به متن موجود در فیلد ورودی textBoxLignes اضافه شده و پس از آن یک خط جدید قرار می‌گیرد.
  • خط ۱۳: فیلد ورودی textBoxLigne پاک می‌شود

7.2.3. لیست‌های کشویی ComboBox

ما فرم زیر را ایجاد می‌کنیم:

خیر.
نوع
نام
نقش
1
ComboBox
comboNombres
حاوی رشته‌های متنی است
DropDownStyle=DropDownList

یک کامپوننت ComboBox یک لیست کشویی است که با یک فیلد ورودی ترکیب شده است: کاربر می‌تواند یا یک مورد را از (2) انتخاب کند یا متن را در (1) تایپ کند. سه نوع ComboBox وجود دارد که توسط خاصیت DropDownStyle تعیین می‌شوند:

Simple
فهرست غیر کشویی با کادر ویرایش
DropDown
لیست کشویی با کادر ویرایش
DropDownList
لیست کشویی بدون کادر ویرایش

به طور پیش‌فرض، نوع یک ComboBox، DropDown است.

کلاس ComboBox دارای یک سازنده است:

new ComboBox()
یک لیست کشویی خالی ایجاد می‌کند

عناصر ComboBox در خصوصیت Items در دسترس هستند:

public ComboBox.ObjectCollection Items {get;}

این یک خاصیت نمایه‌گذاری‌شده است، که در آن Items[i] به آیتم i-ام در کادر ترکیبی اشاره می‌کند. این خاصیت فقط-خواندنی است.

فرض کنید C یک کادر ترکیبی و C.Items فهرست موارد آن باشد. ما ویژگی‌های زیر را داریم:

C.Items.Count
تعداد آیتم‌ها در کادر ترکیبی
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)
موقعیت شیء o را در کادر ترکیبی روی i تنظیم می‌کند
C.SelectedIndex
شاخص عنصر انتخاب‌شده
C.SelectedItem
عنصر انتخاب‌شده
C.SelectedItem.Text
متن نمایش داده شده عنصر انتخاب‌شده
C.Text
متن نمایش داده شده برای عنصر انتخاب شده

شاید تعجب‌آور باشد که یک کادر ترکیبی می‌تواند اشیاء را در خود داشته باشد، در حالی که از نظر بصری، رشته‌ها را نمایش می‌دهد. اگر یک ComboBox حاوی یک شیء obj باشد، رشته obj.ToString() را نمایش می‌دهد. به یاد داشته باشید که هر شیء دارای متد ToString است که از کلاس object به ارث رسیده و یک رشتهٔ کاراکتری را بازمی‌گرداند که «نماینده» آن شیء است.

عنصر Item انتخاب‌شده در کادر ترکیبی C، C.SelectedItem یا C.Items[C.SelectedIndex] است، که در آن C.SelectedIndex نمایه است از آیتم انتخاب‌شده، با این شماره که برای اولین آیتم از صفر شروع می‌شود. متن انتخاب‌شده را می‌توان به روش‌های مختلف به‌دست آورد: C.SelectedItem.Text، C.Text

وقتی یک مورد از فهرست کشویی انتخاب می‌شود، رویداد SelectedIndexChanged فعال می‌شود؛ این رویداد می‌تواند برای اطلاع از تغییر در انتخاب داخل کادر ترکیبی استفاده شود. در برنامهٔ زیر، از این رویداد برای نمایش موردی که از فهرست انتخاب شده است استفاده می‌کنیم.

 

کد برنامه به شرح زیر است:


using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        private int previousSelectedIndex=0;

        public Form1() {
            InitializeComponent();
            // پر کردن لیست کشویی
            comboBoxNombres.Items.AddRange(new string[] { "zéro", "un", "deux", "trois", "quatre" });
            //عنصر شماره 0 انتخاب شده
            comboBoxNombres.SelectedIndex = 0;
        }

        private void comboBoxNombres_SelectedIndexChanged(object sender, System.EventArgs e) {
            int newSelectedIndex = comboBoxNombres.SelectedIndex;
            if (newSelectedIndex != previousSelectedIndex) {
                //عنصر انتخابی تغییر کرده است – آن را نمایش دهید
                MessageBox.Show(string.Format("Elément sélectionné : ({0},{1})", comboBoxNombres.Text, newSelectedIndex), "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information);
                // شاخص جدید را ثبت کنید
                previousSelectedIndex = newSelectedIndex;
            }
        }
    }
}
  • خط ۵: previousSelectedIndex آخرین شاخص انتخاب‌شده در کادر ترکیبی را ذخیره می‌کند
  • خط ۱۰: کادر ترکیبی را با آرایه‌ای از رشته‌ها پر می‌کند
  • خط ۱۲: اولین آیتم انتخاب می‌شود
  • خط ۱۵: متد هر بار که کاربر یک مورد را از لیست کشویی انتخاب می‌کند، اجرا می‌شود. برخلاف آنچه نام رویداد ممکن است القا کند، این رویداد حتی در صورتی که مورد انتخاب‌شده با مورد قبلی یکسان باشد نیز رخ می‌دهد.
  • خط ۱۶: نمایه آیتم انتخاب‌شده ثبت می‌شود
  • خط ۱۷: اگر با مورد قبلی متفاوت باشد
  • خط ۱۹: شماره و متن آیتم انتخاب‌شده نمایش داده می‌شوند
  • خط ۲۱: ایندکس جدید ثبت می‌شود

7.2.4. کامپوننت ListBox

ما پیشنهاد می‌کنیم رابط زیر را بسازیم:

اجزای این پنجره به شرح زیر است:

خیر.
نوع
نام
نقش/ویژگی‌ها
0
فرم
Form1
فرم
FormBorderStyle=FixedSingle (مرز غیرقابل تغییر اندازه)
1
TextBox
textBoxSaisie
میدان ورودی
2
دکمه
buttonAjouter
دکمه برای افزودن محتویات فیلد ورودی [1] به لیست [3]
۳
ListBox
listBox1
فهرست ۱
حالت انتخاب = چندگانه توسعه‌یافته:
4
ListBox
listBox2
فهرست ۲
SelectionMode=MultiSimple:
۵
دکمه
دکمه1به2
موارد انتخاب‌شده را از لیست ۱ به لیست ۲ منتقل می‌کند
۶
دکمه
دکمه 2 به 1
مخالف آن را انجام می‌دهد
۷
دکمه
buttonEffacer1
لیست ۱ را پاک می‌کند
8
دکمه
buttonEffacer2
پاک کردن لیست ۲

اجزای ListBox دارای یک حالت انتخاب برای عناصر خود هستند که توسط ویژگی SelectionMode آن‌ها تعریف می‌شود:

One
فقط یک عنصر می‌تواند انتخاب شود
MultiExtended
چندگزینی امکان‌پذیر است: با نگه داشتن کلید SHIFT و کلیک بر روی یک عنصر، انتخاب از عنصر انتخاب‌شده قبلی تا عنصر فعلی گسترش می‌یابد.
MultiSimple
انتخاب چندگانه امکان‌پذیر است: یک مورد با کلیک ماوس یا فشردن کلید اسپیس انتخاب یا لغو انتخاب می‌شود.
  • کاربر متن را در فیلد 1 تایپ می‌کند. سپس با استفاده از دکمه Ajouter (2) آن را به لیست 1 اضافه می‌کند. فیلد ورودی (1) سپس پاک می‌شود و کاربر می‌تواند یک مورد جدید اضافه کند.
  • آنها می‌توانند با انتخاب آیتمی برای انتقال از یکی از لیست‌ها و انتخاب دکمه انتقال مناسب ۵ یا ۶، آیتم‌ها را از یک لیست به لیست دیگر منتقل کنند. آیتم منتقل‌شده به انتهای لیست مقصد اضافه شده و از لیست مبدأ حذف می‌شود.
  • آنها می‌توانند روی یک آیتم در لیست ۱ دوبار کلیک کنند. این آیتم سپس برای ویرایش به کادر ورودی منتقل شده و از لیست ۱ حذف می‌شود.

دکمه‌ها بر اساس قوانین زیر فعال یا غیرفعال می‌شوند:

  • دکمه Ajouter تنها در صورتی روشن می‌شود که در فیلد ورودی متنی غیرخالی وجود داشته باشد
  • دکمه [5] برای انتقال از فهرست 1 به فهرست 2 تنها زمانی فعال می‌شود که یک آیتم در فهرست 1 انتخاب شده باشد
  • دکمه [6] برای انتقال از فهرست ۲ به فهرست ۱ تنها زمانی روشن می‌شود که یک مورد در فهرست ۲ انتخاب شده باشد
  • دکمه‌های [7] و [8] برای پاک کردن فهرست‌های ۱ و ۲ تنها زمانی روشن می‌شوند که فهرست مورد نظر برای پاک کردن حاوی آیتم باشد.

در شرایط فوق، همه دکمه‌ها باید هنگام شروع برنامه خاموش باشند. بنابراین، ویژگی Enabled برای دکمه‌ها باید روی false تنظیم شود. این کار را می‌توان در زمان طراحی انجام داد که در این صورت کد مربوطه در متد InitializeComponent تولید می‌شود، یا می‌توانیم آن را خودمان در کانتراکتور (constructor) همان‌طور که در زیر نشان داده شده است، انجام دهیم:


        public Form1() {
            InitializeComponent();
            // --- инициалиزاسيون‌های اضافی ---
            // غیرفعال کردن تعدادی دکمه
            buttonAjouter.Enabled = false;
            button1vers2.Enabled = false;
            button2vers1.Enabled = false;
            buttonEffacer1.Enabled = false;
            buttonEffacer2.Enabled = false;
}

وضعیت دکمه Ajouter توسط محتویات فیلد ورودی کنترل می‌شود. این رویداد TextChanged است که به ما امکان می‌دهد تغییرات این محتوا را پیگیری کنیم:


        private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
            //محتوای textBoxSaisie تغییر کرده است
            //دکمهٔ «افزودن» تنها زمانی فعال می‌شود که فیلد خالی نباشد
            buttonAjouter.Enabled = textBoxSaisie.Text.Trim() != "";
        }

وضعیت دکمه‌های انتقال به این بستگی دارد که آیا موردی از فهرستی که کنترل می‌کنند انتخاب شده است یا خیر:


        private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
            //یک آیتم انتخاب شده است
            // دکمه انتقال از ۱ به ۲ فعال است
            button1vers2.Enabled = true;
        }

        private void listBox2_SelectedIndexChanged(object sender, System.EventArgs e) {
            //یک آیتم انتخاب شده است
            // دکمه «انتقال ۲ به ۱» فعال است
            button2vers1.Enabled = true;
}

کد مربوط به کلیک روی دکمه Ajouter به شرح زیر است:


        private void buttonAjouter_Click(object sender, System.EventArgs e) {
            //یک آیتم جدید به لیست 1 اضافه شده است
            listBox1.Items.Add(textBoxSaisie.Text.Trim());
            // حذف ورودی
            textBoxSaisie.Text = "";
            //فهرست ۱ خالی نیست
            buttonEffacer1.Enabled = true;
            //تمرکز به کادر ورودی بازمی‌گردد
            textBoxSaisie.Focus();
}

توجه کنید به متد Focus که به شما امکان می‌دهد «تمرکز» را روی یک کنترل فرم قرار دهید. کد مربوط به کلیک روی دکمه‌های Effacer:


        private void buttonEffacer1_Click(object sender, System.EventArgs e) {
            // فهرست 1 پاک شد
            listBox1.Items.Clear();
            // دکمه پاک کردن
            buttonEffacer1.Enabled = false;
        }

        private void buttonEffacer2_Click(object sender, System.EventArgs e) {
            // فهرست ۲ پاک شد
            listBox2.Items.Clear();
            // دکمه پاک کردن
            buttonEffacer2.Enabled = false;
}

کد انتقال موارد انتخاب‌شده از یک لیست به لیست دیگر:


        private void button1vers2_Click(object sender, System.EventArgs e) {
            // انتقال آیتم انتخاب‌شده از فهرست ۱ به فهرست ۲
            transfert(listBox1, button1vers2, buttonEffacer1, listBox2, button2vers1, buttonEffacer2);
        }

        private void button2vers1_Click(object sender, System.EventArgs e) {
            // انتقال آیتم انتخاب‌شده از فهرست ۲ به فهرست ۱
            transfert(listBox2, button2vers1, buttonEffacer2, listBox1, button1vers2, buttonEffacer1);
        }

دو متد بالا، انتقال موارد انتخاب‌شده از یک لیست به لیست دیگر را به یک متد خصوصی واحد به نام «transfert» واگذار می‌کنند:


        //انتقال
        private void transfert(ListBox l1, Button button1vers2, Button buttonEffacer1, ListBox l2, Button button2vers1, Button buttonEffacer2) {
            //انتقال موارد انتخاب‌شده از لیست l1 به لیست l2
            for (int i = l1.SelectedIndices.Count - 1; i >= 0; i--) {
                // شاخص عنصر انتخاب‌شده
                int index = l1.SelectedIndices[i];
                // افزودن به l2
                l2.Items.Add(l1.Items[index]);
                // حذف از l1
                l1.Items.RemoveAt(index);
            }
            // دکمه‌های پاک کردن
            buttonEffacer2.Enabled = l2.Items.Count != 0;
            buttonEffacer1.Enabled = l1.Items.Count != 0;
            // دکمه‌های انتقال
            button1vers2.Enabled = false;
}
  • خط b: متد `transfer` شش پارامتر می‌گیرد:
  • یک مرجع به لیستی که حاوی موارد انتخاب‌شده است، که در اینجا l1 نامیده می‌شود. هنگامی که برنامه در حال اجرا است، l1 یا listBox1 یا listBox2 خواهد بود. نمونه‌هایی از فراخوانی‌ها را می‌توان در خطوط ۳ و ۸ رویه‌های انتقال buttonXversY_Click مشاهده کرد.
  • یک مرجع به دکمه انتقال مرتبط با لیست l1. برای مثال، اگر l1 برابر listBox2 باشد، این دکمه button2vers1 خواهد بود (به فراخوانی در خط ۸ مراجعه کنید)
  • ارجاعی به دکمهٔ پاک کردن لیست l1. برای مثال، اگر l1 برابر listBox1 باشد، مقدار آن buttonEffacer1 خواهد بود (به فراخوانی در خط ۳ مراجعه کنید)
  • سه مرجع دیگر مشابه هستند اما به لیست l2 اشاره می‌کنند.
  • خط d: مجموعه [ListBox].SelectedIndices نمایانگر شاخص‌های عناصر انتخاب‌شده در مؤلفه [ListBox] است. این یک مجموعه است:
  • [ListBox].SelectedIndices.Count تعداد عناصر این مجموعه است
  • [ListBox].SelectedIndices[i] شماره عنصر i در این مجموعه است

ما مجموعه را به ترتیب معکوس پیمایش می‌کنیم: از انتهای مجموعه شروع کرده و به ابتدای آن می‌رسیم. توضیح خواهیم داد چرا.

  • خط f: نمایه یک عنصر منتخب در لیست l1
  • خط h: این عنصر به لیست l2 اضافه می‌شود
  • خط j: و از لیست l1 حذف می‌شود. از آنجایی که حذف شده است، دیگر انتخاب‌شده نیست. مجموعه l1.SelectedIndices از خط d مجدداً محاسبه خواهد شد. این مجموعه عنصر تازه‌حذف‌شده را از دست خواهد داد. شماره تمام عناصر پس از این عنصر از n به n-1 تغییر خواهد کرد.
  • اگر حلقه در خط (d) صعودی باشد و به تازگی عنصر شماره 0 را پردازش کرده باشد، سپس عنصر شماره 1 را پردازش خواهد کرد. با این حال، عنصری که قبل از حذف عنصر شماره 0، شماره 1 را داشت، اکنون شماره 0 را خواهد داشت. بنابراین این عنصر توسط حلقه نادیده گرفته می‌شود.
  • اگر حلقه در خط (d) نزولی باشد و به تازگی عنصر شماره n را پردازش کرده باشد، در مرحله بعد عنصر شماره n-1 را پردازش خواهد کرد. پس از حذف عنصر شماره n، شماره عنصر شماره n-1 تغییر نمی‌کند. بنابراین در تکرار بعدی حلقه پردازش می‌شود.
  • خطوط m–n: وضعیت دکمه‌های [Effacer] به این بستگی دارد که آیا عناصری در لیست‌های مربوطه وجود دارند یا خیر
  • خط p: لیست l2 دیگر هیچ مورد انتخاب‌شده‌ای ندارد: دکمه انتقال آن غیرفعال می‌شود.

7.2.5. تیک‌باکس‌های CheckBox، دکمه‌های رادیویی ButtonRadio

ما پیشنهاد می‌کنیم برنامه زیر را بنویسیم:

اجزای پنجره به شرح زیر هستند:

خیر.
نوع
نام
نقش
1
GroupBox
به [6] مراجعه کنید
groupBox1
یک کانتینر برای کامپوننت‌ها. سایر کامپوننت‌ها می‌توانند درون آن قرار گیرند.
متن=دکمه‌های رادیویی
2
RadioButton
radioButton1
radioButton2
radioButton3
3 دکمه رادیویی – radioButton1 دارای ویژگی Checked=True و ویژگی Text=1 است - radioButton2 دارای ویژگی Text=2 است - radioButton3 دارای ویژگی Text=3 است
دکمه‌های رادیویی در یک کانتینر واحد – در این مورد، GroupBox – یکدیگر را مستثنی می‌کنند: تنها یکی از آن‌ها می‌تواند در هر زمان انتخاب شود.
۳
GroupBox
groupBox2
 
4
CheckBox
checkBox1
checkBox2
checkBox3
۳ خانه تیک. chechBox1 دارای ویژگی Checked=True و ویژگی Text=A است. - chechBox2 دارای ویژگی Text=B است - chechBox3 دارای ویژگی Text=C است
۵
ListBox
listBoxValeurs
فهرستی که مقادیر دکمه‌های رادیویی و چک‌باکس‌ها را به محض وقوع تغییر نمایش می‌دهد.
۶
  
نشان می‌دهد که کانتینر GroupBox را کجا می‌توان یافت

رویدادی که برای این شش کنترل برای ما اهمیت دارد، رویداد CheckChanged است که نشان می‌دهد وضعیت تیک‌باکس یا دکمهٔ رادیویی تغییر کرده است. در هر دو مورد، این وضعیت با خاصیت بولین Checked نشان داده می‌شود؛ وقتی این مقدار true باشد، یعنی کنترل تیک خورده است. در اینجا، ما تنها از یک متد برای رسیدگی به هر شش رویداد CheckChanged استفاده خواهیم کرد: متد affiche. برای اطمینان از اینکه شش رویداد CheckChanged توسط همان متد affiche مدیریت می‌شوند، مراحل زیر را دنبال کنید:

کامپوننت radioButton1 را انتخاب کرده و روی آن کلیک راست کنید تا به ویژگی‌های آن دسترسی پیدا کنید:

در زبانه événements [1]، متد affiche [2] را با رویداد CheckChanged مرتبط کنید. این بدان معناست که ما می‌خواهیم کلیک روی گزینه A1 توسط متدی به نام affiche پردازش شود. ویژوال استودیو به‌طور خودکار متد affiche را در پنجره کد تولید می‌کند:


private void affiche(object sender, EventArgs e) {
        }

متد affiche یک متد از نوع EventHandler است.

همان رویه برای پنج مؤلفهٔ دیگر نیز اعمال می‌شود. برای مثال، گزینهٔ CheckBox1 و رویدادهای [3] آن را در نظر بگیرید. در کنار رویداد Click، یک لیست کشویی با برچسب [4] وجود دارد که شامل متدهای موجودی است که قادر به مدیریت این رویداد هستند. در اینجا، تنها متد affiche. در دسترس است. آن را انتخاب کنید. این فرآیند را برای سایر اجزا نیز تکرار کنید.

در متد InitializeComponent، کد تولید شده است. متد affiche به عنوان دست‌پردازنده برای شش رویداد CheckedChanged به شرح زیر اعلام شده است:


this.radioButton1.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton2.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton3.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox1.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox2.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox3.CheckedChanged += new System.EventHandler(this.affiche);

متد affiche به صورت زیر تکمیل شده است:


        private void affiche(object sender, System.EventArgs e) {
            //وضعیت دکمه رادیویی یا چک‌باکس را نمایش می‌دهد
            //آیا این یک چک‌باکس است؟
            if (sender is CheckBox) {
                CheckBox chk = (CheckBox)sender;
                listBoxvaleurs.Items.Add(chk.Name + "=" + chk.Checked);
            }
            //آیا این دکمهٔ رادیویی است؟
            if (sender is RadioButton) {
                RadioButton rdb = (RadioButton)sender;
                listBoxvaleurs.Items.Add(rdb.Name + "=" + rdb.Checked);
            }
}

سینتکس


            if (sender is CheckBox) {

به ما امکان می‌دهد تا تأیید کنیم که شیء sender از نوع CheckBox است. این امر سپس به ما امکان می‌دهد تا آن را به نوع دقیق sender تبدیل کنیم. متد affiche نام کامپوننتی را که رویداد را تحریک کرده و مقدار خاصیت Checked آن را در لیست listBoxValeurs می‌نویسد. وقتی [7] اجرا می‌شود، می‌بینیم که کلیک روی یک دکمه رادیویی دو رویداد CheckChanged را ایجاد می‌کند: یکی روی دکمه رادیویی قدیمی که به «unchecked» تغییر می‌کند و دیگری روی دکمه رادیویی جدید که به «checked» تغییر می‌کند.

7.2.6. اسلایدرها ScrollBar

چند نوع اسلایدر وجود دارد:
اسلایدر افقی (HscrollBar
اسلایدر عمودی (VscrollBar
افزایش‌دهنده (NumericUpDown).

بیایید اپلیکیشن زیر را ایجاد کنیم:

خیر.
نوع
نام
نقش
1
hScrollBar
hScrollBar1
یک واریاتور افقی
2
hScrollBar
hScrollBar2
یک واریاتور افقی که تغییرات واریاتور ۱ را دنبال می‌کند
3
برچسب
labelValeurHS1
مقدار اسلایدر افقی را نمایش می‌دهد
۴
NumericUpDown
numericUpDown2
امکان تنظیم مقدار اسلایدر ۲ را فراهم می‌کند

اسلایدر ScrollBar به کاربر امکان می‌دهد از میان بازه‌ای از مقادیر صحیح، مقداری را انتخاب کند که با «باند» اسلایدر نشان داده می‌شود و نشانگر روی آن حرکت می‌کند. مقدار اسلایدر در خاصیت Value آن در دسترس است.

  • برای یک اسلایدر افقی، انتهای سمت چپ نشان‌دهنده حداقل مقدار محدوده، انتهای سمت راست نشان‌دهنده حداکثر مقدار و خود اسلایدر نشان‌دهنده مقدار انتخاب‌شده در حال حاضر است. برای یک اسلایدر عمودی، حداقل با انتهای بالایی و حداکثر با انتهای پایینی نشان داده می‌شود. این مقادیر توسط خواص Minimum و Maximum نمایش داده می‌شوند و به طور پیش‌فرض روی 0 و 100 تنظیم شده‌اند.
  • کلیک کردن روی انتهای اسلایدر، مقدار را به اندازه یک گام (مثبت یا منفی) تغییر می‌دهد که این بستگی به انتهای کلیک‌شده دارد؛ این عمل LargeChange نامیده می‌شود که مقدار پیش‌فرض آن 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

کد برنامه به شرح زیر است:


using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
            // ویژگی‌های درایو ۱ را تنظیم کنید
            hScrollBar1.Value = 7;
            hScrollBar1.Minimum = 1;
            hScrollBar1.Maximum = 130;
            hScrollBar1.LargeChange = 11;
            hScrollBar1.SmallChange = 1;
            //ما درایو ۲ را با همان تنظیمات درایو ۱ پیکربندی می‌کنیم
            hScrollBar2.Value = hScrollBar1.Value;
            hScrollBar2.Minimum = hScrollBar1.Minimum;
            hScrollBar2.Maximum = hScrollBar1.Maximum;
            hScrollBar2.LargeChange = hScrollBar1.LargeChange;
            hScrollBar2.SmallChange = hScrollBar1.SmallChange;
            //همین امر در مورد شمارنده نیز صدق می‌کند
            numericUpDown2.Value = hScrollBar1.Value;
            numericUpDown2.Minimum = hScrollBar1.Minimum;
            numericUpDown2.Maximum = hScrollBar1.Maximum;
            numericUpDown2.Increment = hScrollBar1.SmallChange;

            //مقدار درایو ۱ را به برچسب اختصاص دهید
            labelValeurHS1.Text = hScrollBar1.Value.ToString();
        }

        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) {
            // تغییر در مقدار درایو ۱
            // مقدار آن در درایو ۲ و برچسب منعکس می‌شود
            hScrollBar2.Value = hScrollBar1.Value;
            labelValeurHS1.Text = hScrollBar1.Value.ToString();
        }

        private void numericUpDown2_ValueChanged(object sender, System.EventArgs e) {
            // شمارنده مقدار خود را تغییر داده است
            //مقدار درایو 2 را تنظیم کنید
            hScrollBar2.Value = (int)numericUpDown2.Value;
        }
    }
}

7.3. رویدادهای ماوس

هنگام ترسیم در یک کانتینر، دانستن موقعیت ماوس مهم است تا برای مثال، هنگام کلیک روی ماوس بتوان یک نقطه نمایش داد. حرکت ماوس، رویدادهایی را در کانتینری که ماوس در آن در حال حرکت است، فعال می‌کند.

  • [1]: رویدادهایی که هنگام حرکت ماوس روی فرم یا یک کنترل رخ می‌دهند
  • [2]: رویدادهایی که در طول کشیدن و رها کردن (Drag'nDrop) رخ می‌دهند
MouseEnter
موش به تازگی وارد ناحیه کنترل شده است
MouseLeave
موش به تازگی محدوده کنترل را ترک کرده است
MouseMove
موش در داخل ناحیه کنترل در حال حرکت است
MouseDown
دکمه چپ ماوس فشرده شده
MouseUp
رها شدن دکمه چپ ماوس
DragDrop
کاربر یک شیء را روی کنترل می‌اندازد
DragEnter
کاربر یک شیء را به داخل ناحیه کنترل می‌کشد
DragLeave
کاربر یک شیء را از ناحیه کنترل بیرون می‌کشد
DragOver
کاربر هنگام کشیدن یک شیء، ماوس را بر روی ناحیه کنترل نگه می‌دارد

در اینجا یک برنامه کاربردی برای کمک به شما جهت درک بهتر زمان وقوع رویدادهای مختلف ماوس آورده شده است:

خیر.
نوع
نام
نقش
1
برچسب
lblPositionSouris
برای نمایش موقعیت ماوس در فرم 1، لیست 2 یا دکمه 3
2
ListBox
listBoxEvts
برای نمایش رویدادهای ماوس به غیر از MouseMove
3
دکمه
buttonEffacer
برای پاک کردن محتویات ۲

برای ردیابی حرکات ماوس در سه‌گانه کنترل‌ها، ما تنها یک هندلر می‌نویسیم، هندلر affiche:

کد رویه affiche به شرح زیر است:


        private void affiche(object sender, MouseEventArgs e) {
            //حرکت ماوس – مختصات ماوس (X، Y) نمایش داده می‌شوند
            labelPositionSouris.Text = "(" + e.X + "," + e.Y + ")";
}

هر بار که ماوس وارد ناحیه یک کنترل می‌شود، سیستم مختصات آن تغییر می‌کند. مبدأ آن (0,0) گوشه بالا سمت چپ کنترلی است که روی آن قرار دارد. بنابراین، در زمان اجرا، وقتی ماوس را از فرم به دکمه می‌برید، می‌توانید تغییر مختصات را به‌وضوح ببینید. برای نمایش بهتر این تغییرات در ناحیه ماوس، می‌توانید از ویژگی‌های Cursor و [1] کنترل‌ها استفاده کنید:

این خاصیت به شما امکان می‌دهد شکل نشانگر ماوس را هنگام ورود به ناحیه کنترل تنظیم کنید. بنابراین، در مثال ما، ما نشانگر را برای خود فرم روی «Default» ([2])، برای لیست ۲ روی «Hand» ([3]) و برای دکمه ۳ روی «Cross» ([4]) تنظیم کرده‌ایم.

علاوه بر این، برای تشخیص زمانی که ماوس وارد و خارج لیست ۲ می‌شود، رویدادهای MouseEnter و MouseLeave را برای همان لیست مدیریت می‌کنیم:


        private void listBoxEvts_MouseEnter(object sender, System.EventArgs e) {
            //رویداد سیگنال داده می‌شود
            listBoxEvts.Items.Insert(0, string.Format("MouseEnter à {0:hh:mm:ss}",DateTime.Now));
        }

        private void listBoxEvts_MouseLeave(object sender, EventArgs e) {
            // رویداد سیگنال داده می‌شود
            listBoxEvts.Items.Insert(0, string.Format("MouseLeave à {0:hh:mm:ss}", DateTime.Now));
}

برای رسیدگی به کلیک‌ها روی فرم، رویدادهای MouseDown و MouseUp را مدیریت می‌کنیم:


        private void listBoxEvts_MouseDown(object sender, MouseEventArgs e) {
            // رویداد را فعال می‌کند
            listBoxEvts.Items.Insert(0, string.Format("MouseDown à {0:hh:mm:ss}", DateTime.Now));
        }

        private void listBoxEvts_MouseUp(object sender, MouseEventArgs e) {
            // رویداد را فعال می‌کند
            listBoxEvts.Items.Insert(0, string.Format("MouseUp à {0:hh:mm:ss}", DateTime.Now));
}
  • رده‌های ۳ و ۸: پیام‌ها ابتدا در ListBox قرار می‌گیرند تا رویدادهای جدیدتر در بالای لیست ظاهر شوند.
 

در نهایت، کد برای رویدادپرداز کلیک دکمه Effacer:


        private void buttonEffacer_Click(object sender, EventArgs e) {
            listBoxEvts.Items.Clear();
}

7.4. ایجاد یک پنجره با منو

حال بیایید ببینیم چگونه یک پنجره با منو ایجاد کنیم. ما پنجره زیر را ایجاد خواهیم کرد:

برای ایجاد یک منو، کامپوننت «MenuStrip» را از نوار «منوها و نوار ابزارها» انتخاب کنید:

  • [1]: انتخاب کامپوننت [MenuStrip]
  • [2]: این یک منو ایجاد می‌کند که با فیلدهای خالی با برچسب «Type Here» روی فرم ظاهر می‌شود. کافی است گزینه‌های مختلف منو را در این فیلدها وارد کنید.
  • [3]: برچسب «گزینه‌های A» وارد شده است. به برچسب [4] می‌رویم.
  • [5]: برچسب‌های گزینه‌های زیر «A» وارد شده‌اند. به برچسب [6] می‌رویم.
  • [6]: اولین گزینه‌های B
  • [7]: زیر B1، یک جداکننده درج کنید. این در یک منوی کشویی با برچسب «Type Here» موجود است.
  • [8]: برای ایجاد یک زیرمنو، از فلش [8] استفاده کنید و زیرمنو را در [9] تایپ کنید

اکنون باید اجزای مختلف فرم را نام‌گذاری کنید:

خیر.
نوع
نام(ها)
نقش
۱
برچسب
labelStatut
برای نمایش متن گزینه منوی کلیک‌شده
2
toolStripMenuItem
toolStripMenuItemOptionsA
toolStripMenuItemA1
toolStripMenuItemA2
toolStripMenuItemA3
گزینه‌های منو زیر گزینهٔ اصلی «گزینه‌ها A»
۳
toolStripMenuItem
toolStripMenuItemOptionsB
toolStripMenuItemB1
toolStripMenuItemB2
toolStripMenuItemB3
گزینه‌های منو زیر گزینهٔ اصلی «گزینه‌ها ب»
۴
toolStripMenuItem
toolStripMenuItemB31
toolStripMenuItemB32
گزینه‌های منو زیر گزینهٔ اصلی "B3"

گزینه‌های منو کنترل‌هایی هستند درست مانند سایر اجزای بصری و دارای ویژگی‌ها و رویدادها می‌باشند. برای مثال، ویژگی‌های گزینه منوی A1 به شرح زیر است:

 

در مثال ما از دو ویژگی استفاده شده است:

Name
نام کنترل منو
Text
برچسب گزینهٔ منو

در ساختار منو، گزینه A1 را انتخاب کرده و برای دسترسی به ویژگی‌های کنترل، روی آن کلیک راست کنید:

در برگه événements [1]، متد affiche [2] را به رویداد Click مرتبط می‌کنیم. این بدان معناست که ما می‌خواهیم کلیک روی گزینه A1 توسط متدی به نام affiche پردازش شود. ویژوال استودیو به‌طور خودکار متد affiche را در پنجره کد تولید می‌کند:


private void affiche(object sender, EventArgs e) {
        }

در این متد، ما به سادگی خاصیت Text از گزینه منویی که کلیک شده است را در برچسب labelStatut نمایش می‌دهیم:


private void affiche(object sender, EventArgs e) {
            //نام زیرمنوی انتخاب‌شده را در TextBox نمایش می‌دهد
            labelStatut.Text = ((ToolStripMenuItem)sender).Text;
}

منبع رویداد sender از نوع object است. از آنجا که گزینه‌های منو از نوع ToolStripMenuItem هستند، ما موظف به انجام تبدیل نوع از object به ToolStripMenuItem هستیم.

برای همه گزینه‌های منو، رویداد کلیک را به متد affiche [3,4] اختصاص می‌دهیم.

بیایید برنامه را اجرا کنیم و یک مورد منو را انتخاب کنیم:

 

7.5. کامپوننت‌های غیرگرافیکی

اکنون به تعدادی از مؤلفه‌های غیرگرافیکی می‌پردازیم: این مؤلفه‌ها در حین طراحی استفاده می‌شوند اما در زمان اجرا قابل مشاهده نیستند.

7.5.1. دیالوگ boxes ، OpenFileDialog و SaveFileDialog

ما قصد داریم برنامه زیر را بسازیم:

کنترل‌ها به شرح زیر هستند:

خیر.
نوع
name
نقش
1
TextBox
TextBoxLignes
متن وارد شده توسط کاربر یا بارگذاری شده از یک فایل
چندخطی=True، نوارپیمایش=هر دو، پذیرشEnter=True، پذیرشTab=True
2
دکمه
buttonSauvegarder
امکان ذخیره متن از [1] در یک فایل متنی را برای شما فراهم می‌کند
3
دکمه
buttonCharger
امکان بارگذاری محتویات یک فایل متنی را در [1] فراهم می‌کند
۴
دکمه
buttonEffacer
محتویات [1] را پاک می‌کند
۵
SaveFileDialog
saveFileDialog1
کامپوننتی که به شما امکان می‌دهد نام و محل فایل پشتیبان برای [1] را انتخاب کنید. این کامپوننت از نوار ابزار [7] کشیده شده و به سادگی روی فرم رها می‌شود. سپس ذخیره می‌شود اما هیچ فضایی روی فرم اشغال نمی‌کند. این یک کامپوننت غیرنمایشی است.
۶
OpenFileDialog
openFileDialog1
کامپوننت برای انتخاب فایلی که باید در [1] بارگذاری شود.

کد مرتبط با دکمه Effacer ساده است:


        private void buttonEffacer_Click(object sender, EventArgs e) {
            //رشته را در TexBox به خالی تنظیم می‌کند
            textBoxLignes.Text = "";
}

ما از ویژگی‌ها و متدهای زیر کلاس SaveFileDialog استفاده خواهیم کرد:

Field
نوع
نقش
string Filter
Propriété
انواع فایل‌های موجود در فهرست کشویی نوع فایل در کادر محاوره‌ای
int FilterIndex
Propriété
شمارهٔ پیش‌فرض نوع فایل در فهرست بالا. از ۰ شروع می‌شود.
string InitialDirectory
Propriété
پوشه‌ای که در ابتدا برای ذخیره فایل مشخص شده است
string FileName
Propriété
نام فایل پشتیبان مشخص‌شده توسط کاربر
DialogResult.ShowDialog()
Méthode
روشی که کادر محاوره‌ای ذخیره را نمایش می‌دهد. مقداری از نوع DialogResult را برمی‌گرداند.

روش ShowDialog یک کادر محاوره‌ای مشابه شکل زیر را نمایش می‌دهد:

1
فهرست کشویی تولید شده از خاصیت Filter. نوع فایل پیش‌فرض توسط FilterIndex تنظیم می‌شود.
2
 
3
نام فایل انتخاب‌شده یا مستقیماً واردشده توسط کاربر. در خصوصیت FileName در دسترس خواهد بود.
4
دکمه‌های ذخیره/لغو. اگر از دکمه Enregistrer استفاده شود، تابع ShowDialog نتیجه DialogResult.OK را برمی‌گرداند.

روند ذخیره به صورت زیر نوشته می‌شود:


private void buttonSauvegarder_Click(object sender, System.EventArgs e) {
            // جعبه‌ی ورودی را در یک فایل متنی ذخیره می‌کند
            // جعبه‌ی گفت‌وگوی savefileDialog1 پیکربندی می‌شود
            saveFileDialog1.InitialDirectory = Application.ExecutablePath;
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
            saveFileDialog1.FilterIndex = 0;
            // جعبه‌ی گفتگو را نمایش داده و نتیجه‌ی آن را بازیابی می‌کند
            if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
                //بازیابی نام فایل
                string nomFichier = saveFileDialog1.FileName;
                StreamWriter fichier = null;
                try {
                    // فایل را برای نوشتن باز کنید
                    fichier = new StreamWriter(nomFichier);
                    // متن را در آن بنویسید
                    fichier.Write(textBoxLignes.Text);
                } catch (Exception ex) {
                    // مشکل
                    MessageBox.Show("Problème à l'écriture du fichier (" +
                    ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                } finally {
                    // بستن فایل
                    if (fichier != null) {
                        fichier.Dispose();
                    }
                }
            }
        }
  • خط ۴: پوشه اولیه (InitialDirectory) روی پوشه (Application.ExecutablePath) حاوی فایل اجرایی برنامه تنظیم می‌شود.
  • خط ۵: انواع فایل‌هایی که باید نمایش داده شوند مشخص می‌شوند. به سینتکس فیلترها توجه کنید: filtre1|filtre2|..|filtren، که در آن filteri = Text|قالب فایل. در اینجا، کاربر قادر خواهد بود بین فایل‌های *.txt و *.* انتخاب کند.
  • خط ۶: نوع فایلی که ابتدا به کاربر نمایش داده شود مشخص می‌شود. در اینجا، اندیس ۰ به فایل‌های *.txt اشاره دارد.
  • خط ۸: کادر محاوره‌ای نمایش داده می‌شود و نتیجهٔ آن بازیابی می‌شود. هنگامی که کادر محاوره‌ای نمایش داده می‌شود، کاربر دیگر به فرم اصلی دسترسی ندارد (این به عنوان کادر محاوره‌ای مدال شناخته می‌شود). کاربر نام فایلی را که باید ذخیره شود مشخص می‌کند و از کادر محاوره‌ای یا با کلیک بر روی دکمه Enregistrer، دکمه Annuler,، یا با بستن کادر محاوره‌ای خارج می‌شود. نتیجه متد ShowDialog تنها در صورتی DialogResult.OK است که کاربر از دکمه Enregistrer برای بستن کادر محاوره‌ای استفاده کرده باشد.
  • پس از انجام این کار، نام فایلی که باید ایجاد شود اکنون در خصوصیت FileName از شیء saveFileDialog1 قرار دارد. سپس به فرآیند استاندارد ایجاد یک فایل متنی بازمی‌گردیم. محتویات TextBox: textBoxLignes.Text را در آن می‌نویسیم، در حالی که هر استثنایی را که ممکن است رخ دهد، مدیریت می‌کنیم.

کلاس OpenFileDialog بسیار شبیه به کلاس SaveFileDialog است. ما از همان متدها و ویژگی‌های قبلی استفاده خواهیم کرد. متد ShowDialog یک کادر محاوره‌ای مشابه شکل زیر را نمایش می‌دهد:

1
لیست کشویی تولید شده از خاصیت Filter. نوع فایل پیش‌فرض توسط FilterIndex تعیین می‌شود.
2
 
3
نام فایلی که توسط کاربر انتخاب یا مستقیماً وارد شده است. این نام در ویژگی FileName در دسترس خواهد بود.
4
دکمه‌های باز/لغو. اگر دکمه Ouvrir استفاده شود، تابع ShowDialog نتیجه DialogResult.OK را بازمی‌گرداند.

روند بارگذاری فایل متنی را می‌توان به صورت زیر نوشت:


private void buttonCharger_Click(object sender, EventArgs e) {
            // بارگذاری یک فایل متنی در کادر ورودی
            // راه‌اندازی کادر گفتگو openfileDialog1
            openFileDialog1.InitialDirectory = Application.ExecutablePath;
            openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
            openFileDialog1.FilterIndex = 0;
            // پنجرهٔ گفتگو را نمایش داده و نتیجهٔ آن را بازیابی می‌کند
            if (openFileDialog1.ShowDialog() == DialogResult.OK) {
                //بازیابی نام فایل
                string nomFichier = openFileDialog1.FileName;
                StreamReader fichier = null;
                try {
                    // فایل را در حالت فقط خواندنی باز کنید
                    fichier = new StreamReader(nomFichier);
                    // کل فایل را می‌خواند و در TextBox ذخیره می‌کند
                    textBoxLignes.Text = fichier.ReadToEnd();
                } catch (Exception ex) {
                    // مشکل
                    MessageBox.Show("Problème à la lecture du fichier (" +
                    ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                } finally {
                    // بستن فایل
                    if (fichier != null) {
                        fichier.Dispose();
                    }
                }//بالاخره
            }//اگر
        }
  • خط ۴: دایرکتوری اولیه (InitialDirectory) روی دایرکتوری (Application.ExecutablePath) حاوی فایل اجرایی برنامه تنظیم می‌شود.
  • خط ۵: انواع فایل‌هایی که باید نمایش داده شوند مشخص می‌شوند. به دستوریات فیلترها توجه کنید: filtre1|filtre2|..|filtren، که در آن filteri = قالب متن|فایل. در اینجا، کاربر قادر خواهد بود بین فایل‌های *.txt و *.* انتخاب کند.
  • خط ۶: نوع فایلی که ابتدا به کاربر نمایش داده می‌شود مشخص می‌شود. در اینجا، اندیس ۰ به فایل‌های *.txt اشاره دارد.
  • خط ۸: کادر محاوره‌ای نمایش داده می‌شود و نتیجهٔ آن بازیابی می‌شود. در حالی که کادر محاوره‌ای نمایش داده می‌شود، کاربر دیگر به فرم اصلی دسترسی ندارد (این به عنوان کادر محاوره‌ای مدال شناخته می‌شود). کاربر نام فایلی را که باید ذخیره شود مشخص می‌کند و از کادر محاوره‌ای یا با کلیک بر روی دکمه Ouvrir، دکمه Annuler,، یا با بستن کادر محاوره‌ای خارج می‌شود. نتیجه متد ShowDialog تنها در صورتی DialogResult.OK است که کاربر از دکمه Enregistrer برای بستن کادر محاوره‌ای استفاده کرده باشد.
  • پس از انجام این کار، نام فایلی که قرار است ایجاد شود اکنون در ویژگی FileName از شیء openFileDialog1 ذخیره می‌شود. سپس به فرآیند استاندارد خواندن یک فایل متنی بازمی‌گردیم. توجه کنید که در خط ۱۶، متدی وجود دارد که امکان خواندن کل فایل را فراهم می‌کند.

7.5.2. جعبه‌های محاوره‌ای FontColor و ColorDialog

ما با مثال قبلی ادامه می‌دهیم و دو دکمه جدید و دو کنترل غیرگرافیکی جدید اضافه می‌کنیم:

۶

۷

خیر.
نوع
نام
نقش
۱
دکمه
buttonCouleur
برای تنظیم رنگ متن برای TextBox
2
دکمه
buttonPolice
برای تنظیم قلم برای TextBox
3
ColorDialog
colorDialog1
کامپوننتی که امکان انتخاب رنگ را فراهم می‌کند – برگرفته از جعبه ابزار [5].
4
FontDialog
colorDialog1
کامپوننتی که امکان انتخاب قلم را فراهم می‌کند – برگرفته از مجموعه ابزار [5].

کلاس‌های FontDialog و ColorDialog دارای متدی به نام ShowDialog هستند که مشابه متد ShowDialog در کلاس‌های OpenFileDialog و QZXW2HTML می‌باشند.P002972ZQX.

روش ShowDialog کلاس ColorDialog به شما امکان می‌دهد یک رنگ [1] را انتخاب کنید. متد در کلاس FontDialog به شما امکان می‌دهد فونت [2] را انتخاب کنید:

  • [1]: اگر کاربر با استفاده از دکمه OK از کادر گفتگو خارج شود، نتیجه متد ShowDialog برابر DialogResult.OK است و رنگ انتخاب‌شده در خصوصیت Color از شیء ColorDialog مورد استفاده ذخیره می‌شود.
  • [2]: اگر کاربر با استفاده از دکمه OK پنجرهٔ گفتگو را ببندد، نتیجهٔ متد ShowDialog برابر DialogResult.OK است و قلم انتخاب‌شده در خصوصیت Font از شیء FontDialog در حال استفاده ذخیره می‌شود.

اکنون ما عناصر لازم برای رسیدگی به کلیک‌های روی دکمه‌های Couleur و Police را داریم:


        private void buttonCouleur_Click(object sender, EventArgs e) {// رنگ متن را انتخاب کنید
            if (colorDialog1.ShowDialog() == DialogResult.OK) {
                //تغییر خاصیت Forecolor در TextBox
                textBoxLignes.ForeColor = colorDialog1.Color;
            }//اگر
        }

        private void buttonPolice_Click(object sender, EventArgs e) {
            //انتخاب فونت
            if (fontDialog1.ShowDialog() == DialogResult.OK) {
                //تغییر ویژگی Font در TextBox
                textBoxLignes.Font = fontDialog1.Font;
}
  • خط [4]: ویژگی [ForeColor] یک کامپوننت TextBox، رنگ نوع [Color] را برای حروف در TextBox مشخص می‌کند. در اینجا، این رنگ همان رنگی است که کاربر در کادر محاوره‌ای از نوع [ColorDialog] انتخاب کرده است.
  • خط [12]: ویژگی [Font] یک مؤلفه TextBox به قلم نوع [Font] برای کاراکترهای در TextBox اشاره دارد. در اینجا، این قلم همان قلمی است که کاربر در کادر محاوره‌ای از نوع [FontDialog] انتخاب کرده است.

7.5.3. تایمر

در اینجا، پیشنهاد می‌کنیم برنامه زیر را بنویسیم:

خیر.
نوع
نام
نقش
1
برچسب
labelChrono
نمایش ساعت‌شمار
۲
دکمه
buttonArretMarche
دکمه توقف/شروع برای تایمر
۳
تایمر
تایمر۱
کامپوننتی که هر ثانیه یک رویداد را فعال می‌کند

در [4]، می‌بینیم که زمان‌سنج در حال اجرا است؛ در [5]، زمان‌سنج متوقف شده است.

برای به‌روزرسانی محتوای برچسب LabelChrono هر ثانیه، به یک کامپوننت نیاز داریم که هر ثانیه یک رویداد تولید کند؛ سپس می‌توانیم این رویداد را رهگیری کرده و نمایش ساعت‌شمار را به‌روزرسانی کنیم. این کامپوننت Timer [1] است که در کیت ابزار Components [2] موجود است:

ویژگی‌های کامپوننت تایمر مورد استفاده در اینجا به شرح زیر خواهد بود:

Interval
تعداد میلی‌ثانیه‌ها پس از آن که رویداد Tick فعال می‌شود.
Tick
رویدادی که در پایان Interval میلی‌ثانیه فعال می‌شود
Enabled
تایمر را روی فعال (true) یا غیرفعال (false) تنظیم می‌کند

در مثال ما، تایمر timer1 نامیده می‌شود و timer1.Interval روی 1000 میلی‌ثانیه (1 ثانیه) تنظیم شده است. بنابراین رویداد Tick هر ثانیه یک‌بار رخ خواهد داد. کلیک روی دکمه توقف/شروع توسط رویه زیر، buttonArretMarche_Click، مدیریت می‌شود:


using System;
using System.Windows.Forms;

namespace Chap5 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        // متغیر نمونه
        private DateTime début = DateTime.Now;
...
        private void buttonArretMarche_Click(object sender, EventArgs e) {
            // متوقف یا شروع؟
            if (buttonArretMarche.Text == "Marche") {
                // ثبت زمان شروع
                début = DateTime.Now;
                // آن را نمایش دهید
                labelChrono.Text = "00:00:00";
                // شروع تایمر
                timer1.Enabled = true;
                // برچسب دکمه را تغییر دهید
                buttonArretMarche.Text = "Arrêt";
                //پایان
                return;
            }//
            if (buttonArretMarche.Text == "Arrêt") {
                // تایمر را متوقف کنید
                timer1.Enabled = false;
                // تغییر برچسب دکمه
                buttonArretMarche.Text = "Marche";
                // پایان
                return;
            }
        }

    }
}
  • خط ۱۳: روندی که کلیک روی دکمه شروع/توقف را مدیریت می‌کند.
  • خط ۱۵: برچسب روی دکمه توقف/شروع یا «توقف» یا «شروع» است. بنابراین باید این برچسب را بررسی کنیم تا مشخص شود چه اقدامی باید انجام شود.
  • خط ۱۷: اگر برچسب «On» باشد، زمان شروع در متغیر début ذخیره می‌شود که یک متغیر سراسری (خط ۱۱) از نوع object است.
  • خط ۱۹: محتوای برچسب LabelChrono را مقداردهی اولیه می‌کند
  • خط ۲۱: تایمر شروع می‌شود (Enabled=true)
  • خط ۲۳: برچسب دکمه به «Stop» تغییر می‌کند.
  • خط ۲۷: اگر برچسب «Stop» باشد
  • خط ۲۹: تایمر متوقف می‌شود (Enabled=false)
  • خط ۳۱: برچسب دکمه به «شروع» تغییر می‌کند.

ما همچنان باید رویداد Tick را روی شیء timer1 مدیریت کنیم، رویدادی که هر ثانیه رخ می‌دهد:


private void timer1_Tick(object sender, EventArgs e) {
            // یک ثانیه سپری شده است
            DateTime maintenant = DateTime.Now;
            TimeSpan durée = maintenant - début;
            // تایمر را به‌روزرسانی کن
            labelChrono.Text = durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2");
        }
  • خط ۳: ثبت زمان فعلی
  • خط ۴: ما زمان سپری‌شده از زمان شروع تایمر را محاسبه می‌کنیم. این یک شیء از نوع TimeSpan را بازمی‌گرداند که نشان‌دهنده یک بازه زمانی است.
  • خط ۶: این باید در تایمر با فرمت hh:mm:ss نمایش داده شود. برای این کار از خواص Hours و Minutes استفاده می‌کنیم، Seconds از شیء TimeSPan، که به ترتیب نمایانگر ساعت، دقیقه و ثانیهٔ مدت‌زمان نمایش‌داده‌شده است، در قالب ToString("d2") به کار می‌رود تا نمایش دو رقمی تضمین شود.

7.6. برنامهٔ نمونه – نسخهٔ ۶

به مثال برنامه IMPOTS بازمی‌گردیم. آخرین نسخه در بخش 6.4 مورد بحث قرار گرفت. این برنامه سه‌لایه زیر بود:

  • لایه‌های [metier] و [dao] در داخل DLL محصور شده بودند
  • لایه [ui] یک لایه [console] بود
  • اسپرینگ مسئول ایجاد نمونه‌های لایه‌ها و ادغام آن‌ها در برنامه بود.

در این نسخه جدید، لایه [ui] توسط رابط کاربری گرافیکی زیر پیاده‌سازی خواهد شد:

 

7.6.1. راه حل ویژوال استودیو

راه‌حل ویژوال استودیو شامل اجزای زیر است:

  • [1]: پروژه شامل عناصر زیر است:
  • [Program.cs]: کلاسی که برنامه را راه‌اندازی می‌کند
  • [Form1.cs]: کلاس فرم اول
  • [Form2]: کلاس مربوط به فرم دوم
  • [lib]، با جزئیات در [2]: این شامل تمام فایل‌های DLL مورد نیاز برای پروژه است:
  • [ImpotsV5-dao.dll]: DLL برای لایه [dao] که در بخش 6.4.3 تولید شده است؛
  • [ImpotsV5-metier.dll]: DLL از لایه [dao] تولید شده در بند 6.4.4؛
  • [Spring.Core.dll]، [Common.Logging.dll]، [antlr.runtime.dll]: فایل‌های Spring DLL که در نسخه قبلی قبلاً استفاده شده‌اند (به بخش 6.4.6 مراجعه کنید).
  • [references]، با جزئیات در [3]: مراجع پروژه. برای هر یک از فایل‌های DLL در پوشه [lib] یک مرجع اضافه شده است.
  • [App.config]: فایل پیکربندی پروژه. این فایل با آنچه در نسخه قبلی در بخش 6.4.6 توصیف شده یکسان است؛
  • [DataImpot.txt]: فایل رده مالیاتی که پیکربندی شده تا به طور خودکار در پوشه اجرای پروژه [4] کپی شود.

فرم [Form1] فرم وارد کردن پارامترها برای محاسبه مالیات [A] است، همانطور که در بالا توضیح داده شد. فرم [Form2] [B] برای نمایش یک پیام خطا استفاده می‌شود:

7.6.2. کلاس [Program.cs]

کلاس [Program.cs] برنامه را اجرا می‌کند. کد آن به شرح زیر است:


using System;
using System.Windows.Forms;
using Spring.Context;
using Spring.Context.Support;
using Metier;
using System.Text;

namespace Chap5 {
    static class Program {
        /// <summary>
        /// نقطه ورود اصلی برنامه.
        /// </summary>
        [STAThread]
        static void Main() {
            // کد تولید شده توسط ویژوال استودیو
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            // --------------- کد توسعه‌دهنده
            // نمونه‌هایی از لایه‌های [metier] و [dao]
            IApplicationContext ctx = null;
            Exception ex = null;
            IImpotMetier metier = null;
            try {
                // زمینهٔ Spring
                ctx = ContextRegistry.GetContext();
                // یک مرجع در لایه [metier] درخواست شده است
                metier = (IImpotMetier)ctx.GetObject("metier");
            } catch (Exception e1) {
                //ثبت خطا
                ex = e1;
            }
            // فرم برای نمایش
            Form form = null;
            //آیا استثنا وجود داشت؟
            if (ex != null) {
                // بله – ایجاد پیام خطا برای نمایش
                StringBuilder msgErreur = new StringBuilder(String.Format("Chaîne des exceptions : {0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
                Exception e = ex;
                while (e != null) {
                    msgErreur.Append(String.Format("{0}: {1}{2}", e.GetType().FullName, e.Message, Environment.NewLine));
                    msgErreur.Append(String.Format("{0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
                    e = e.InnerException;
                }
                // ایجاد پنجره خطا که پیام خطا به آن ارسال می‌شود
                Form2 form2 = new Form2();
                form2.MsgErreur = msgErreur.ToString();
                // این پنجره برای نمایش خواهد بود
                form = form2;
            } else {
                //همه‌چیز خوب پیش رفت
                //ایجاد رابط گرافیکی [Form1] است که مرجع آن از طریق لایه [metier] ارسال می‌شود
                Form1 form1 = new Form1();
                form1.Metier = metier;
                //این پنجره‌ای است که نمایش داده می‌شود
                form = form1;
            }
            //پنجره نمایش
            Application.Run(form);
        }
    }
}

کد تولیدشده توسط ویژوال استودیو از خط ۱۹ به بعد گسترش یافته است. این برنامه از [App.config] های زیر استفاده می‌کند:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object name="dao" type="Dao.FileImpot, ImpotsV5-dao">
                <constructor-arg index="0" value="DataImpot.txt"/>
            </object>
            <object name="metier" type="Metier.ImpotMetier, ImpotsV5-metier">
                <constructor-arg index="0" ref="dao"/>
            </object>
        </objects>
    </spring>
</configuration>
  • خطوط ۲۴–۳۲: استفاده از فایل قبلی [App.config] برای ایجاد لایه‌های [metier] و [dao]
  • خط ۲۶: پردازش فایل [App.config]
  • خط ۲۸: بازیابی یک مرجع به لایه [metier]
  • خط ۳۱: ثبت هر استثنا
  • خط ۳۴: مرجع form فرم نمایش داده شده را مشخص می‌کند (form1 یا form2)
  • خطوط ۳۶–۵۰: اگر استثنایی رخ داده باشد، آمادگی‌ها برای نمایش فرم از نوع [Form2] فراهم می‌شود
  • خطوط ۳۸–۴۴: پیام خطا برای نمایش ساخته می‌شود. این پیام شامل الحاق پیام‌های خطای مربوط به استثناءهای مختلف موجود در زنجیره استثناء است.
  • خط ۴۶: یک فرم از نوع [Form2] ایجاد می‌شود.
  • خط ۴۷: همان‌طور که بعداً خواهیم دید، این فرم دارای یک ویژگی عمومی به نام MsgErreur است که پیام خطایی است که باید نمایش داده شود:

        public string MsgErreur { private get; set; }

این ویژگی تنظیم شده است.

  • خط ۴۹: مرجع form که پنجرهٔ نمایش‌داده‌شده را مشخص می‌کند، مقداردهی اولیه می‌شود. به چندریختی (polymorphism) که در اینجا به کار رفته توجه کنید. form2 از نوع [Form] نیست، بلکه از نوع [Form2] است، که نوعی مشتق شده از [Form] می‌باشد.
  • خطوط ۵۰–۵۷: هیچ استثنایی پرتاب نشده است. در حال آماده‌سازی برای نمایش فرم از نوع [Form1] است.
  • خط ۵۳: یک فرم از نوع [Form1] ایجاد می‌شود.
  • خط ۵۴: همان‌طور که بعداً خواهیم دید، این فرم دارای یک ویژگی عمومی Metier است که ارجاعی به لایه [metier] است:

                public IImpotMetier Metier { private get; set; }

ما این ویژگی را مقداردهی می‌کنیم.

  • خط ۵۶: ارجاع form که پنجرهٔ نمایش‌داده‌شده را مشخص می‌کند، مقداردهی اولیه می‌شود. بار دیگر به چندریختی (polymorphism) در عمل توجه کنید. form1 از نوع [Form] نیست، بلکه از نوع [Form1] است، که نوعی مشتق شده از [Form] می‌باشد.
  • خط ۵۹: پنجره‌ای که توسط form ارجاع شده است، نمایش داده می‌شود.

7.6.3. فرم [Form1]

در حالت [conception]، فرم [Form1] به شرح زیر است:

کنترل‌ها به شرح زیر هستند

خیر.
نوع
نام
نقش
0
GroupBox
groupBox1
متن=آیا متأهل هستید؟
1
RadioButton
radioButtonOui
برای متأهل بودن تیک خورده
۲
RadioButton
radioButtonNon
تیک خورده اگر متأهل نیست
تیک خورده=True
۳
NumericUpDown
numericUpDownEnfants
تعداد فرزندان مؤدی
حداقل=0، حداکثر=20، گام=1
4
TextBox
textSalaire
حقوق سالانه مالیات‌دهنده به یورو
5
برچسب
labelImpot
مبلغ مالیات پرداختنی
BorderStyle=Fixed3D
۶
دکمه
buttonCalculer
محاسبه مالیات
7
دکمه
buttonEffacer
فرم را به حالتی که هنگام بارگذاری داشت، بازنشانی می‌کند
8
دکمه
buttonQuitter
برای خروج از برنامه

قوانین رفتار فرم

  • دکمه Calculer تا زمانی که فیلد حقوق خالی باشد، غیرفعال باقی می‌ماند
  • اگر هنگام اجرای محاسبه مشخص شود که حقوق نادرست است، خطایی گزارش می‌شود [9]

کد کلاس به شرح زیر است:


using System.Windows.Forms;
using Metier;
using System;

namespace Chap5 {
    public partial class Form1 : Form {
        //لایه [métier]
        public IImpotMetier Metier { private get; set; }

        public Form1() {
            InitializeComponent();
        }

        private void buttonCalculer_Click(object sender, System.EventArgs e) {
            //آیا حقوق صحیح است
            int salaire;
            bool ok=int.TryParse(textSalaire.Text.Trim(), out salaire);
            if (! ok  || salaire < 0) {
                // پیام خطا
                MessageBox.Show("Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
                //بازگشت به فیلد نادرست
                textSalaire.Focus();
                // انتخاب متن در فیلد ورودی
                textSalaire.SelectAll();
                //بازگشت به رابط ورودی
                return;
            }
            // حقوق صحیح است – مالیات قابل محاسبه است
            labelImpot.Text = Metier.CalculerImpot(radioButtonOui.Checked, (int)numericUpDownEnfants.Value, salaire).ToString();
        }

        private void buttonQuitter_Click(object sender, System.EventArgs e) {
            Environment.Exit(0);
        }

        private void buttonEffacer_Click(object sender, System.EventArgs e) {
            //پاک کردن فرم
            labelImpot.Text = "";
            numericUpDownEnfants.Value = 0;
            textSalaire.Text = "";
            radioButtonNon.Checked = true;
        }

        private void textSalaire_TextChanged(object sender, EventArgs e) {
            //وضعیت دکمه [Calculer]
            buttonCalculer.Enabled=textSalaire.Text.Trim()!="";
        }

    }
}

ما فقط بخش‌های مرتبط را توضیح می‌دهیم:

  • خط [8]: ویژگی عمومی Metier، که به کلاس راه‌انداز [Program.cs] اجازه می‌دهد مرجعی به لایه [metier] را در [Form1] تزریق کند.
  • خط [14]: رویه محاسبه مالیات
  • خطوط ۱۵–۲۷: بررسی اعتبار حقوق (یک عدد صحیح ≥ ۰).
  • خط ۲۹: محاسبه مالیات با استفاده از روش [CalculerImpot] از لایه [metier]. به سادگی این عملیات توجه کنید که با قرار دادن لایه [metier] درون لایه DLL به دست آمده است.

7.6.4. فرم [Form2]

در حالت [conception]، فرم [Form2] به شرح زیر است:

چک‌ها به شرح زیر هستند

خیر.
نوع
نام
نقش
1
TextBox
textBoxErreur
چندخطی=True، نوارهای پیمایش=هر دو

کد کلاس به شرح زیر است:


using System.Windows.Forms;

namespace Chap5 {
    public partial class Form2 : Form {
        // پیام خطا
        public string MsgErreur { private get; set; }

        public Form2() {
            InitializeComponent();
        }

        private void Form2_Load(object sender, System.EventArgs e) {
            // نمایش پیام خطا
            textBoxErreur.Text = MsgErreur;
            // لغو انتخاب تمام متن
            textBoxErreur.Select(0, 0);
        }
    }
}
  • خط ۶: ویژگی عمومی MsgErreur که به کلاس راه‌اندازی [Program.cs] اجازه می‌دهد پیام خطا را برای نمایش به [Form2] تزریق کند. این پیام هنگام پردازش رویداد Load، خطوط ۱۲–۱۶ نمایش داده می‌شود.
  • خط ۱۴: پیام خطا در TextBox قرار می‌گیرد
  • خط ۱۶: انتخاب انجام‌شده در عملیات قبلی حذف می‌شود. [TextBox].Select(start, length) کاراکترهای longueur را از کاراکتر با شماره début انتخاب (هایلایت) می‌کند. [TextBox].Select(0,0) معادل لغو انتخاب تمام متن است.

7.6.5. نتیجه‌گیری

بیایید معماری سه‌سطحی مورد استفاده را مجدداً بررسی کنیم:

این معماری به ما امکان داد تا پیاده‌سازی کنسولی لایه موجود [ui] را با یک پیاده‌سازی گرافیکی جایگزین کنیم، بدون اینکه هیچ تغییری در لایه‌های [metier] و [dao] ایجاد کنیم. ما توانستیم بدون نگرانی از هرگونه تأثیر احتمالی بر لایه‌های دیگر، بر لایه [ui] تمرکز کنیم. این مزیت اصلی معماری‌های سه‌لایه است. بعداً مثال دیگری از این را خواهیم دید، زمانی که لایه [dao]—که در حال حاضر داده‌ها را از یک فایل متنی پردازش می‌کند—با یک لایه [dao] که داده‌ها را از یک پایگاه داده پردازش می‌کند، جایگزین شود. خواهیم دید که این کار بدون تأثیر بر لایه‌های [ui] و [metier] انجام خواهد شد.