چکیده
یکی از سوالات، در بین سوالات بسیاری که از من شده، این است که چگونه زمانی که دو فرم باز بر روی برنامه داریم، با تغییر داده ها در یک فرم، داده های تغییر یافته همزمان در فرم 2 مشخص شوند و فرم 2 نیز همزمان به روز رسانی شود. مثال این مقاله، علاوه بر نحوه پیاده سازی معماری MVC ، راه حل مناسب و درستی از مسئله فوق ارائه خواهد کرد.
در بخش اول مقاله MVC ، توضیحاتی در رابطه با معماری کلی این قالب، داده شد. در این بخش به بررسی یک مثال ساده از پیاده سازی این معماری خواهم پرداخت. در این مثال تنها منبع داده برنامه ، یک متغیر از نوع Double می باشد. این متغیر که در بخش مدل قرار گرفته است، توسط یکی از ویو ها که عمل Controller را برای ما انجام می دهد، قابل دسترسی و به روز رسانی می باشد. پس از تغییر حالت مدل به حالت جدید (تغییر داده به مقدار جدید)، ویو ها یا همان Observer ها ی رجیستر شده در بخش مدل با برقراری ارتباط با این بخش، درخواست تغییرات جدید را از بخش مدل کرده و بر اساس تغییرات جدید و ساختار داخلی خودش، خود را به روز رسانی می کند. هدف از این مثال، تنها آموزش مفاهیم این معماری به صورت عملی از جمله نحوه رجیستر کردن یک Observer در یک Observe، یا خارج کردن Observer از حالت رجیستر شده، نحوه آگاه شدن Observer ها از تغییرات مدل و دریافت تغییرات جدید و ... می باشد. بدیهی است که نمونه برنامه فرستاده شده همراه مقاله از لحاظ کد نویسی بهینه نمی باشد که این امر در پایان مقاله نیز با ذکر یک مثال نشان داده شده است. امید است که این مقاله در راستای ارتقای سطح کیفی برنامه نویسان مفید واقع شود.
مقدمه
در اين مثال ، منبع داده ها تنها يك عدد از نوع اعشاري مي باشد . همانطور كه قبلا نيز اشاره شد ، هيچ الزامي نيست كه منبع داده ، تعداد زياذي داده از يكي از جداول موجود در پايگاه داده SQL SERVER يا هر بانک داده ه ای ديگری باشد . گرچه معمولا اين چنين مي باشد . اما منبع داده مي تواند هر چيزي باشد .
اين مثال مي تواند شامل تعداد زيادي بخش ويو يا Observer باشد. Observer ها در مثال ما همان فرم هاي برنامه مي باشند . فرم اصلی برنامه مي تواند مقدار منبع داده يعني همان عدد اعشاري را تغيير دهد . بخش مدل علاوه بر تغيير دادن مقدار در عدد به مقدار جديد ، موظف است كه تغيير حالت مدل و سيستم را به Observer ها اطلاع دهد . Observer ها نيز موظف هستند كه با برقراري ارتباط با بخش مدل (Observe ) ، داده جديد را دريافت و خود را بر اساس آن به روز رساني كنند.
در اين مثال هر يك از Observer ها عمل جداگانه اي انجام مي دهند . به عنوان مثال يكي از Observer ها داده موجد را به توان 2 رسانيده و نشان مي دهد. Observer ديگر سينوس عدد را محاسبه كرده و نمايش مي دهد . در پايان برنامه متوجه خواهيد شد كه به راحتي مي توانيد Observer هاي ديگري به برنامه اضافه كرده كه هركدام عمل خاصي را بر اساس مقداد موجود در بخش مدل انجام دهند.
اگر چه اين مثال كاربرد عملي ندارد ولي سعي شده است كه اكثر جنبه هاي اين معماري پياده سازي شود . خواننده بايد پس از درك كامل اين مثال ،بتنواند اين معماري را به راحتي در برنامه هاي خود به كار گيرد . اين مثال كه كد كامل آن نيز همراه مقاله موجود می باشد ، از پنج فرم ، دو كلاس و دو اينترفيس استفاده مي كند كه در زير به توضيح آن ها خواهم پرداخت .
ابتدا به پيا ده سازي بخش هاي مدل و كنترلر مي پردازيم و سپس به توضح بخش ويو خواهيم پرداخت.
بخش مدل
بخش مدل شامل يك كلاس به نام MODEL و يك اينترفيس به نام IModel مي باشد كه كلاس Model از آن ارث بري مي كند و اعضاي آن را پياده سازي مي كند.
انترفيس IModel به صورت زير مي باشد.
using System;
using System.Collections.Generic;
using System.Text;
namespace FinalProject_MVC
{
public interface IModel
{
Double GetData
{
get;
}
}
}
همانطور كه در كد فوق مشخص است ، اينترفيس فوق داراي يك property به نام GetData مي باشد كه مقدارداده را در تغييرات مختلف مدل نگه داري مي كند . Observer ها از همين خاصيت استفاده كرده تا بتوانند داده جديد را دريافت كند.
كلاس مدل به شكل زير مي باشد
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace FinalProject_MVC
{
public class MODEL : IDisposable,IModel
{
#region declaration
List<frmBaseView> registerdObserverList = new List<frmBaseView>();
double Data = 0;
public event EventHandler ModelChanged;
#endregion
#region private Constructor
private MODEL()
{
}
#endregion
#region Singleton Instance
private static MODEL _singletonInstance;
public static MODEL SingletonInstance
{
get
{
if (_singletonInstance == null)
_singletonInstance = new MODEL();
return _singletonInstance;
}
}
#endregion
#region public members
public void AddObserver(frmBaseView observer)
{
if (!registerdObserverList.Contains(observer))
{
registerdObserverList.Add(observer);
this.Fire_ModelChanged(this, EventArgs.Empty);
}
else
throw new Exception("this observer was already registerd to model");
}
public void DeleteObserver(frmBaseView observer)
{
if (registerdObserverList.Contains(observer))
registerdObserverList.Remove(observer);
else
throw new Exception("this observer didn't register on model");
}
public void ChangeData(double newvalue)
{
this.Data = newvalue;
this.Fire_ModelChanged(this, EventArgs.Empty);
}
public void StartUpdateProccess()
{
foreach (frmBaseView observer in this.registerdObserverList)
{
observer.UpdateObserver(this);
}
}
#endregion
#region Fire Events
private void Fire_ModelChanged(object sender,EventArgs e)
{
if (ModelChanged == null)
return;
ModelChanged(sender, e);
}
#endregion
#region IDisposable Members
public void Dispose()
{
_singletonInstance = null;
}
#endregion
#region IModel Members
public double GetData
{
get
{
return this.Data;
}
}
#endregion
}
}
همانطور كه در كد فوق مشاهده مي كنيد ، كلاس MODEL به صورت Singleton Pattern پياده سازي شده است . اين به اين دليل است كه چون مي خواهيم كه ليستي از Observer ها را نگه داري كنيم ، بايستي اين ليست طوري باشد كه هنگام نمونه گيري از كلاس MODEL نمونه دگيري از اين ليست ساخته نشود تا در موقع Update كردن Observer ها با مشكل مواجه نشويم.
در بخش Declaration اين كلاس سه تعريف قرار گرفته است ، تعريف زير يك ليست ايجاد مي كند كه مي تواند ليستي از Observer ها رادر خود نگه داري كند .
List<frmBaseView> registerdObserverList = new List<frmBaseView>();
همانطور كه مشاهده مي كنيد در تعريف ليست فوق ، از مفاهيم چند ريختي استفاده شده است كه توضيح اين مفهوم از حوصله اين بحث خارج است .
تعريف دوم ، يك فيلد از نوع double تعريف شده كه به عنوان منبع داده نيز استفاده مي گردد.
در تعريف سوم ، يك Event تعريف شده است ، نكته اي كه در اين بابت بايد بدان توجه شود اين است كه هيچ Delegate اي در كلاس تعريف نشده است . وEvent تعريف شده بر اساس Delegateاي به نام Eventhandler پياده سازي شده است. Delegate ، EventHandler يكي از Delegate ها تعريف شده در خود دات نت مي باشد كه تعريفي مانند زير دارد
public delegate void EventHandler(object sender, EventArgs e);
در بخش اعضاي public اين كلاس ، چهار متد به شرح زير تعريف شده است.
متد AddObserver
public void AddObserver(frmBaseView observer)
{
if (!registerdObserverList.Contains(observer))
{
registerdObserverList.Add(observer);
this.Fire_ModelChanged(this, EventArgs.Empty);
}
else
throw new Exception("this observer was already registerd to model");
}
اين متد داراي آرگوماني از نوع FrmViewBase مي باشد. همانطور كه از نام اين متد مشخص است ، اين متد آرگومان ورودي خود چك كرده و چنانچه Observer قبلا وارد ليست Observer ها نشده باشد ، آن را را به ليست Observer هااضافه مي كند. چنانچه observer اي كه در آرگومان متد قرار دارد ، قبلا وارد ليست Observer ها شده باشد ، متد يك استثنا ايجاد كرده و پيامي مبني بر اينكه Observer مورد نظر قبلا در ليست اضافه شده است ،را صادر مي كند.
متد DeleteObserver
public void DeleteObserver(frmBaseView observer)
{
if (registerdObserverList.Contains(observer))
registerdObserverList.Remove(observer);
else
throw new Exception("this observer didn't register on model");
}
اين متد Observer اي كه در آرگومان خود قرار دارد را چنانچه در ليست Observer ها موجود باشد ، آن را حذف كرده و چنانچه Observer مورد نظر در ليست نباشد ، پيامي مبني بر وجود نداشتن Observer صادر مي گردد.
در دو متد فوق ، ورودي متدها از نوع frmBaseView مي باشد. همانطور كه در بخش هاي قبلي توضيح داده شد ، زماني كه به كلاسي از جنس پدر نياز باشد ، مي توانيم كلاسي از جنس فرزند را به آم بفرستيم . در توضيحاتي كه كمي بعد حواهد آمد خواهيد ديد كه كلاس frmBaseView ، به عنوان كلاس والد براي تمامي Observer ها عمل خواهد كرد.
متد ChangeData
public void ChangeData(double newvalue)
{
this.Data = newvalue;
this.Fire_ModelChanged(this, EventArgs.Empty);
}
اين متد آرگوماني از نوع double را دريافت مي كند ، و آن را به عنوان مقدار جديد عدد اعشاري يعني Data قرار مي دهد. پس از اين تغيير ، در خط دوم اين متد ، Event تعريف شده در اين كلاس يعني ModelChanged ، raise مي شود تا Observer ها را از تغييرات مدل آگاه سازد.
متد StartUpdateProccess
public void StartUpdateProccess()
{
foreach (frmBaseView observer in this.registerdObserverList)
{
observer.UpdateObserver(this);
}
}
اين متد توسط يك حلقه foreach ، متد UpdateObserver مربوط به Observer ها را فراخواني كرده و آرگوماني از جنس IModel به آن ارسال مي كند .
در بخش Fire Event از اين كلاس يك متد به نام Fire_ModelChange قرار گرفته است .
private void Fire_ModelChanged(object sender,EventArgs e)
{
if (ModelChanged == null)
return;
ModelChanged(sender, e);
}
در خط اول اين كد ابتدا چك مي شود كه اگر رويداد ModelChange برابر با null باشد ، كنترل برنامه از متد خارج شود (توسط دستور return) و اگر رويداد modelChange مخالف با null بود ، آن را فراخواني مي كند و آرگومان هاي مربوطه كه يكي از جنس object و ديگري از جنس EventArgs مي باشد را به رويداد مي فرستد.
در بخش IDisposable Members ، يك متد به نام Dispose تعريف شده است . اين متد جزء اعضاي اينترفيسي به نام IDisposable مي باشد ، كه كلاس MODEL از آن ارث بري كرده و ملزم به پياده سازي اعضاي آن مي باشد. در اين متد موقعي كه نمونه گرفته شده از كلاس MODEL در حال ِDispose شدن است ، مقدار _singletonInstancr ، برابر با null مي شود تا در نمونه گيري هاي بعدي برنامه با ايراد مواجه نشود. و در آخرين بخش از تعاريف اين كلاس يعني IModel Members يك property به نام GetData مي باشد،كه مقدار فعلي Data را بر مي گرداند.
بخش كنترلر
بخش كنترلر شامل يك كلاس مس ياشد كه تعريف آن در زير آمده است:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace FinalProject_MVC
{
class CONTROLLER
{
#region Declarations
MODEL _model;
#endregion
#region Constructor
public CONTROLLER()
{
//
//init _Model
//
_model = MODEL.SingletonInstance;
}
#endregion
#region public Members
public void RegisterObserver(frmBaseView observer)
{
_model.ModelChanged += new EventHandler(OnModel_ModelChanged);
if (_model == null)
return;
_model.AddObserver(observer);
}
public void UnRegisterObserver(frmBaseView observer)
{
if (_model == null)
return;
_model.DeleteObserver(observer);
}
public void ChangeData(double newData)
{
_model.ModelChanged += new EventHandler(OnModel_ModelChanged);
if (_model == null)
return;
_model.ChangeData(newData);
}
void OnModel_ModelChanged(object sender, EventArgs e)
{
if (_model == null)
return;
_model.StartUpdateProccess();
}
#endregion
}
}
در بخش Declaration مربوط به اين كلاس ، اعلاني صورت گرفته كه يه نمونه از كلاس Model تعريف شده كه در بخش Constructor كلاس مقدار دهي مي شود ودر واقع حافظه مربوطه به آن اختصاص داده مي شود.
اين كلاس داراي سه متد public و يك متد private مي باشد كه در ادامه توضيحات مربوط به هريك را خواهيد ديد
متد RegisterObserver
public void RegisterObserver(frmBaseView observer)
{
if (_model == null)
return;
_model.ModelChanged += new EventHandler(OnModel_ModelChanged);
_model.AddObserver(observer);
}
اين متد يك Observer به عنوان آرگومان دريافت كرده و آن را به متد AddObserver از كلاس MODEL مي فرستد تا در ليست Observer ها قرار گيرد.
همچنين در ابتداي اين متد با استفاده از رويداد ModelChanged و عملگر Cast ، متدي به نام OnModel_ModelChange را مشخص مي كند كه در هنگام raise شدن رويداد ModelChanged بايستي فراخواني شود.
متد UnRegisterObserver
public void UnRegisterObserver(frmBaseView observer)
{
if (_model == null)
return;
_model.DeleteObserver(observer);
}
اين متد Observer ي كه در آرگومان خود دارد را به متد DeleteObserver از كلاس MODEL مي فرستد تا از ليست Observer ها حذف گردد. در دو متد فوق دستور زير را مي بينيد
if (_model == null)
return;
اين دستور ابتدا چك مي كند كه اگر model برابر با null است ، از ادامه اجراي دستورات جلوگيري شود و كنترل برنامه از متد خارج شود.
متد ChangeData
public void ChangeData(double newData)
{
if (_model == null)
return;
_model.ModelChanged += new EventHandler(OnModel_ModelChanged);
_model.ChangeData(newData);
}
اين متد مقدار جديد داده را به متد changeData از كلاس MODEL مي فرستد ، تا مقدار جديد ، جايگزين مقدار قبلي شود. همچنين كد هاي مربوط به raise كردن رويداد ModelChange در اين متد پياده سازي شده است.
متد OnModel_ModelChanged
void OnModel_ModelChanged(object sender, EventArgs e)
{
if (_model == null)
return;
_model.StartUpdateProccess();
}
اين متد در زماني كه رويداد ModelChange ، raise مي شود ، فراخواني مي شود تا متد startUpdateProccess را از كلاس MODEL فراخواني كند ، تا Observer ها تغييرات جديد مدل را دريافت كرده و خود را به روز رساني كنند.
بخش ويو
در اين مثال فرمي داريم به نام frmBaseView ، همانطور كه حدس زده ايد ، اين فرم يك فرم پدر يا فرم والد مي باشد . در واقع چون ظاهر Observer ها مانند يكديگر مي باشد ، بهتر است كه يك فرم به عنوان فرم پدر ايجاد كنيم و بقيه Observer ها از اين فرم ارث بري كنند. طرح فرم frmBaseView به صورت زير مي باشد.

