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+ (true)، GDI (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]: برگهٔ ویژگیهای پنجره در گوشهٔ پایینراست ویژوال استودیو ظاهر میشود
از جمله ویژگیهای قابل توجه فرم عبارتند از:
برای تنظیم رنگ پسزمینه پنجره | |
برای تنظیم رنگ گرافیک یا متن روی پنجره | |
برای مرتبط کردن یک منو با پنجره | |
برای دادن عنوان به پنجره | |
برای تعیین نوع پنجره | |
برای تنظیم قلم متن در پنجره | |
برای تعیین نام پنجره |
در اینجا، ما ویژگیهای Text و Name را تنظیم میکنیم:
میدانهای ورودی و دکمهها - ۱ | |
frmSaisiesBoutons |
![]() |
- [1]: ابزارک [Common Controls] را از میان ابزارکهای ارائهشده توسط ویژوال استودیو انتخاب کنید
- [2, 3, 4]: بهترتیب روی کامپوننتهای [Label]، [Button] و [TextBox] دوبار کلیک کنید
- [5]: سه کامپوننت روی فرم قرار دارند
برای تراز و تغییر اندازه صحیح کامپوننتها، میتوانید از گزینههای نوار ابزار استفاده کنید:
![]() | |||||||||
قالببندی به شرح زیر انجام میشود:
- اجزای مختلف را که میخواهید با هم قالببندی شوند، انتخاب کنید (کلید Ctrl را نگه دارید و همزمان روی اجزا کلیک کنید)
- نوع قالببندی مورد نظر را انتخاب کنید:
- (ادامه)
- گزینههای 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);
با
پیامی که باید نمایش داده شود | |
عنوان پنجره | |
دکمهها در پنجره | |
آیکون نمایش داده شده در پنجره |
پارامتر buttons میتواند مقادیری از ثابتهای زیر (که با MessageBoxButtons همانطور که در خط ۷ نشان داده شده است) بگیرد:
دکمهها | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
پارامتر icon میتواند مقادیری از ثابتهای زیر (که با MessageBoxIcon پیشوند شدهاند، همانطور که در خط ۱۰ نشان داده شده) داشته باشد:
![]() | همان متوقف | ||
همان هشدار | ![]() | ||
همانند Asterisk | ![]() | ||
![]() | همان دست | ||
![]() |
متد Show یک متد ایستا است که نتیجهای از نوع [System.Windows.Forms.DialogResult] را برمیگرداند، که یک شمارشپذیر است:

برای اینکه بفهمیم کاربر کدام دکمه را برای بستن پنجره از نوع MessageBox کلیک کرده است، مینویسیم:
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 مطابقت دارد، میتوانیم بنویسیم:
یک متغیر از نوع delegate در واقع یک لیست از ارجاعات به متدهای نوع delegate است. برای افزودن یک متد جدید M به متغیر evtHandler فوق، از سینتکس زیر استفاده میشود:
نشانهگذاری += میتواند حتی زمانی که 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] خواهیم داشت که به این صورت تعریف شده است:
delegate و MouseEventHandler به صورت زیر تعریف شدهاند:
![]() |
این یک تابع نماینده با امضای void f(object, MouseEventArgs) است. آیا کلاس MouseEventArgs توسط تعریف شده است:
![]() |
کلاس MouseEventArgs امکانات بیشتری نسبت به کلاس EventArgs دارد. برای مثال، میتوان مختصات X و Y ماوس را در لحظه وقوع رویداد دریافت کرد.
7.1.2.3. Conclusion
از دو پروژه مورد مطالعه، میتوان نتیجه گرفت که پس از ساخت رابط کاربری گرافیکی با استفاده از ویژوال استودیو، کار توسعهدهنده عمدتاً شامل نوشتن رویدادپردازها برای رویدادهایی است که میخواهد در آن رابط مدیریت کند. کد به طور خودکار توسط ویژوال استودیو تولید میشود. این کد که میتواند پیچیده باشد، در ابتدا میتوان از آن چشمپوشی کرد. در مرحله بعد، مطالعه آن ممکن است درک بهتری از ایجاد و مدیریت فرمها فراهم کند.
7.2. کامپوننتهای پایه
اکنون برنامههای مختلفی را که از رایجترین کامپوننتها استفاده میکنند، ارائه میدهیم تا روشها و ویژگیهای اصلی آنها را بررسی کنیم. برای هر برنامه، رابط کاربری گرافیکی و کد مربوطه، عمدتاً کدهای هندلرهای رویداد، را ارائه خواهیم داد.
7.2.1. فرم
ما با معرفی مؤلفه ضروری شروع میکنیم: فرم (Form) که مؤلفهها روی آن قرار میگیرند. ما قبلاً برخی از ویژگیهای پایهای آن را پوشش دادهایم. در اینجا، بر چند رویداد مهم مرتبط با فرم تمرکز میکنیم.
فرم در حال بارگذاری است | |
فرم در حال بسته شدن است | |
فرم بسته شد |
رویداد 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، ویژگیهای کنترل زیر را تنظیم کنید:
برای امکان چند خطی بودن متن | |
برای مشخص کردن اینکه آیا کنترل باید نوار پیمایش (افقی، عمودی، هر دو) داشته باشد یا خیر (بدون) | |
اگر روی true تنظیم شود، فشردن کلید Enter به خط بعدی میرود | |
اگر روی 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 تعیین میشوند:
فهرست غیر کشویی با کادر ویرایش | |
لیست کشویی با کادر ویرایش | |
لیست کشویی بدون کادر ویرایش |
به طور پیشفرض، نوع یک ComboBox، DropDown است.
کلاس ComboBox دارای یک سازنده است:
یک لیست کشویی خالی ایجاد میکند |
عناصر ComboBox در خصوصیت Items در دسترس هستند:
این یک خاصیت نمایهگذاریشده است، که در آن Items[i] به آیتم i-ام در کادر ترکیبی اشاره میکند. این خاصیت فقط-خواندنی است.
فرض کنید C یک کادر ترکیبی و C.Items فهرست موارد آن باشد. ما ویژگیهای زیر را داریم:
تعداد آیتمها در کادر ترکیبی | |
عنصر i از کادر ترکیبی | |
شیء o را به عنوان آخرین عنصر کامبو اضافه میکند | |
یک آرایه از اشیاء را به انتهای کامبو اضافه میکند | |
شیء o را در موقعیت i در کامبو اضافه میکند | |
عنصر i را از لیست کشویی حذف میکند | |
حذف شیء o از لیست کشویی | |
حذف تمام موارد از لیست ترکیبی | |
موقعیت شیء o را در کادر ترکیبی روی i تنظیم میکند | |
شاخص عنصر انتخابشده | |
عنصر انتخابشده | |
متن نمایش داده شده عنصر انتخابشده | |
متن نمایش داده شده برای عنصر انتخاب شده |
شاید تعجبآور باشد که یک کادر ترکیبی میتواند اشیاء را در خود داشته باشد، در حالی که از نظر بصری، رشتهها را نمایش میدهد. اگر یک 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 آنها تعریف میشود:
فقط یک عنصر میتواند انتخاب شود | |
چندگزینی امکانپذیر است: با نگه داشتن کلید SHIFT و کلیک بر روی یک عنصر، انتخاب از عنصر انتخابشده قبلی تا عنصر فعلی گسترش مییابد. | |
انتخاب چندگانه امکانپذیر است: یک مورد با کلیک ماوس یا فشردن کلید اسپیس انتخاب یا لغو انتخاب میشود. |
- کاربر متن را در فیلد 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) رخ میدهند
موش به تازگی وارد ناحیه کنترل شده است | |
موش به تازگی محدوده کنترل را ترک کرده است | |
موش در داخل ناحیه کنترل در حال حرکت است | |
دکمه چپ ماوس فشرده شده | |
رها شدن دکمه چپ ماوس | |
کاربر یک شیء را روی کنترل میاندازد | |
کاربر یک شیء را به داخل ناحیه کنترل میکشد | |
کاربر یک شیء را از ناحیه کنترل بیرون میکشد | |
کاربر هنگام کشیدن یک شیء، ماوس را بر روی ناحیه کنترل نگه میدارد |
در اینجا یک برنامه کاربردی برای کمک به شما جهت درک بهتر زمان وقوع رویدادهای مختلف ماوس آورده شده است:
![]() |
خیر. | نوع | نام | نقش |
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 به شرح زیر است:
![]() |
در مثال ما از دو ویژگی استفاده شده است:
نام کنترل منو | |
برچسب گزینهٔ منو |
در ساختار منو، گزینه 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 | نوع | نقش |
Propriété | انواع فایلهای موجود در فهرست کشویی نوع فایل در کادر محاورهای | |
Propriété | شمارهٔ پیشفرض نوع فایل در فهرست بالا. از ۰ شروع میشود. | |
Propriété | پوشهای که در ابتدا برای ذخیره فایل مشخص شده است | |
Propriété | نام فایل پشتیبان مشخصشده توسط کاربر | |
Méthode | روشی که کادر محاورهای ذخیره را نمایش میدهد. مقداری از نوع DialogResult را برمیگرداند. |
روش ShowDialog یک کادر محاورهای مشابه شکل زیر را نمایش میدهد:
![]() |
فهرست کشویی تولید شده از خاصیت Filter. نوع فایل پیشفرض توسط FilterIndex تنظیم میشود. | |
نام فایل انتخابشده یا مستقیماً واردشده توسط کاربر. در خصوصیت FileName در دسترس خواهد بود. | |
دکمههای ذخیره/لغو. اگر از دکمه 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 یک کادر محاورهای مشابه شکل زیر را نمایش میدهد:
![]() |
لیست کشویی تولید شده از خاصیت Filter. نوع فایل پیشفرض توسط FilterIndex تعیین میشود. | |
نام فایلی که توسط کاربر انتخاب یا مستقیماً وارد شده است. این نام در ویژگی FileName در دسترس خواهد بود. | |
دکمههای باز/لغو. اگر دکمه 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] موجود است:
![]() |
ویژگیهای کامپوننت تایمر مورد استفاده در اینجا به شرح زیر خواهد بود:
تعداد میلیثانیهها پس از آن که رویداد Tick فعال میشود. | |
رویدادی که در پایان Interval میلیثانیه فعال میشود | |
تایمر را روی فعال (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] انجام خواهد شد.


































































