کنترل کننده رويداد (event Handler)

رابط گرافیکی کاربر در دات نت و ویژوال سی شارپ از مکانیزم کنترل کننده رویداد برای کنترل رویدادها که در هنگام اجرای برنامه به وقوع می‌پیوندند استفاده می‌کند. رویدادها رفتارهایی یا اتفاقاتی هستند که هنگام اجرای برنامه به وقوع می‌پیوندند. کنترل رویداد فرایند نظارت بر وقوع یک رویداد مشخص می‌باشد. فرم‌های ویندوزی از کنترل کننده رویداد برای اضافه کردن یک قابلیت و پاسخ به کاربر استفاده می‌کنند. بدون کنترل کننده رویداد، فرم‌ها و رابط گرافیکی تا حد زیادی بدون استفاده هستند. در این درس فرض بر این است که شما بر مفاهیم delegates و events (رویداد) تسلط دارید. رویدادها با استفاده از یک delegate به عنوان یک نوع تعریف می‌شوند.delegate ها آدرس متدها را در خود ذخیره می‌کنند. در مثال زیر یک delegate و یک event تعریف شده‌اند.

public delegate void SampleEventHandler(int);

public event SampleDelegate SampleEvent;

بر اساس تعریف بالا delegate آدرس متدهایی را قبول می‌کند که دارای مقدار برگشتی نیستند و یک آرگومان ساده از نوع int قبول می‌کنند. سپس از این نوع delegate برای ایجاد event مان استفاده می‌کنیم. حال یک کنترل کننده رویداد به رویداد اضافه می‌کنیم. همانطور که قبلاً اشاره شد، هر رویداد دارای یک delegate مخصوص به خود است که این delegate دارای یک یا چند متد می‌باشد. کنترل کننده‌های رویداد متدهایی هستند که امضای آنها همانند امضای این delegate است و هنگام وقوع رویداد اجرا می‌شوند. آن‌ها (کنترل کننده‌های رویداد) چسبیده به یک event هستند و هنگامی که رویداد به وقوع می‌پیوندد اجرا می‌شوند. می‌توان چندین کنترل کننده‌های رویداد را به یک event متصل کرد تا هنگام وقوع رویداد اجرا شوند. برای این کار ابتدا باید کنترل کننده رویداد را ایجاد کنید. بعد از ایجاد، مطمئن شوید که امضای آن با امضای delegate ی که رویداد از آن استفاده می‌کند مطابق باشد. به عنوان مثال delegate مثال بالا دارای نوع برگشتی void بوده و یک پارامتر از نوع int دریافت می‌کند، پس کنترل کننده رویداد ما نیز باید دارای نوع برگشتی void بوده و یک پارامتر از نوع int دریافت کند (به این نکته توجه کنید که سطح دسترسی مهم نیست).

public void ShowMessage(int number)
{
    MessageBox.Show("Hello World");
}

سپس می‌توان با استفاده از عملگر =+ یک رویداد را متصل کرد :

SampleEvent += new SampleEventHandler(ShowMessage);

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

ShowMessage(3);

کنترل کننده رویداد در فرم‌های ویندوزی

قبل از توضیح کنترل کننده رویداد در ویندوز فرم ابتدا ارتباط بین کنترل کننده رویداد، delegate و رویداد را دوباره یادآوری می‌کنیم :

جمله بالا در کد زیر خلاصه می‌شود :

SampleEvent += new SampleDelegate(SampleEventHandler);

به این نکته هم اشاره کنیم که در سی شارپ تعداد زیادی event و delegate از پیش تعریف شده وجود دارد که ویژوال استودیو مسئولیت اداره آنها را به عهده دارد. برای شرح استفاده از رویدادها در فرم‌های ویندوزی، یک فرم ویندوزی جدید ایجاد کرده و نام آن را eventHandling بگذارید. بر روی فرم دوبار کلیک کرده تا ویژوال استودیو به صورت خودکار یک کنترل کننده رویداد ایجاد کرده و آن را به رویداد Load فرم متصل کند. به شکل زیر توجه کنید :

