در سیشارپ، هنگامی که یک دکمه یا (Button) به فرم اضافه می کنید برای اضافه کردن کد به آن باید روی آن دابل کلیک کنید، پس از اینکار به طور خودکار یک متد رویداد (Event Handler) درون کدها ایجاد میشود که به رویداد Click دکمه متصل میشود. این متد به شکل زیر است:
private void button1_Click(object sender, EventArgs e)
{
// کدهایی که میخواهید هنگام کلیک روی دکمه اجرا شوند
}
این کد که نمایش متد Click است شامل بخشهایی است که بهتر است از همین ابتدای کار چشم و گوشتان با آن آشنا شود و بدانید که هر کدام چه معنی و کاربردی می توانند داشته باشند .
کلمه private نشان می دهد که کدهای درون متد Click خصوصی بوده و از بیرون آن قابل دسترسی نمی باشند.
کلمه void نشان می دهد که این متد فقط یک کار انجام می دهد و هیچگونه خروجی ندارد که بخواهیم یا بتوانیم آن را دریافت کنیم.
در اینجا، Click یک رویداد از کلاس Button است و button1_Click متدی است که وقتی رویداد Click اتفاق میافتد، فراخوانی میشود. این متد دارای دو پارامتر است که درون پرانتزها قرار میگیرند:
object sender
این پارامتر اشارهگر به شیئی است که رویداد را ایجاد کرده است. در این مورد، شیء Button است که روی آن کلیک شده است.
از این پارامتر میتوانید برای تشخیص اینکه کدام دکمه رویداد را ایجاد کرده است استفاده کنید. برای مثال، اگر چندین دکمه به این متد متصل باشند، میتوانید با استفاده از این پارامتر تشخیص دهید که کدام دکمه کلیک شده است.
EventArgs e:
این پارامتر حاوی اطلاعات اضافی درباره رویداد است. برای رویداد Click، این پارامتر معمولاً خالی است (EventArgs.Empty)، زیرا رویداد کلیک به اطلاعات اضافی نیاز ندارد.
برای برخی رویدادهای دیگر مانند MouseClick یا KeyPress ، این پارامتر حاوی اطلاعات مفیدی مانند موقعیت ماوس یا کلید فشرده شده است.
کاربردهای این پارامترها
۱. تشخیص دکمه کلیک شده (sender)
اگر چندین دکمه به یک متد رویداد متصل باشند، میتوانید از پارامتر sender برای تشخیص اینکه کدام دکمه کلیک شده است استفاده کنید. برای مثال:
private void button1_Click(object sender, EventArgs e)
{
Button clickedButton = (Button)sender; // تبدیل sender به Button
MessageBox.Show("شما روی دکمه " + clickedButton.Text + " کلیک کردید.");
}
در این مثال، اگر چندین دکمه به این متد متصل باشند، متن دکمهای که کلیک شده است در یک پیام نمایش داده میشود.
۲. استفاده از EventArgs
برای رویدادهایی که اطلاعات اضافی ارائه میدهند، میتوانید از پارامتر e استفاده کنید. برای مثال، در رویداد MouseClick، میتوانید موقعیت ماوس را دریافت کنید:
private void button1_MouseClick(object sender, MouseEventArgs e)
{
int x = e.X; // موقعیت X ماوس
int y = e.Y; // موقعیت Y ماوس
MessageBox.Show($"ماوس در موقعیت ({x}, {y}) کلیک کرده است.");
}
دستکاری پارامترها
شما میتوانید این پارامترها را به روشهای مختلف دستکاری کنید تا رفتار برنامه را تغییر دهید. برای مثال:
۱. تغییر متن دکمه
میتوانید متن دکمهای که کلیک شده است را تغییر دهید:
private void button1_Click(object sender, EventArgs e)
{
Button clickedButton = (Button)sender;
clickedButton.Text = "کلیک شد!";
}
۲. غیرفعال کردن دکمه
میتوانید دکمهای که کلیک شده است را غیرفعال کنید:
private void button1_Click(object sender, EventArgs e)
{
Button clickedButton = (Button)sender;
clickedButton.Enabled = false;
}
۳. استفاده از EventArgs برای رویدادهای خاص
اگر از رویدادهایی مانند KeyPress یا MouseMove استفاده میکنید، میتوانید از اطلاعات موجود در EventArgs استفاده کنید. برای مثال، در رویداد KeyPress:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
MessageBox.Show("کلید Enter فشرده شد.");
}
}
جمعبندی
sender: اشارهگر به شیئی است که رویداد را ایجاد کرده است (مثلاً دکمهای که کلیک شده است).
e: حاوی اطلاعات اضافی درباره رویداد است برای رویداد Click معمولاً خالی است.
شما میتوانید از این پارامترها برای تشخیص شیء ایجادکننده رویداد، تغییر رفتار برنامه، یا دریافت اطلاعات اضافی استفاده کنید.
در درسهای بعدی که با انواع خروجی متدها اشنا شدید فقط این نکته را بدانید که
متدهای رویداد (Event Handlers) در سیشارپ باید از نوع void باشند، زیرا Delegate هایی که برای رویدادها استفاده میشوند (مثل EventHandler) فقط با متدهای void سازگار هستند. اگر نوع بازگشتی متد را تغییر دهید، دیگر نمیتوانید آن را مستقیماً به رویداد دکمه متصل کنید. لذا برای اینکه هنگام استفاده از این رویدادها دچار خطا نشوید نوع برگشتیشان را تغییر ندهید و بشکل void باقی بگذارید .
در پست امروز کدهای مربوط به بستن برنامه را می نویسیم . پس از دابل کلیک روی باتنی که قصد دارید از ان جهت خروج از برنامه استفاده کنید بخش معمول متد نوشته می شود که شما باید درون اکولادهای آن بنا به نیازهای خاص برنامه از یکی از این کدها استفاده کنید.
در C#، چندین روش برای خروج از برنامه وجود دارد:
متد زیر فرم فعلی را میبندد.
this.Close();
دستور زیر یک زیربرنامه یا یک thread را میبندد.
Application.ExitThread();
دستور زیر تمام پنجرههای برنامه را میبندد و همه حلقههای پیام را در تمام threadها متوقف میکند. و مناسب برنامه های ویندوزی می باشد
Application.Exit();
دستور زیر برنامه را به طور کامل خاتمه میدهد. عدد 0 نشاندهنده خروج موفقیتآمیز است و برای برنامههای کنسولی مناسب است.
Environment.Exit(0);
در برخی موارد، مخفی کردن یک فرم به جای بستن آن مزایایی دارد:
حفظ وضعیت: اگر فرم حاوی اطلاعات یا تنظیماتی است که میخواهید حفظ شود، مخفی کردن آن بهتر از بستن است.
بازگشت سریع: برای فرمهایی که مکرراً استفاده میشوند، مخفی کردن باعث میشود دفعات بعد سریعتر باز شوند.
جلوگیری از بازسازی مجدد: مخفی کردن از ایجاد مجدد فرم جلوگیری میکند، که میتواند در عملکرد برنامه تأثیر مثبت داشته باشد.
حفظ ارتباطات: اگر فرم با سایر بخشهای برنامه در ارتباط است، مخفی کردن این ارتباطات را حفظ میکند.
برای مخفی کردن یک فرم در C#، میتوانید از متد Hide() استفاده کنید:
this.Hide();
برای نمایش مجدد فرم مخفی شده:
this.Show();
اگر میخواهید فرم فعلی را مخفی کرده و فرم دیگری را نمایش دهید:
this.Hide();
Form2 f = new Form2();
f.Show();
به یاد داشته باشید که فرمهای مخفی همچنان در حافظه باقی میمانند، بنابراین در صورت عدم نیاز به آنها در آینده، باید مدیریت مناسبی برای آزادسازی منابع انجام دهید.
در این پست یک برنامه بسیار ساده را ارائه می دهم که در آن با تغییر اندازه فرم ، یک برچسب با نوشتن ابعاد داخلی فرم همواره در وسط فرم قرار می گیرد . این کار نسبتا خیلی ساده است اما من از نوشتن این کد کوچک و ساده منظور خاصی را دنبال می کنم و آن هم این است که آنچه در برنامه نویسی از خود کدنویسی مهمتراست رعایت اصول برنامه نویسی می باشد که با رعایت آن نکات برنامه شما براحتی قابل توسعه خواهد بود یعنی تمام آنچه که شما در کدهای زیر می بینید را می شود در چند خط نیز نوشت اما اگر شما اصول برنامه نویسی را رعایت نکنید هنگامی که می خواهید برنامه خود را توسعه دهید و یا برای موارد مشابه استفاده کنید با مشکلات و چالش های بزرگی مواجه می شوید پس بهتر است از ابتدای کار بجای اینکه کد هدف را یاد بگیرید اصول استفاده صحیح از کدها را یاد گرفته و از آن مهمتر اینکه به رعایت این اصول عادت کنید در غیر این صورت شما از یک برنامه نویس که می تواند برنامه های چند خطی بنویسد پا فراتر نمی گذارید . حالا متن کد زیر را بدقت بررسی کنید.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DynamicLabelPositioner
{
public partial class Form1 : Form
{
private Label sizeInfoLabel;
public Form1()
{
InitializeComponent();
InitializeControls();
}
private void InitializeControls()
{
sizeInfoLabel = lbl; // فرض میکنیم lbl در طراحی فرم تعریف شده است
UpdateLabelText();
CenterControl(sizeInfoLabel);
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
UpdateLabelText();
CenterControl(sizeInfoLabel);
}
/// بهروزرسانی متن برچسب با اطلاعات اندازه فرم
private void UpdateLabelText()
{
if (ClientSize.Width >= 0 && ClientSize.Height >= 0)
{
sizeInfoLabel.Text = $"Width: {ClientSize.Width} Height: {ClientSize.Height}";
}
else
{
sizeInfoLabel.Text = "Invalid size detected";
}
}
/// مرکزی کردن یک کنترل در فرم
private void CenterControl(Control control)
{
if (control == null) return;
int x = (ClientSize.Width - control.Width) / 2;
int y = (ClientSize.Height - control.Height) / 2;
/// اطمینان از مختصات مثبت
control.Location = new Point(Math.Max(0, x), Math.Max(0, y));
}
}
در برنامه نویسی شی گرا موارد و اصولی وجود دارد که باید رعایت شوند . من این اصول را در 16 مورد جمع آوری کرده ام که در ذیل با یک جمله کوتاه آنها را توضیح می دهم.
1 – Don't Repeat Yourself یعنی خودت را تکرار نکن : از نوشتن کد تکراری پرهیز کن
2 – Keep It Simple, Stupid یعنی ساده نگهش دار احمق : بدون فدا کردن کارایی و دقت کد آن را تا حد امکان ساده نگه دارید.
3 - You Aren’t Gonna Need It یعنی بهش نیاز نداری : فقط چیزی رو پیادهسازی کن که الان لازم داری .
4 - Testability یعنی قابلیت تستپذیری : کد طوری طراحی بشه که به راحتی بتونید براش تست بنویسید و رفتارش رو بررسی کنید .
5 - Resource Management یعنی مدیریت بهینه منابع سیستم (مثل حافظه، فایلها یا اتصالات) در برنامهنویسی، طوری که تخصیص و آزادسازی اونها به موقع و بدون نشتی (leak) انجام بشه، مثلاً با استفاده از using در سیشارپ برای بستن خودکار اتصالات.
6 - Portability یعنی قابلیت انتقالپذیری : کد شما باید بتونه روی سیستمها، پلتفرمها یا محیطهای مختلف (مثل ویندوز، لینوکس) بدون تغییر یا با کمترین تغییر اجرا بشه.
7 - Coding Standards یا استانداردهای کدنویسی به مجموعهای از قوانین و راهنماییها گفته میشه که برنامهنویسان برای نوشتن کد تمیز، خوانا و یکنواخت ازش پیروی میکنن تا همکاری تیمی، نگهداری و فهم کد آسونتر بشه.
8 - Security یعنی امنیت در برنامهنویسی، که به حفاظت از نرمافزار و دادهها در برابر دسترسی غیرمجاز، حملات یا سوءاستفاده اشاره داره، مثلاً با رمزنگاری یا اعتبارسنجی ورودیها.
9 - Performance Optimization یعنی بهینهسازی عملکرد، که به بهبود سرعت و کارایی برنامه با کاهش مصرف منابع (مثل CPU یا حافظه) از طریق تکنیکهایی مثل الگوریتم بهتر یا کش کردن اشاره داره.
10 - Internationalization (i18n) یعنی بینالمللیسازی، که به طراحی نرمافزار به شکلی اشاره داره که بتونه به راحتی با زبانها، فرهنگها و مناطق مختلف (مثل پشتیبانی از چند زبان یا فرمت تاریخ) سازگار بشه.
11 - Maintainability & Readability یعنی قابلیت نگهداری و خوانایی، که به نوشتن کدی اشاره داره که ساده، قابل فهم و منظم باشه تا توسعهدهندهها بتونن به راحتی اون رو درک کنن و تغییرش بدن.
12 - Single Responsibility Principle (SRP) یعنی اصل مسئولیت واحد، که میگه هر کلاس یا ماژول باید فقط یک وظیفه یا مسئولیت داشته باشه تا تغییرات سادهتر و کد منظمتر بشه.
13 - Open/Closed Principle (OCP) یعنی اصل باز/بسته، که میگه کلاسها باید برای گسترش (اضافه کردن قابلیت جدید) باز باشن، اما برای تغییر (ویرایش کد موجود) بسته باشن.
14 - Liskov Substitution Principle (LSP) یعنی اصل جانشینی لیسکوف، که میگه اشیاء از کلاسهای فرزند باید بتونن جای کلاس والد رو بگیرن بدون اینکه رفتار برنامه خراب بشه.
15 - Interface Segregation Principle (ISP) یعنی اصل تفکیک رابط، که میگه کلاسها نباید مجبور بشن رابطهایی رو پیادهسازی کنن که استفاده نمیکنن، و رابطها باید کوچک و خاص باشن.
16 - Dependency Inversion Principle (DIP) یعنی اصل وارونگی وابستگی، که میگه ماژولهای سطح بالا و پایین باید به abstraction (مثل رابطها) وابسته باشن، نه به پیادهسازیهای خاص.
مقدمه ساده برای درک مفهوم کلاس :
امروز میخوایم با یه چیز جالب توی برنامهنویسی سیشارپ آشنا بشیم:
کلاس : کلاس مثل یه نقشه یا طرح اولیهست که به ما کمک میکنه چیزهایی که توی برنامهمون نیاز داریم رو بسازیم. مثلاً فکر کنید یه کلاس مثل یه قالب کیکه؛ با اون قالب میتونید کلی کیک درست کنید که همشون شکلشون شبیه همه، ولی مواد داخلشون میتونه فرق داشته باشه. توی این درس، یه کلاس ساده درست میکنیم و یه کم راجع به اینکه چه جوری میشه بهش دسترسی داشت حرف میزنیم.
توضیح کلاس و سطوح دسترسی :
کلاس توی سیشارپ یه جور جعبهست که میتونیم توش اطلاعات (مثل اسم یا سن یا تاریخ و هر چیز دیگه) و یا کارهایی که میخوایم انجام بدیم (مثل سلام کردن یا محاسبه چند عبارت ریاضی و یا انجام یک کار روی فرم و هر کاری که می تونید تصور کنید) رو بذاریم. فرض کنید میخوایم یه کلاس برای "دانشآموز" درست کنیم. این کلاس میتونه اطلاعاتی مثل اسم و سن داشته باشه و کاری مثل گفتن "سلام" رو انجام بده. به این شکل:
internal class Student
{
string name = " " علی ; یه اطلاعات //
int age = 15; // یه اطلاعات دیگه
void SayHello() // یه کار که کلاس میتونه بکنه
{
Console.WriteLine("سلام ، من " + name + " هستم! ");
}
}
اینجا Student یه کلاسه که نقشه ی یه دانشآموزه. حالا اگه بخوایم ازش استفاده کنیم، باید یه دانشآموز واقعی از روش بسازیم که بهش میگیم شیء یا Object
حالا بریم سراغ سطوح دسترسی. اینها مثل قفل در خونهاند که میگن کی میتونه وارد بشه و کی نمی تونه. توی سیشارپ، ما میتونیم با چند کلمه مشخص کنیم کلاسمون چه جوری دیده بشه. دو تا از مهمترینشون ایناس:
public اگه بنویسیم public class Student، یعنی همه میتونن از این کلاس استفاده کنن، حتی از جاهای دیگه توی برنامهمون. مثل اینه که در خونهتون همیشه باز باشه و هرکی بخواد بیاد داخل.
internal یعنی فقط کسایی که توی همون پروژه یا "خونه" هستن میتونن ازش استفاده کنن، و غریبهها نمیتونن بیان داخل. همآنطور که در مثال بالا می بینید کلاس با سطح دسترسی internal تعریف شده در چنین حالتی در صورتی که مایل باشید می توانید از نوشتن این کلمه صرفنظر کنید زیرا سی شارپ بطور پیشفرض کلاسی را که برایش سطح دسترسی تعیین نشده آن را internal در نظر می گیرد .
یه مثال سادهتر: فکر کنید کلاس Student مثل یه کتابخونهست که داخل کلاس ماست اگه public باشه، همه دانشآموزای مدرسه میتونن بیان و کتاب بگیرن. اما اگه internal باشه، فقط بچههای کلاس خودمون میتونن ازش استفاده کنن. اگه بخوایم چیزایی داخل کلاس (مثل اسم یا سن) رو قفل کنیم که فقط خود کلاس بتونه باهاشون کار کنه، از private استفاده میکنیم.
بیائید مثال کتابخونه رو کمی بیشتر باز کنیم و تا جایی که امکان دارد سطوح دسترسی را با این مثال توضیح دهیم.
فکر کنید یه کتابخونه داریم که پر از کتابهای مختلفه. این کتابخونه مثل یه کلاس توی سیشارپه که میتونه چیزایی مثل کتاب (اطلاعات) و کارایی مثل امانت دادن (متدها) رو داشته باشه . حالا اینکه کی بتونه بیاد از این کتابخونه استفاده کنه، بستگی داره به اینکه درش رو چه جوری قفل کنیم. توی سیشارپ به این قفلها میگیم سطوح دسترسی. بیاید با مثال کتابخونه ببینیم این سطوح چیان!
فرض کنیم کتابخونهمون یه اسم داره، مثلاً Library، و توی یه پروژه سیشارپ تعریفش کردیم. حالا میخوایم ببینیم چه کسایی میتونن بیان داخلش:
public عمومی اگه بگیم public class Library, یعنی در کتابخونه برای همه بازه! هر دانشآموزی از هر مدرسهای توی شهر، حتی اگه از یه جای دیگه باشه، میتونه بیاد و کتاب بگیره. مثلاً:
public class Library
{
string book = "داستانهای خوب";
void LendBook()
{
Console.WriteLine("کتاب امانت داده شد!");
}
}
اینجا همه میتونن از کتابخونه استفاده کنن، حتی اگه توی یه پروژه دیگه باشن (مثلاً یه برنامه جداگانه).
internal یا داخلی : اگه چیزی ننویسیم، مثل class Library, سیشارپ خودش اون رو internal میکنه. یعنی فقط کسایی که توی همون مدرسه (یا همون پروژه) هستن میتونن بیان داخل. مثلاً:
class Library
{
string book = "ریاضی آسون";
}
توی این حالت، فقط دانشآموزای مدرسهای که کتابخونه توش ساخته شده میتونن ازش استفاده کنن. اگه یه مدرسه دیگه بخواد بیاد، در قفله و نمیتونه!
private خصوصی : حالا اگه بخوایم چیزایی توی کتابخونه رو قفل کنیم (مثل کتابها یا کارهاش)، از private استفاده میکنیم.
توجه کنید که این سطح برای خود کلاس نیست، بلکه برای چیزای داخلش (مثل متدها یا متغیرها) استفاده میشه. مثلاً:
public class Library
{
private string secretBook = "گنج مخفی"; // فقط خود کتابخونه میتونه ببینه
public string normalBook = "داستان ساده"; // همه میتونن ببینن
}
اینجا secretBook مثل یه کتاب توی گاوصندوق کتابخونهست که فقط مسئول کتابخونه (یعنی خود کلاس) میتونه بهش دست بزنه، ولی normalBook برای همه آزاده.
protected محافظتشده
این یه کم خاصتره. فرض کنید کتابخونه یه بخش ویژه داره که فقط برای کسایی که از خانواده کتابخونه باشن (مثلاً کلاسهای فرزند) بازه. مثلاً:
public class Library
{
protected string familyBook = "کتاب خانوادگی";
}
public class SpecialLibrary : Library
{
void CheckBook()
{
Console.WriteLine(familyBook); // فقط اینجا میتونه ببینه
}
}
اینجا familyBook فقط برای کلاسهایی که از Library ارث میبرن مثل SpecialLibrary قابل دسترسیه، نه برای بقیه.
جمعبندی :
اگه کتابخونه public باشه، همه بچههای شهر میتونن بیان و کتاب بگیرن.
اگه internal باشه، فقط بچههای همون مدرسهای که کتابخونه توشه میتونن بیان.
اگه یه کتاب داخلش private باشه، فقط مسئول کتابخونه میتونه بهش دست بزنه.
اگه protected باشه، فقط مدرسههایی که شعبه اون کتابخونهان میتونن به بخش ویژهش دسترسی داشته باشن.