شکل 1 – 1 فرم frmBaseView
بر روي فرم فوق دو عدد Label ، دو عدد textbox و دو عدد Button قرار گرفته است . Textbox اي كه روبري main Data قرار گرفته است ، هميشه مقدار فعلي داده موجود در بخش مدل را نگه داري مي كند . TextBox اي كه روبروي related Data قرار گرفته است بسته به عملي كه Observer انجام مي دهد ، تغيير مي كند . به عنوان مثال اگر عدد موجود در بخش مدل يعني main Data برابر با 30 باشد و Observer عمل Sinus گرفتن از داده را انجام دهد ، آن گاه TextBox دوم مقدار 0.5 كه برابر با سينوس 30 درجه مي باشد را نشان خواهد داد.
اين فرم از اينترفيسي به نام IVIEW ارث بري مي كند .اينترفيس IVIEW به صورت زير مي باشد.
using System;
using System.Collections.Generic;
using System.Text;
namespace FinalProject_MVC
{
public interface IView
{
void UpdateObserver(IModel model);
}
}
همانطور كه مشاهده مي كنيد ، اينترفيس فوق داراي متدي به نام UpdateObserver مي باشد. اين متد يك آرگومان از جنس IModel در يافت مي كند . فرم frmBaseView ، چون از اينترفيس ارث بري مي كند ،ملزم به پياده سازي متد updateObserver مي باشد. كد هايي كه در فرم frmBaseView نوشته شده را در زير مشاهده خواهيد كرد.
public partial class frmBaseView : Form ,IView
{
public frmBaseView()
{
InitializeComponent();
}
#region IView Members
public void UpdateObserver(IModel model)
{
if (model == null)
return;
double newData = model.GetData;
this.ContinueUpdateProccess(newData);
}
#endregion
private void btnUnRegister_Click(object sender, EventArgs e)
{
Button senderButton = sender as Button;
btnUnRegister.Enabled = false;
btnRegister.Enabled = true;
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.UnRegisterObserver((frmBaseView)senderButton.FindForm());
}
private void btnRegister_Click(object sender, EventArgs e)
{
Button senderButton = sender as Button;
btnRegister.Enabled = false;
btnUnRegister.Enabled = true;
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.RegisterObserver((frmBaseView)senderButton.FindForm());
}
protected virtual void ContinueUpdateProccess(double NewData)
{
}
private void frmBaseView_Load(object sender, EventArgs e)
{
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.RegisterObserver((frmBaseView)sender);
}
private void frmBaseView_FormClosing(object sender, FormClosingEventArgs e)
{
//
//Un register Observer
//
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.UnRegisterObserver((frmBaseView)sender);
}
}
}
همانطور كه مشاهده مي كنيد ، كلاس frmBaseView از كلاس Form ارث بري مي كند (چون يك فرم است) و همچنين اين كلاس از انترفيس IView نيز ارث بري مي كند.
متد UpdateObserver
public void UpdateObserver(IModel model)
{
if (model == null)
return;
double newData = model.GetData;
this.ContinueUpdateProccess(newData);
}
اين متد كه در واقع عضو انترفيس IView مي باشد ، جهت Update كردن Observer به كار مي رود. اين متد داراي يك آرگومان از جنس IModel مي باشد ، اين متد مقدار داده را از طريق خاصيت GetData از كلاس MODEL دريافت مي كند و آن را جهت Update كردن Observer مربوطه به متد ContinueUpdateproccess مي فرستد.
متد ContinueUpdateProccess
protected virtual void ContinueUpdateProccess(double NewData)
{
}
همانطور كه مشاهده مي كنيد بدنه اين متد خالي مي باشد. در واقع اين متد به صورت Protect و Virtual تعريف شده استو. اين بدان جهت است كه Observer هايي كه از frmBaseView ارث بري مي كنند ، اين متد را Override كنند و بر اساس مقداري كه در آرگومان اي متد است ، خود را به روز رساني كنند. اين عمل باعث مي شود كه به طور اتوماتيك آن متد مربوط به Observer ي فراخواني شود كه در بخش مدل آن را notify كرده است.
رويداد كليكك مربوط به btnRegister
private void btnRegister_Click(object sender, EventArgs e)
{
Button senderButton = sender as Button;
btnRegister.Enabled = false;
btnUnRegister.Enabled = true;
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.RegisterObserver((frmBaseView)senderButton.FindForm());
}
كد فوق مربوط به رويداد كليلك مربوط به btnRegister مي باشد كه در فرم با عنوان RegisterMe مشخص شده است. در اين متد (رويداد ها نيز متد هستند) ابتدا يك عمل Cast در خط اول صورات مي گيرد ، اين خط از كد باعث مي شود كه تشخيص بدهيم ، كليد registerme از كدام Observer كليك شده است. در ادامه دستورات با فراخواني متد RegisterObserver از كلاس CONTROLLER ، Observer مورد تظر را رجيستر مي كنيم. نحوه تشخيص Observer توسط دستور زير مشخص مي شود .
(frmBaseView)senderButton.FindForm()
اين دستور ابتدا از متد FindForm ، Observer ي كه دكمه كليك شده در آن قرار دارد ، تشخيص داده مي شود و سپس توسط يك تبديل cast ، Observer تشخيص داده شده به نوعي frmBaseView تبديل مي شود تا بتواند در Observe رجيستر شود.
رويداد كليكك مربوط به btnUnRegister
private void btnUnRegister_Click(object sender, EventArgs e)
{
Button senderButton = sender as Button;
btnUnRegister.Enabled = false;
btnRegister.Enabled = true;
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.UnRegisterObserver((frmBaseView)senderButton.FindForm());
}
كد فوق مربوط به رويداد كليلك مربوط به btnUnRegister مي باشد كه در فرم با عنوان UnRegisterMe مشخص شده است. اين كد دقيقا عملي مشابه با كليد btnRegisterMe انجام مي دهد ، با اين تفاوت كه Observer تشخيص داده شده از ليست Observer ها حذف مي شود.
رويداد Load مربوط به فرم
private void frmBaseView_Load(object sender, EventArgs e)
{
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.RegisterObserver((frmBaseView)sender);
}
كد فوق مربوط به رويداد load ، از فرم frmBaseView مي باشد. اين كد باعث مي شود هنگامي كه Observwer ي نمونه سازي و نمايش داده مي شود توسط اين كد تشخيص داده شده و در ليست Observer ها رجيستر شود.
رويداد Form_Closing مربوط به فرم
private void frmBaseView_FormClosing(object sender, FormClosingEventArgs e)
{
CONTROLLER MVC_Controller = new CONTROLLER();
MVC_Controller.UnRegisterObserver((frmBaseView)sender);
}
رويداد فوق ، هر زمان كه يكي از Observer ها در حال بسته شدن باشد ، اتفاق مي افتد و باعث مي شود كه آن Observer از ليست Observer ها حذف گردد. در اين مثال سه Observer به نام هاي frmSinusView ، frmCostineview و frmExponentialView طراحي شده اند كه هرسه از كلاس frmBaseView ارث بري مي كنند و متد ContinueUpdateProccess را بر اساس عملي كه انجام مي دهند ، پياده سازي مي كنند. در زير اشكال هر يك از سه Observer را همراه با كد هاي مربوط به متد continueUpdateProccess را مشاهده خواهيد كرد.
frmSinusView و متد ContinueUpdateProccess

شکل 1 – 2 فرم frmSinusView
protected override void ContinueUpdateProccess(double NewData)
{
this.txtMainData.Text = NewData.ToString();
this.txtVariableData.Text = Math.Sin((Math.PI*NewData)/180).ToString();
}
frmCostineView و متد ContinueUpdateproccess

شکل 1 – 3 فرم frmCosineView
protected override void ContinueUpdateProccess(double NewData)
{
this.txtMainData.Text = NewData.ToString();
this.txtVariableData.Text = Math.Cos((Math.PI * NewData) / 180).ToString();
}
frmExponentialView و متد ContinueUpdateProccess

شکل 1 – 4 فرم frmExponentialView
protected override void ContinueUpdateProccess(double NewData)
{
this.txtMainData.Text = NewData.ToString();
this.txtVariableData.Text = (NewData * NewData).ToString();
}
آخرين قسمتي كه ار مثال فوق تشريح خواهيم كرد ، فرم اصلي برنامه مي باشد كه با نام frmMain تعريف شده است. اين فرم در شكل زير نشان داده شده است.

شکل 1 – 5 فرم اصلي برنامه مربوط به مثال 4-1
خروجي برنامه پس از كليك كردن بر روي كليد هاي Sinus ، Cosine و Exponential

شکل 1 – 7 خروجي برنامه پس از كليك كردن بر روي كليد هاي Sinus ، Cosine و Exponential
خروجي برنامه پس از وارد كردن عدد 30 در فرم MVC Example و فشردن كليد Update