همانطور که در شکل بالا مشاهده می‌کنید، یک کنترل کننده رویداد به نام Form1_Load به طور خودکار ایجاد می‌شود، که دارای دو پارامتر است. در مورد این دو پارامتر در ادامه توضیح می‌دهیم. نکته‌ای که بهتر است در همین جا به آن اشاره کنیم نحوه نامگذاری کنترل کننده‌های رویداد توسط ویژوال استودیو است. در ویژوال استودیو کنترل کننده‌های رویداد بر طبق یک قرار داد نامگذاری می‌شوند. هنگام نامگذاری آن‌ها ابتدا نام کنترل (که در خاصیت Name کنترل مشخص شده است)، سپس علامت زیر خط و بعد از آن نام رویداد می‌آید مانند : Form1_Load. می‌توان این قرارداد را زمانی که کنترل کننده رویداد به وسیله چند رویداد مورد استفاده قرار می‌گیرد، نادیده گرفت که در ادامه توضیح داده خواهد شد. همانطور که گفتیم ویژوال استودیو به صورت خودکار یک کنترل کننده رویداد ایجاد کرده و آن را به رویداد Load فرم متصل می‌کند. برای مشاهده رویداد ایجاد شده و اتصال آن به کنترل کننده رویداد کافی است که در پنجره Solution Explorer بر روی Form1.Designer.cs دوبار کلیک کنید. به شکل زیر توجه کنید :

همانطور که در شکل بالا مشاهده می‌کنید، رویداد Load دارای یک delegate از نوع EventHandler است که کنترل کننده رویداد Form1_Load در داخل آن قرار می‌گیرد. ویژوال استودیو با استفاده از عملگر =+ این کنترل کننده را که قرار است هنگام وقوع رویداد Load اجرا شود را به رویداد معرفی می‌کند. بیشتر رویدادهای کنترل‌ها یک delegate از نوع System.eventHandler دارند. مثال زیر تعریفی از دلیگیت eventHandler می‌باشد:

public delegate void EventHandler(object sender, EventArgs e)

همانطور که مشاهده می‌کنید،delegate مثال بالا دارای مقدار برگشتی نیست و دارای دو پارامتر است، یکی object و دیگری یک نمونه از eventArgs.

پارامتر object sender

object sender نشان دهنده کنترلی است که رویداد را فعال می‌کند و ویژوال استودیو از این آرگومان برای تشخیص کنترلی که رویداد مربوط به آن است استفاده می‌کند. (بعداً توضیح داده می‌شود). از آن جایی که نوع این پارامتر object است، هر کنترلی می‌تواند منبع ارسال رویداد باشد چون هر کنترل یک شیء است که از کلاس پایه object مشتق می‌شود. از آنجایی که ارسال کننده رویداد (کنترل) به نوع object تبدیل می‌شود، برای استفاده مجدد از خصوصیات کنترل باید آن را با استفاده از عمل cast تبدیل کنیم (تولید کنیم). برای روشن شدن مطلب به مثال زیر توجه کنید. ابتدا یک کنترل button را روی فرم قرار دهید و سپس خاصیت Text آن را به “Hello World!” تغییر دهید.
event-handling-01
بر روی کنترل دوبار کلیک کنید تا یک کنترل کننده رویداد برای رویداد کلیک آن ایجاد شود. مانند رویداد Load، رویداد Click نیز یک کنترل کننده رویداد دارد. کد زیر را در داخل کنترل کننده رویداد بنویسید :

private void button1_Click(object sender, EventArgs e)
{
    Button source = (Button)sender;
    MessageBox.Show("The message inside the button is " + source.Text);
}

برنامه را اجرا کرده و بر روی دکمه کلیک کنید. یک جعبه متن نمایش داده خواهد شد. این متن هنگام وقوع رویداد Click نمایش داده می‌شود.
event-handling-02
خط اول کنترل کننده رویدادشی sender را با استفاده از عمل cast به یک دکمه تبدیل می‌کند بنابراین می‌توانیم به خاصیت Text دکمه دسترسی داشته باشیم. سپس متد Show از کلاس MessageBox را برای نشان دادن مقدار خاصیت Text دکمه که به رویداد فرستاده شده است، فراخوانی می‌کنیم.

پارامتر eventArgs

