PDA

View Full Version : آموزش : نحوه ی کنترل کردن فرمها و مشکلات متداول



sinpin
دوشنبه 17 دی 1386, 20:41 عصر
:لبخندساده:
سلام

از اونجا که تاپیکهای زیادی ناشی از مشکلات در مدیریت فرمها، باز کردن فرمها فقط برای یکبار و ... مطرح شده است؛ تصمیم گرفتم این تاپیک رو ایجاد کنم تا ضمن بررسی مشکلات و مرکزیت دادن به حل این موضوع؛ شاید تونسته باشم به بعضی دوستان نیز کمکی کنم.

در ادامه ی بحث به این موارد پرداخته میشود :
1- نحوه ی بار کردن فرمهای فرزند بصورت تنها یکبار در حافظه (به کمک Singleton Pattern)
2- و نحوه ی کار کردن با کنترلهای یک فرم و روش صحیح پاس دادن مقادیر بین فرمهای یک برنامه
و ...

امیدوارم که مفید باشه.
دوستان اگر نظر موافق یا مخالفی داشتند خوشحال میشم که مطرح کنند.

sinpin
دوشنبه 17 دی 1386, 21:02 عصر
فکر میکنم اکثر دوستان با این pattern آشنا باشند.
(اونایی که از این الگو اطلاعی ندارن به این لینک مراجعه کنند :
http://www.dofactory.com/Patterns/PatternSingleton.aspx)

اما بصورت خیلی خلاصه : در این الگو هدف رسیدن به وضعیتی است که بتونیم تضمین کنیم از یک کلاس فقط و فقط یک (و یا در حالتی خاص: تعدادی مشخص و کنترل شده) آبجکت ساخته شود. (ضمن اینکه روش مناسبی برای عملیاتی ست که نیاز به یک دسترسی سراسری دارند)

Ensure a class has only one instance and provide a global point of access to it.
استفاده از این الگو در موارد بسیاری از جمله ConnectionManager و یا در همین موضوع کنترل فرمها بصورتی که از هر فرم تنها یک Instance ساخته شود.

خب از اونجا که هدف تاپیک چیز دیگریست بحث راجع به این pattern رو همینجا خاتمه میدم.

در این پست سعی داریم یک برنامه بسازیم که متشکل از یک فرم والد و سه فرم فرزند باشه.
(و با این شرط که : فرم والد تنها میتواند یک نمونه از هر فرزند را در حافظه بار کند.)

اولن کاری که بنظر میرسه اینه که در تک تک فرمهای فرزند الگوی سینگلتون رو - مانند نمونه ی زیر - تکرار کنیم :

namespace Ticket
{
public partial class frmBoat : Form
{
#region Singleton Form
private static frmBoat formInstance = null;
public static frmBoat GetInstance()
{
if (formInstance == null)
{
formInstance = new frmBoat();
}
return formInstance;
}

private frmBoat()
{
InitializeComponent();
}
#endregion
}
}البته راههای زیادی - و با توجه به حالتهای خاص - برای ایجاد کلاسهای سینگتون وجود دارد.

اما مشکل عمده ای که روش بالا دارد این است که باید برای تمامی فرمها (و یا بصورت کلی : برای تمامی کلاسهای مورد نظر) این الگو را تکرار کنیم.

چه خوب بود اگه میتونستیم یکبار این الگو رو مینوشتیم و همه جا از اون استفاده میکردیم.
لذا بدون مقدمه میریم سراغ نوشتن یک Generic Singleton :


public class SingletonProvider<T> where T : new()
{
SingletonProvider() { }

public static T Instance
{
get { return SingletonCreator.Instance; }
}

class SingletonCreator
{
private SingletonCreator() { }
private static T instance;
internal static T Instance
{
get
{
if (instance == null)
instance = new T();
else if (instance is Form)
if ((instance as Form).IsDisposed)
instance = new T();
return instance;
}
}
}
}واقعا چقدر استفاده از کلاسهای ژنریک لذت بخشه. اینطور نیست ؟!

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

public class FormManagementHelper
{
public static Form ShowChildForm(Form _parent, Form _child)
{
_child.MdiParent = _parent;
_child.Show();
_child.Activate();
return _child;
}

public static void ShowChild1(Form _parent)
{
ShowChildForm(_parent, SingletonProvider<Child1>.Instance);
}

public static void ShowChild2(Form _parent)
{
ShowChildForm(_parent, SingletonProvider<Child2>.Instance);
}

public static void ShowChild3(Form _parent)
{
ShowChildForm(_parent, SingletonProvider<Child3>.Instance);
}
}خب فکر میکنم خود کد به تنهایی گویا باشه.
فقط اینکه Child1 , Child2, Child3 اسم فرمهای فرزند هستند که توسط کلاس ژنریک سینگلتون یک Instance از اونها برگشت داده میشه.

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

private void child1ToolStripMenuItem_Click(object sender, EventArgs e)
{
FormManagementHelper.ShowChild1(this);
}

private void child2ToolStripMenuItem_Click(object sender, EventArgs e)
{
FormManagementHelper.ShowChild2(this);
}

private void child3ToolStripMenuItem_Click(object sender, EventArgs e)
{
FormManagementHelper.ShowChild3(this);
}

sinpin
دوشنبه 17 دی 1386, 21:34 عصر
من سورس کد نهایی برنامه بالا رو هم آپلود میکنم واسه دوستانی که شاید مایل باشند با جزییات بیشتر برنامه رو ببینند.
و یکی از دوستان هم نحوه به ارث بردن فرمهای برنامه از یک فرم رو پرسیده بودند که تو همین مثال (با نام BaseForm) استفاده شده است.

razavi_university
دوشنبه 17 دی 1386, 23:56 عصر
<T> where T

شبیه به اینها رو قدیما توی ++C استفاده میکردیم. نمیدونستم تو #C هم میشه استفاده کرد:متفکر: (ندیده بودم:افسرده:)
میشه در این مورد بیشتر توضیح بدی
ممنون

Amir Oveisi
سه شنبه 18 دی 1386, 00:31 صبح
تا اونجایی که من یادمه تو ++C بهشون میگفتیم Template (البته الانم میگیم :لبخند: )
ولی باز تا اونجایی که یادمه پیاده سازی Template با Generic های #C متفاوته (هر چند از نظر مفهومی تقریبا مثل همن )

razavi_university
سه شنبه 18 دی 1386, 00:51 صبح
تا اونجایی که من یادمه تو ++C بهشون میگفتیم Template (البته الانم میگیم :لبخند: )
ولی باز تا اونجایی که یادمه پیاده سازی Template با Generic های #C متفاوته (هر چند از نظر مفهومی تقریبا مثل همن )
خب منم اسمشو تقریبا یادم بود:اشتباه: ولی گفتم شاید اشتبا باشه (آخه خیلی وقته دستم به ++C نخورده بعد از ترم 1 تا حالا)
خب در مورد پیاده سازی و اجراشون مثله ++C می مونه یا چطوریه؟

Amir Oveisi
سه شنبه 18 دی 1386, 00:54 صبح
http://msdn2.microsoft.com/en-us/library/c6cyy67b.aspx

bahar_engineer
سه شنبه 30 تیر 1388, 15:34 عصر
من همینطور کلاسها رو توی پروژه قرار دادم اما وقتی یک بار مثلاً صفحه ثبت نام رو باز می کنه بار دوم دیگه بازش نمی کنه ... یعنی نمایشش نمی ده و هیچ عملی انجام نمی شه

چرا ؟؟

تو پروژه من 3 تا کاربر می تونن ثبت نام کنن ... و هر کدام از بخش ویژه خودشون بالا بیان ... حالا اگه یکی از کاربران اول نرم افزار رو نصب کنه و بعد از نصب در ابتدای بالا اومدن صفحه ثبت نام می آد و اون ثبت نام می کنه ... حالا اگه همین موقع کاربر دوم بیاد ثبت نام کنه باید اون صفحه ثبت نام دوباره نمایش داده بشه چون صفحه ای هم هست که بعد از ثبت نام صفحه اصلیه نرم افزار رو باز می کنه نمی تونم close کنم... پس باید پنهان بشه از دید کاربر و موقع فراخوانی دوباره نمایش داده بشه


البته من در حالت عادی هم با این سینگلتون مشکل دارم یعنی اصلاً hidden هم نکردم صفحه ثبت نام رو اما با سینگلتون فقط همون بار اول باز می شه و اگه دوباره بخوام ثبت نام کنم نمیاد بالا

منتظر راه حلم ... THX

reza_arab
جمعه 03 اردیبهشت 1389, 19:25 عصر
این فرم یک فرم سینگلتون با محدودیت خاصی است و فقط به این معنا نیست که یک فرم دوبار new نشود بلکه اگر یک صفت آن(isdispose) مثبت باشد می توان آن را دوباره new کرد. بنابراین یک سینگلتون با این شرایط است .
به نظرم روشی که ارائه کردید مشکلاتی را داراست که در ادامه توضیح می دهم
1-هر وقت فرم جدیدی را بخواهید به مجموعه اضافه کنید نیاز دارید که کلاس FormManagementHelper را باز تعریف کنید و از نگاه شی گرا این وضعیت مطلوبی نیست
2- بعد از نمایش فرم دیگر نمی توان به اشاره گر فرم دسترسی داشت(مثلا احیانا بخواهیم بعد از بسته شدن فرم مقدار یک خصوصیت فرم را بخوانیم)، البته می توان با ایجاد یک خصوصیت(مثل GetChild1) در کلاس FormManagementHelper اشاره گر به آن فرم را برای در خواست کننده بر گرداند که اگر این کار صورت گیرد مشکل دیگری را اضافه می کند، و آن اینکه ممکن است در طول برنامه دو ارجاع به یک فرم ایجاد شود و اگر اولین ارجاع فرم را به هر دلیلی مثل بستن فرم dispose کند و ارجاع دوم بخواهد بعد این کار تابع show فرم را مستقلا فراخوانی کند به exception بر می خورد. پس اساسا ورود بحث Isdispose به کلاس سینگلتون کار اصولی نیست و فقط "کار راه انداز" عمل می کند.
3- خوانایی کد شما از درجه پایینی برخوردار است
پس مسئله را دوباره تعریف می کنیم:
می خواهیم فرمی داشته باشیم که تا هنگامی که dispose نشده هر فراخوانی از آن فرم، ما را به یک آبجکت منحصر به فرد رهنمون کند. و در صورتی که dispose شد یک آبجکت جدید تحویلمون بده.(البته دیگه اسمش singleton نیست!)
راه حل مناسب تر:
به نظر من بهتره یک فرم Singleton تعریف شود و کاربر هر زمانی که مایل بود فرمی به شکلی که در بالا تعریف کردیم داشته باشد آن را به صورت item به برنامه خود اضافه کند و بدون درگیر شدن با محتوای کلاسی که ما تعریف کردیم از آن استفاده کند. این کار خصوصیت استفاده مجدد را بالا می برد.
ناگفته نماند که مشکل ارجاعات که در بند 3 به ان اشاره شد همچنان پابرجاست






public partial class FormSingleton : Form
{
FormSingleton()
{
InitializeComponent();
}
class SingletonCreator
{
static SingletonCreator() { }
internal static
FormSingleton uniqueInstance = new FormSingleton();
}
public static FormSingleton UniqueInstance
{
get
{
if (SingletonCreator.uniqueInstance == null)
SingletonCreator.uniqueInstance = new FormSingleton();
else
if (SingletonCreator.uniqueInstance.IsDisposed)
SingletonCreator.uniqueInstance = new FormSingleton();
return SingletonCreator.uniqueInstance;
}
}
}



این کد را code behind یک فرم جدید وارد می کنیم و با ساخت یک template item از آن می توان آن را در هر پروژه ایی استفاده کرد.
فایل template را ضمیمه می کنم

shocraneh
پنج شنبه 27 مرداد 1390, 16:38 عصر
راستش من نفهمیدم فایل هایی که دوستمون گذاشتند چه طوری استفاده کنم . من با singelton کار نکردم .کاش خود پروژه رو میزاشتید .من خیلی دوست دارم با singelton آشنا بشم

wolfmen_007
شنبه 12 مهر 1393, 10:35 صبح
با سلام خدمت استادهاي گرامي
من يك مبتدي هستم 2 تا فرم دارم مي خوام در فرم دوم 2 رشته از من گرفته بوسيله تكست باكس و آنهارا وارد يك ليست باكس در فرم اول كند
ولي در فرم دوم ليست باكس من رو نميشناسه
يعني كلا هيچ شئي از فرم ديگه رو نميشناسه و تغير نمي ده....
فقط شئي هاي خودش رو مي شناسه:گریه::گریه::گریه: