در قالب طراحی شده، نه در کدهای Viewهای اضافه شده و نه در ViewModelها، اثری از کدهای مرتبط با تزریق وابستگیها و یا حتی وهله سازی ViewModel مرتبط با یک View مشاهده نمیشود. در ادامه قصد داریم جزئیات پیاده سازی آنرا مرور کنیم.
مدیریت خودکار وهله سازی ViewModelها
اگر به فایل MVVM\ViewModelFactory.cs قرار گرفته در پروژه Common مراجعه کنید، کدهای کلاسی که کار وهله سازی ViewModelها را انجام میدهد، مشاهده خواهید کرد:
using System.Windows;
using StructureMap;
namespace WpfFramework1999.Common.MVVM
{
/// <summary>
/// Stitches together a view and its view-model
/// </summary>
public class ViewModelFactory
{
private readonly FrameworkElement _control;
/// <summary>
/// سازنده کلاس تزریق وابستگیها به ویوو مدل و وهله سازی آن
/// </summary>
/// <param name="control">وهلهای از شیءایی که باید کار تزریق وابستگیها در آن انجام شود</param>
public ViewModelFactory(FrameworkElement control)
{
_control = control;
}
/// <summary>
/// وهله متناظر با ویوو مدل
/// </summary>
public IViewModel ViewModelInstance { get; private set; }
/// <summary>
/// کار تزریق خودکار وابستگیها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
/// </summary>
public void WireUp()
{
var viewName = _control.GetType().Name;
var viewModelName = string.Concat(viewName, "ViewModel"); //قرار داد نامگذاری ما است
if (!_control.IsLoaded)
{
_control.Loaded += (s, e) =>
{
setDataContext(viewModelName);
};
}
else
{
setDataContext(viewModelName);
}
}
private void setDataContext(string viewModelName)
{
//کار تزریق خودکار وابستگیها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
ViewModelInstance = ObjectFactory.TryGetInstance<IViewModel>(viewModelName);
if (ViewModelInstance == null) // این صفحه ویوو مدل ندارد
return;
_control.DataContext = ViewModelInstance;
}
}
}
در این کلاس، یک وهله از صفحهای که توسط کاربر درخواست شدهاست، در سازنده کلاس دریافت گردیده و سپس در متد WireUp، بر اساس قرارداد نامگذاری که پیشتر نیز عنوان شد، ViewModel متناظر با نام View از IoC Container استخراج و وهله سازی میگردد. سپس این وهله به DataContext صفحه انتساب داده میشود.
چند سؤال مهم:
- IoC Container از کجا میداند که ViewModelها در کجا قرار دارند؟
- این کلاس ViewModelFactory چگونه به وهلهای از یک صفحه درخواستی توسط کاربر دسترسی پیدا میکند و در کجا؟
IoC Container از کجا میداند که ViewModelها در کجا قرار دارند؟
اگر بحث سری جاری را از ابتدا دنبال کرده باشید، عنوان شد که ViewModelها را در این قالب، باید مشتق شده از کلاس پایهای به نام BaseViewModel تهیه کنیم. برای مثال:
/// <summary>
/// ویوو مدل افزودن و مدیریت کاربران
/// </summary>
public class AddNewUserViewModel : BaseViewModel
این کلاس پایه که در فایل MVVM\BaseViewModel.cs پروژه Common قرار دارد، به نحو زیر آغاز شده است:
/// <summary>
/// کلاس پایه ویوو مدلهای برنامه که جهت علامتگذاری آنها برای سیم کشیهای تزریق وابستگیهای برنامه نیز استفاده میشود
/// </summary>
public abstract class BaseViewModel : DataErrorInfoBase, INotifyPropertyChanged, IViewModel
اگر دقت کنید در اینجا اینترفیس IViewModel نیز ذکر شده است. این اینترفیس برای علامتگذاری ViewModelها و یافتن خودکار آنها توسط IoC Container مورد استفاده درنظر گرفته شده است. اگر به فایل Core\IocConfig.cs پروژه Infrastructure مراجعه کنید، چنین تنظیمی را در آن مشاهده خواهید نمود:
// Add all types that implement IView into the container,
// and name each specific type by the short type name.
scan.AddAllTypesOf<IViewModel>().NameBy(type => type.Name);
به این ترتیب StructureMap با اسکن اسمبلی Infrastructure کلیه کلاسهای پیاده سازی کننده IViewModel را یافته و سپس آنها را بر اساس نام متناظری که دارند، ذخیره میکند. با این تنظیم، اکنون در کلاس ViewModelFactory یک چنین کدی کار خواهد کرد:
//کار تزریق خودکار وابستگیها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
ViewModelInstance = ObjectFactory.TryGetInstance<IViewModel>(viewModelName);
کلاس ViewModelFactory چگونه به وهلهای از یک صفحه درخواستی توسط کاربر دسترسی پیدا میکند و در کجا؟
در اینجا قسمتی از کدهای فایل Core\FrameFactory.cs قرار گرفته در پروژه Infrastructure را ملاحظه میکنید:
namespace WpfFramework.Infrastructure.Core
{
/// <summary>
/// ایجاد یک کنترل فریم سفارشی که قابلیت تزریق وابستگیها را به صورت خودکار دارد
/// به همراه اعمال مسایل راهبری برنامه که از منوی اصلی دریافت میشوند
/// </summary>
public class FrameFactory : Frame
{
/// <summary>
/// در اینجا میشود به وهلهای از صفحهای که قرار است اضافه گردد دسترسی یافت
/// </summary>
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var newPage = newContent as FrameworkElement;
if (newPage == null)
return;
_currentViewModelFactory = new ViewModelFactory(newPage);
_currentViewModelFactory.WireUp(); //کار تزریق وابستگیها و وهله سازی ویوو مدل مرتبط انجام خواهد شد
}
}
}
در این کلاس، یک Frame سفارشی را طراحی کردهایم؛ از این جهت که بتوان متد OnContentChanged آنرا تحریف کرد. در این متد، newContent دقیقا وهلهای از صفحه جدیدی است که توسط کاربر درخواست شدهاست. خوب ... این وهله را داریم، بنابراین تنها کافی است آنرا به کلاس ViewModelFactory ارسال کنیم و متد WireUp آنرا بر روی وهله کلاس صفحه درخواستی فراخوانی نمائیم. به این ترتیب، صفحهای نمایش داده خواهد شد که DataContext آن با وهلهای از ViewModel متناظر مقدار دهی شدهاست. از این جهت که این وهله سازی توسط IoC Container صورت میگیرد، کلیه وابستگیهای تعریف شده در سازنده کلاس ViewModel نیز به صورت خودکار وهله سازی و مقدار دهی خواهند شد.
نهایتا فراخوانی متد IocConfig.Init، در فایل App.xaml.cs پروژه ریشه، در آغاز برنامه قرار گرفته است.