این پارامتر نمونه‌ای از کلاس eventArgs می‌باشد و می‌توان آن را به عنوان آرگومان رویداد در نظر گرفت که حاوی دادهایی در مورد رویدادی که اتفاق افتاده است می‌باشد . eventArgs در اصل یک کلاس پایه است و دارای اعضای مفیدی نمی‌باشد. این پارامتر در برخی از کنترل کننده‌های رویداد متفاوت است. مثلاً در رویداد Click این آرگومان EventArgs و در رویداد MouseClick به صورت MouseEventArgs می‌باشد. همانطور که گفتیم، کلاس پایه eventArgs در هیچ خاصیتی که شما بتوانید در کنترل کننده رویداد خود از آن استفاده کنید ندارد :
event-handling-09
اما MouseEventArgs دارای خواص مفیدی از جمله اینکه کدام دکمه ماوس فشار داده شده، تعداد کلیک‌ها، مقدار چرخش ماوس و مختصات مکانی که رویداد کلیک در آن اتفاق افتاده است می‌باشد :
event-handling-10
رویداد MouseClick از کنترل button است که نسخه بهتری از رویداد Click می‌باشد. از آنجایی که این رویداد (MouseClick) رویداد پیشفرض نیست، برای دسترسی به آن باید از بخش events از پنجره Properties اقدام نمایید. مطمئن شوید که کنترل button در قسمت طراحی فرم انتخاب شده است. بر روی آیکون events ( که به شکل جرقه است) کلیک کنید. ابتدا کنترل کننده رویداد Click را حذف کنید تا با این رویداد (MouseClick) تداخل پیدا نکند. برای حذف رویداد Click از پنجره Properties بر روی آیکون جرقه کلیک کرده و از لیست رویدادها، رویداد Click را پیدا کرده و سپس بر روی آن راست کلیک و سپس دکمه Reset را بزنید :
event-handling-06
سپس رویداد MouseClick را یافته و بر روی آن دوبار کلیک کرده و کد زیر را در داخل کنترل کننده رویداد MouseClick آن بنویسید :

private void button1_MouseClick(object sender, MouseEventArgs e)
{
    MessageBox.Show("You clicked at point (" + e.X + ", " + e.Y + ")");
}

کد بالا با استفاده از پارامتر event argument به مختصات x و y نقطه‌ای که با ماوس بر روی آن کلیک شده است دست می‌باید. خروجی زیر بستگی به این دارد که شما بر روی چه نقطه‌ای از دکمه کلیک کنید.
event-handling-03
انواع زیادی event argument وجود دارد که هر کدام خواص مفیدی برای رویدادی که اتفاق می افتد پیشنهاد می‌دهند.

استفاده از پنجره خواص برای اضافه کردن کنترل کننده رویداد

همانطور که می دانید صرف نظر از دوبار کلیک کردن بر روی کنترل‌ها برای ایجاد کنترل کننده رویداد می‌توانیم از پنجره Properties برای این کار نیز استفاده کنیم. بر روی آیکونی که شبیه به یک جرقه است کلیک کنید تا وارد بخش رویدادهای (Events) پنجره Properties شوید.
event-handling-04
از منوی باز شونده‌ای که با فلش قرمز در شکل بالا نشان داده شده است برای انتخاب کنترل‌ها در محیط طراحی استفاده می‌شود. استفاده از این منو زمانی مفید است که شما بخواهید یک کنترل غیر قابل رویت و یا بسیار کوچک را انتخاب کنید. رویداد مورد نظر را پیدا کنید. برای اضافه کردن کنترل کننده رویداد به آن می‌توانید بر روی نام رویداد دوبار کلیک کنید تا کنترل کننده رویداد مربوط به آن ایجاد شود. همچنین اگر از قبل کنترل کننده‌های رویداد دیگری نیز ایجاد کرده‌اید می‌توانید مانند شکل زیر به آنها دسترسی داشته باشید.
event-handling-05

به این نکته توجه کنید که نام برخی از رویدادها در داخل نام رویدادهای پنجره Properties نیست. رویداد MouseWheel یکی از این رویدادهاست. برای دسترسی به چنین رویدادهایی می‌توانید از محیط کدنویسی و قابلیت IntelliSense استفاده کنید. البته این کار نیاز به کمی مهارت و یا حتی جستجو در بین سایت‌های مختلف دارد. ولی قواعد کلی همان است که در بالا اشاره شد. به کد زیر توجه کنید :

using System.Windows.Forms;

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

            this.button1.MouseWheel += new MouseEventHandler(this.button1_MouseWheel);            
        }

        private void button1_MouseWheel(object sender, MouseEventArgs e)
        {
            this.button1.Width += e.Delta;
        }
    }
}

MouseWheel رویداد مربوط به حرکت دکمه وسط ماوس به طرف بالا و پایین است. همانطور که در کد بالا مشاهده می‌کنید کافیست که در قسمت سازنده form1 این رویداد را به مثلاً دکمه اضافه کنیم. سپس یک کنترل کننده رویداد (button1_MouseWheel) به آن وصل کنیم. کدهای بدنه کنترل کننده رویداد باعث می‌شوند که با چرخاندن دکمه وسط ماوس به سمت بالا و پایین عرض دکمه‌ی روی فرم کم و زیاد شود.