شکل 1 – 8 خروجي برنامه پس از وارد كردن عدد 30 در فرم MVC Example و فشردن كليد Update
خروجي برنامه پس از تغييرعدد 30 به 20و فشردن كليد UnRegisterme از فرم ExponentialView (پايين شكل بالا در سمت راست) و سپس فشردن كليد Update

شکل 1 – 9 خروجي برنامه پس از تغييرعدد 30 به 20و فشردن كليد UnRegisterme از فرم ExponentialView (پايين شكل بالا در سمت راست) و سپس فشردن كليد Update
همانطور كه در شكل فوق مشخص است ، فرم ExponentialView كه در پايين و سمت راست شكل فوق قرار دارد، از ليست Observer ها حذف شده و با تغيير در داده ، تغييري در حالت آن به وجود نيامده است.
در پايان اين مثال مجددا متذكر مي شوم كه اين هدف از اين مثال ، تشريح و روش پياده سازي معماري MVC در برنامه ها مي باشد. مي توان كد هاي اين برنامه را توسعه داد و همچنين آن ها را بهينه كرد. به عنوان مثال مي توان كد مربوط به متد ChangeData از كلاس MODEL را به صورت زير تغيير داد
public void ChangeData(double newvalue)
{
if (this.Data == newvalue)
return;
this.Data = newvalue;
this.Fire_ModelChanged(this, EventArgs.Empty);
}
تغيير كد به صورت فوق باعث مي شود كه چنانچه مقدار جديد با مقدار فعلي برابر است از Update كردن Observer ها جلوگيري شود.
خلاصه
در بخش دوم معماری MVC به بیان نحوه پیاده سازی این معماری در زبان برنامه نویسی سی شارپ پرداخته شد. انتظار می رود که خواننده گرامی پس از درک کامل مطالی دو مقاله و بررسی مثال زده شده در این بخش، درک نسبتا کاملی از معماری MVC پیدا کند و بتواند این معماری را در برنامه های بزرگ و تجاری به کار ببرد. در پایان هرگونه سوال، پیشنهاد و یا انتقادی از مطالب این بخش دارید، را با من در میان گذارید تا نسبت به آن اقدامات لازم صورت گیرد.