استفاده از یک کنترل کننده رویداد برای چندین رویداد

به این نکته توجه کنید که می‌توان از یک کنترل کننده رویداد برای چندین رویداد که دارای نماینده (delegate) یکسانی هستند، استفاده کرد. برای انجام این کار لازم است که یک کنترل کننده رویداد با یک امضای مناسب ایجاد کنید. مثلاً رویدادهای click و Load دارای امضاهای یکسانی هستند، پس می‌توان یک کنترل کننده رویداد را به این دو رویداد اختصاص داد. یک دکمه بر روی فرم قرار داده و سپس با زدن دکمه F7 به محیط کدنویسی رفته و کد زیر را بنویسید :

private void myEventHandler(object sender, EventArgs e)
{
   MessageBox.Show("This is myEventHandler!");              
}

بعد از ایجاد کنترل کننده رویداد بالا (myEventHandler)، با استفاده از عملگر =+ آن را به رویداد مورد نظرتان اختصاص دهید. فرض کنید که می‌خواهید قبل از اینکه فرم بالا بیاید و یا بر روی دکمه کلیک شود، این کنترل کننده رویداد اجرا شود. برای این منظور کنترل کننده رویداد را به صورت زیر به رویدادهای Load فرم و Click دکمه اختصاص دهید :

using System;
using System.Windows.Forms;

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

            this.Load     += new EventHandler(myEventHandler);
            button1.Click += new EventHandler(myEventHandler);
        }

        private void myEventHandler(object sender, EventArgs e)
        {
            MessageBox.Show("This is myEventHandler!");
        }
    }
}

قبلاً ذکر شد که می‌توان قرارداد نامگذاری کنترل کننده‌های رویداد، زمانی که توسط چندین رویداد مورد استفاده قرار می‌گیرند را نادیده گرفت، این نکته را در کد بالا مشاهده می‌کنید. به این نکته توجه کنید که این کنترل کننده رویداد (myEventHandler) را برای هر رویدادی (مثلاً FormClosing) نمی‌توان مورد استفاده قرار داد. چون کنترل کننده‌های رویدادی که با اجرای این رویداد به وقوع می‌پیوندند در داخل Delegate ی قرار می‌گیرند که کنترل کننده‌های رویدادی با امضای زیر را قبول می‌کند :

private void Form1_FormClosing(object sender, FormClosingEventArgs e)

در حالیکه کنترل کننده رویداد ما دارای امضای زیر است :

private void myEventHandler(object sender, EventArgs e)

و در آخر به این نکته خیلی مهم توجه کنید که با کپی کردن یک کنترل کننده رویداد در محیط کدنویسی، کدهای داخل آن اجرا نمی‌شوند. برای روشن شدن مطلب یک برنامه ویندوزی ایجاد کرده و یک دکمه بر روی فرم قرار دهید. حال با زدن دکمه F7 به محیط کدنویسی رفته و کنترل کننده رویداد زیر را در آن کپی کنید :

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show("Hello World!");
}

به صورت زیر

همانطور که از کد بالا پیداست، این کد برای رویداد کلیک دکمه نوشته شده است، به طوریکه اگر بر روی دکمه کلیک شود یک پیغام نمایش داده شود. حال با زدن دکمه F5 برنامه را اجرا کرده و بر روی دکمه کلیک کنید. مشاهده می‌کنید که هیچ پیغامی نمایش داده نمی‌شود. دلیل این امر روشن است و آن این است که ما این کنترل کننده رویداد را به رویداد Click وصل نکرده‌ایم. برای این منظور دو راه وجود دارد. اولین راه این است که برنامه را متوقف کرده و بر روی دکمه دو بار کلیک کنید تا ویژوال استودیو به طور خودکار این کار را انجام دهد. در این حالت اگر به صورت زیر بر روی فایل Form1.Designer.cs دو بار کلیک کنید مشاهده می‌کنید که کنترل کننده رویداد به رویداد Click متصل شده است :

حال اگر برنامه اجرا و بر روی دکمه کلیک کنید، مشاهده می‌کنید که پیغام نمایش داده می‌شود. راه دوم هم این است که خودتان به صورت دستی این کنترل کننده رویداد را به رویداد Click وصل کنید :