برای استفاده از Angular CLI در پروژه های MVC یا کلا استفاده از ویژوال استودیو باید اینو در نظر گرفت که دیگه کار IDE نگهداری فایلهای پروژه هست و حتی برای Run گرفتن از پروژه باید از کامندهای CLI استفاده کنید، من مثالی رو در گیت هاب دارم میتونید اونو بگیرید و بعد کامند لاین رو از روت پروژه باز کنید و کامندهای CLI رو برای دریافت ماجولهای Angular CLI و خروجی گرفتن اجرا کنید و کار توسعه رو ادامه بدید :
https://github.com/mehdipayervand/MVCSampleNG2
بعد از دریافت پروژه کامندهای زیر توی روت پروژه اجرا کنید:(کمی زمانبر هست تقزیبا 150 مگابایت)
npm install
ng build
و اگر خطایی مشاهده نکردید:
ng serve
حالا آدرس زیر رو توی مرورگرتون باز کنید و ادامه کارتون ...
http://localhost:4200/
Today, we’re excited to introduce GitHub Package Registry, a package management service that makes it easy to publish public or private packages next to your source code.
GitHub Package Registry is fully integrated with GitHub, so you can use the same search, browsing, and management tools to find and publish packages as you do for your repositories. You can also use the same user and team permissions to manage code and packages together. GitHub Package Registry provides fast, reliable downloads backed by GitHub’s global CDN. And it supports familiar package management tools: JavaScript (npm), Java (Maven), Ruby (RubyGems), .NET (NuGet), and Docker images, with more to come.
برای رفع این مشکلات، از روشهای توسعهی به همراه ابزارهای یکپارچگی مداوم استفاده میشود. برای نمونه، AppVeyor یکی از سرویسهای ابری یکپارچگی مداوم (Continuous Integration و یا به اختصار CI) است. به کمک آن میتوان یک image از ویندوز سرور را به همراه ابزارهای build، آزمایش و توزیع برنامههای NET. در اختیار داشت. این سرویس، مخزن کد شما را مونیتور کرده و هر زمانیکه تغییری را در آن ایجاد کردید، آنها را به صورت خودکار build و در صورت موفقیت آمیز بودن این عملیات، بستهی نیوگت متناظری را به سایت nuget.org ارسال میکند. بنابراین پس از یکپارچه کردن مخزن کد خود با این نوع سرویسهای یکپارچگی مداوم، دیگر حتی نیازی به build دستی آن نیز نخواهید داشت. همینقدر که کدی را به مخزن کد تحت نظر، commit کنید، مابقی مراحل آن خودکار است.
به همین جهت در این مطلب قصد داریم نحوهی اتصال یک مخزن کد GitHub را به سرویس یکپارچگی مداوم AppVeyor، جهت تولید خودکار بستههای Nuget، بررسی کنیم.
معرفی سرویس ابری AppVeyor
AppVeyor یک راه حل یکپارچگی مداوم چند سکویی است که استفادهی از آن برای پروژههای سورس باز رایگان است و سازگاری فوق العادهای را با محصولات مایکروسافت دارد. برای ورود به آن میتوان از اکانتهای GitHub ،BitBucket و VSTS (Visual Studio Team Services) استفاده کرد.
گردش کاری متداول یکپارچگی مداوم AppVeyor به این صورت است:
الف) با اکانت GitHub خود به آن وارد شوید.
ب) یک مخزن کد GitHub خود را به آن Import کنید.
ج) به مخزن کد GitHub خود یک فایل yml. تنظیمات مخصوص AppVeyor را اضافه کنید.
د) نظارهگر Build و توزیع خودکار پروژهی خود باشید.
ایجاد اکانت و اتصال به مخزن کد GitHub
در ابتدا به صفحهی لاگین آن مراجعه کنید. در اینجا جهت سهولت کار با GitHub و مخازن کد آن، گزینهی GitHub را انتخاب کرده و توسط آن به سیستم وارد شوید:
پس از ورود موفق، گزینهی new project را انتخاب کنید:
در ادامه مخزن کد GitHub و نوع عمومی آنرا انتخاب میکنیم تا AppVeyor بتواند پروژههای آنرا Import کند و همچنین به آنها web hookهایی را اضافه کند تا با اعمال تغییراتی در سمت GitHub، کار اطلاع رسانی آنها به AppVeyor به صورت خودکار صورت گیرد:
پس از آن لیست مخزنهای کد شما در همینجا ارائه میشود تا بتوانید یک یا چند مورد را انتخاب کنید:
انجام تنظیمات عمومی مخزن کد
در صفحهی بعدی، برگهی settings و سپس از منوی کنار صفحهی آن، گزینهی General را انتخاب کنید:
در اینجا اگر پروژهی شما از نوع NET Core. است، گزینهی NET Core .csproj patching. را انتخاب نمائید:
سپس در پایین صفحه بر روی دکمهی Save کلیک کنید.
انتخاب و تنظیم محیط Build
در ادامه در برگهی settings و سپس از منوی کنار صفحهی آن، گزینهی Environment را انتخاب کنید:
در این صفحه، worker image را بر روی VS 2017 قرار دهید و همچنین در قسمت Cached directories and files، مسیر C:\Users\appveyor\.dnx را جهت کش کردن عملیات Build و بالا بردن سرعت آن، مقدار دهی کنید. سپس در پایین صفحه بر روی دکمهی Save کلیک نمائید.
اکنون بر روی گزینهی Build در منوی سمت چپ صفحه کلیک کنید. در اینجا سه حالت msbuild ،script و off را میتوان انتخاب کرد.
- در حالت msbuild، که سادهترین حالت ممکن است، فایل sln. مخزن کد، یافت شده و بر اساس آن به صورت خودکار تمام پروژههای این solution یکی پس از دیگری build خواهند شد. این مورد برای برنامههای Full .NET Framework شاید گزینهی مناسبی باشد.
- حالت script برای پروژههای NET Core. مناسبتر است و در این حالت میتوان کنترل بیشتری را بر روی build داشت. به علاوه این روش بر روی لینوکس هم کار میکند؛ زیرا در آنجا دسترسی به msbuild نداریم.
- حالت off به معنای خاموش کردن عملیات build است.
در اینجا گزینهی cmd را جهت ورود build script انتخاب میکنیم:
سپس دستورات ذیل را جهت ورود به پوشهی پروژهی کتابخانه (جائیکه فایل csproj آن قرار دارد)، بازیابی وابستگیهای پروژه و سپس تولید بستهی نیوگتی از آن، وارد میکنیم:
cd ./src/DNTPersianUtils.Core dotnet restore dotnet pack -c release cd ../..
ذکر ../.. cd در انتهای این دستورات ضروری است. در غیر اینصورت cd بعدی در تنظیماتی دیگر، داخل همین پوشه انجام میشود.
تنظیم اجرای خودکار آزمونهای واحد
در همین صفحه، گزینههای settings -> tests -> script -> cmd را انتخاب و سپس دستورات زیر را وارد کرده و آنرا ذخیره کنید:
cd ./src/DNTPersianUtils.Core.Tests dotnet restore dotnet test cd ../..
تنظیم اطلاع رسانی خودکار از اجرای عملیات
در برگهی settings -> notifications مطابق تنظیمات فوق میتوان نوع email را جهت اطلاع رسانی شکست انجام عملیات یکپارچگی مداوم، انتخاب کرد.
آزمایش Build خودکار
برای آزمایش تنظیماتی که انجام دادیم، به برگهی latest build مراجعه کرده و بر روی دکمهی new build کلیک کنید تا اسکریپتهای build و test فوق اجرا شوند. بدیهی است اجرای بعدی این اسکریپتها خودکار بوده و به ازای هر commit به GitHub، بدون نیاز به مراجعهی مستقیم به appveyor صورت میگیرند.
اضافه کردن نماد AppVeyor به پروژه
در تنظیمات برگهی Settings، گزینهی AppVeyor badge نیز در منوی سمت چپ صفحه، وجود دارد:
در اینجا همان کدهای mark down آنرا انتخاب کرده و به ابتدای فایل readme پروژهی خود اضافه کنید. برای نمونه نماد فعلی (تصویر فوق)، build failing را نمایش میدهد؛ چون سه آزمون واحد آن مشکل دارند و باید اصلاح شوند.
پس از رفع مشکلات پروژه و commit آنها، build و اجرای خودکار آزمونهای واحد آن توسط AppVeyor صورت گرفته و اینبار این نماد به صورت زیر تغییر میکند:
ارسال خودکار بستهی نیوگت تولید شده به سایت nuget.org
برای ارسال خودکار حاصل Build، به سایت نیوگت، نیاز است یک API Key داشته باشیم. به همین جهت به صفحهی مخصوص آن در سایت nuget پس از ورود به سایت آن، مراجعه کرده و یک کلید API جدید را صرفا برای این پروژه تولید کنید (در قسمت Available Packages بستهی پیشینی را که دستی آپلود کرده بودید انتخاب کنید).
در اینجا API Key را ذکر خواهیم کرد. سپس در پایین صفحه بر روی دکمهی Save کلیک کنید.
همچنین نیاز است مشخص کنیم که بستههای nupkg تولید شده در چه مسیری قرار دارند. به همین جهت در قسمت settings -> artifacts مسیر پوشهی bin نهایی را ذکر میکنیم:
این مورد را نیز با کلیک بر روی دکمهی Save ذخیره کنید.
اکنون اگر نگارش جدیدی را به GitHub ارسال کنیم (تغییر VersionPrefix در فایل csproj و سپس commit آن)، پس از Build پروژه، بستهی نیوگت آن نیز به صورت خودکار تولید شده و به سایت nuget.org ارسال میشود. لاگ آنرا در پایین صفحهی برگهی latest build میتوانید مشاهده کنید.
ساده سازی مراحل تنظیمات AppVeyor
در صفحهی settings، در منوی سمت چپ آن، گزینهی export YAML نیز وجود دارد. در اینجا میتوان تمام تنظیمات انجام شدهی فوق را با فرمت yml. دریافت کرد و سپس این فایل را به ریشهی مخزن کد خود افزود. با وجود این فایل، دیگر نیازی به طی کردن دستی هیچکدام از مراحل فوق نیست.
برای نمونه فایل appveyor.yml نهایی مطابق با توضیحات این مطلب، چنین محتوایی را دارد:
version: 1.0.{build} image: Visual Studio 2017 dotnet_csproj: patch: true file: '**\*.csproj' version: '{version}' package_version: '{version}' assembly_version: '{version}' file_version: '{version}' informational_version: '{version}' cache: C:\Users\appveyor\.dnx build_script: - cmd: >- cd ./src/DNTPersianUtils.Core dotnet restore dotnet pack -c release cd ../.. test_script: - cmd: >- cd ./src/DNTPersianUtils.Core.Tests dotnet restore dotnet test cd ../.. artifacts: - path: ./src/DNTPersianUtils.Core/bin/release/*.nupkg name: NuGet deploy: - provider: NuGet api_key: secure: xyz skip_symbols: true notifications: - provider: Email to: - me@yahoo.com on_build_success: false on_build_failure: true on_build_status_changed: true
یک نکته: api_key ذکر شدهی در اینجا در قسمت secure، رمزنگاری شدهاست. برای تولید آن میتوانید از مسیر https://ci.appveyor.com/tools/encrypt استفاده کنید. به این صورت مشکلی با عمومی کردن فایل appveyor.yml خود در GitHub نخواهید داشت.
- دانلود تمامی محتواها شامل ویدیوها، اسلایدها و متون ارائه شده
- گروه بندی در فولدرهای مختلف
- شروع مجدد دانلود در صورت قطع ارتباط از آخرین مرحله
- فیلتر نمودن دانلود براساس کلمات کلیدی مورد نظر
- توانایی انتخاب فرمت ویدیوها جهت دانلود
- به آدرس ذیل مراجعه و فایل PowerShell مربوطه را دانلود نمایید.
- جهت دانلود تمامی محتواهای مطرح شده با حجم حدودی 1 ترابایت از دستور ذیل استفاده نمایید.
.\ DownloadBuild2017Content.ps1
- جهت دانلود براساس کلمات کلیدی مورد نظر از دستور ذیل استفاده نمایید.
.\ DownloadBuild2017Content.ps1 -keyword "Graph,Bluetooth"
- جهت دانلود براساس کدهای مرتبط با Sessionهای برگزار شده از دستور ذیل استفاده نمایید.
.\DownloadBuild2017Content.ps1 -session "P4116,P4118,C9R04"
The project has not been restored or restore failed -run 'dotnet restore'
The project does not list one of 'win10-x64, win81-x64, win7-x64' in the 'runtimes'
"runtimes": { "win10-x64": {}, "win81-x64": {}, "win8-x64": {} }
بعد از افزایش ماژولهای مختلف و به نسبت حجیم شدن پروژه به طور وحشتناکی کاهش سرعت اجرا بعد از build پروژه رو دارم
cpu: i7
Ram : 16 DDR4
HDD: 500 SSD
این مشکل در بین تمامی اعضا تیم داریم. آیا محدودیت در حجم پروژه هست؟ آیا با مهاجرت به نسخه 2.1 این مشکل کمتر میشه؟
شروع به کار با ngrok
- قبل از ادامهی این آموزش، وارد سایت https://ngrok.com شده و بعد از ثبت نام، مطابق سیستم عاملی که دارید، فایل مخصوص آن را دریافت کنید.
- بعد از دریافت، فایل زیپ را از حالت فشرده خارج کنید. به محل فایل استخراج شده رفته و در یک مکان خالی در پوشهی جاری، دکمهی Shift را فشرده و سپس کلیک راست کنید و در آخر گزینهی Open Command Window Here را انتخاب نمائید و یا کلا از طریق cmd وارد پوشهی مربوطه شوید.
- سپس پروژهی خود را توسط ویژوال استودیو (IIS Express) و یا بر روی IIS لوکال خود، اجرا کنید.
- همانطور که در تصویر مشاهده میکنید؛ آدرس پروژهی لوکال ما به شکل زیر میباشد:
http://localhost:51095/Home/Index
- حال به cmd برمیگردیم و سپس با دستور زیر میتوانیم لوکالهاست خود را بر روی https به اشتراک بگذاریم:
ngrok http [port] -host-header="localhost:[port]"
همانطور که ملاحظه میفرمائید، هم http و هم https را در اختیار ما قرار میدهد. برای مثال اگر نیاز است با webhookهای تلگرام کار کنید، باید از آدرس https آن استفاده کنید.
در اینجا در قسمت HTTP Requests میتوانید درخواستهایی را که فرستاده میشوند، ببینید و یا میتوانید با رفتن به آدرس زیر، دستورات بالا را در نمایی گرافیکی و بصورت کامل نظارهگر باشید:
http://127.0.0.1:4040
برای نمونه، خروجی آن در گوشی و مرورگر آن، به شکل زیر است:
آموزش Prism #2
تشریح پروژه:
میخواهیم برنامه ای بنویسیم که دارای سه ماژول زیر است.:
- ماژول Navigator : برای انتخاب و Switch کردن بین ماژولها استفاده میشود؛
- ماژول طبقه بندی کتابها : لیست طبقه بندی کتابها را به ما نمایش میدهد؛
- ماژول لیست کتابها : عناوین کتابها به همراه نویسنده و کد کتاب را به ما نمایش میدهد.
ابتدا یک پروژه WPF در Vs.Net ایجاد کنید(در اینجا من نام آن را FirstPrismSample گذاشتم). قصد داریم یک صفحه طراحی کنیم که دو ماژول مختلف در آن لود شود. ابتدا باید Shell پروژه رو طراحی کنیم. یک Window جدید به نام Shell بسازید و کد زیر را در آن کپی کنید.
<Window x:Class="FirstPrismSample.Shell" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:com="http://www.codeplex.com/CompositeWPF" Title="Prism Sample By Masoud Pakdel" Height="400" Width="600" WindowStartupLocation="CenterScreen"> <DockPanel> <ContentControl com:RegionManager.RegionName="WorkspaceRegion" Width="400"/> <ContentControl com:RegionManager.RegionName="NavigatorRegion" DockPanel.Dock="Left" Width="200" /> </DockPanel> </Window>
#پروژه Common
قبل از هر چیز یک پروژه Common میسازیم و مشترکات بین ماژولها رو در آن قرار میدهیم(این پروژه باید به تمام ماژولها رفرنس داده شود). این مشترکات شامل :
- کلاس پایه ViewModel
- کلاس ViewRequestEvent
- کلاس ModuleService
کد کلاس ViewModelBase که فقط اینترفیس INotifyPropertyChanged رو پیاده سازی کرده است:
using System.ComponentModel; namespace FirstPrismSample.Common { public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChangedEvent( string propertyName ) { if ( PropertyChanged != null ) { PropertyChangedEventArgs e = new PropertyChangedEventArgs( propertyName ); PropertyChanged( this, e ); } } } }
using Microsoft.Practices.Composite.Presentation.Events; namespace FirstPrismSample.Common.Events { public class ViewRequestedEvent : CompositePresentationEvent<string> { } }
توضیح درباره EventAggregator
EventAggregator یا به اختصار EA مکانیزمی است در پروژهای ماژولار برای اینکه در Composite UIها بتوانیم بین کامپوننتها ارتباط برقرار کنیم. استفاده از EA وابستگی بین ماژولها را از بین خواهد برد. برنامه نویسانی که با MVVM Light آشنایی دارند از قابلیت Messaging موجود در این فریم ورک برای ارتباط بین View و ViewModel استفاده میکنند. در Prism این عملیات توسط EA انجام میشود. یعنی برای ارتباط با Viewها باید از EA تعبیه شده در Prism استفاده کنیم. در ادامه مطلب، چگونگی استفاده از EA را خواهید آموخت.
namespace FirstPrismSample .Common { public interface IModuleServices { void ActivateView(string viewName); } }
using Microsoft.Practices.Composite.Regions; using Microsoft.Practices.Unity; namespace FirstPrismSample.Common { public class ModuleServices : IModuleServices { private readonly IUnityContainer m_Container; public ModuleServices(IUnityContainer container) { m_Container = container; } public void ActivateView(string viewName) { var regionManager = m_Container.Resolve<IRegionManager>(); // غیر فعال کردن ویو IRegion workspaceRegion = regionManager.Regions["WorkspaceRegion"]; var views = workspaceRegion.Views; foreach (var view in views) { workspaceRegion.Deactivate(view); } //فعال کردن ویو انتخاب شده var viewToActivate = regionManager.Regions["WorkspaceRegion"].GetView(viewName); regionManager.Regions["WorkspaceRegion"].Activate(viewToActivate); } } }
*نکته: در هر ماژول ارجاع به اسمبلیهای Prism مورد نیاز است.
#ماژول طبقه بندی کتاب ها:
برای شروع یک Class Library جدید به نام ModuleCategory به پروژه اضافه کنید. یک UserControl به نام CategoryView بسازید و کدهای زیر را در آن کپی کنید.
<UserControl x:Class="FirstPrismSample.ModuleCategory.CategoryView " xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="LightGray" FlowDirection="RightToLeft" FontFamily="Tahoma"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Text=" طبقه بندی ها"/> <ListView Grid.Row="1" Margin="10" Name="lvCategory"> <ListView.View> <GridView> <GridViewColumn Header="کد" Width="50" /> <GridViewColumn Header="عنوان" Width="200" /> </GridView> </ListView.View> </ListView> </Grid> </UserControl>
using Microsoft.Practices.Composite.Events; using Microsoft.Practices.Composite.Modularity; using Microsoft.Practices.Composite.Regions; using Microsoft.Practices.Unity; using FirstPrismSample.Common; using FirstPrismSample.Common.Events; using Microsoft.Practices.Composite.Presentation.Events; namespace FirstPrismSample.ModuleCategory { [Module(ModuleName = "ModuleCategory")] public class CategoryModule : IModule { private readonly IUnityContainer m_Container; private readonly string moduleName = "ModuleCategory"; public CategoryModule(IUnityContainer container) { m_Container = container; } ~CategoryModule() { var eventAggregator = m_Container.Resolve<IEventAggregator>(); var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>(); viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler); } public void Initialize() { var regionManager = m_Container.Resolve<IRegionManager>(); regionManager.Regions["WorkspaceRegion"].Add(new CategoryView(), moduleName); var eventAggregator = m_Container.Resolve<IEventAggregator>(); var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>(); viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true); } public void ViewRequestedEventHandler(string moduleName) { if (this.moduleName != moduleName) return; var moduleServices = m_Container.Resolve<IModuleServices>(); moduleServices.ActivateView(moduleName); } } }
*ModuleAttribute استفاده شده در بالای کلاس برای تعیین نام ماژول استفاده میشود. این Attribute دارای دو خاصیت دیگر هم است :
- OnDemand : برای تعیین اینکه ماژول باید به صورت OnDemand (بنا به درخواست) لود شود.
- StartupLoaded : برای تعیین اینکه ماژول به عنوان ماژول اول پروزه لود شود.(البته این گزینه Obsolute شده است)
*برای تعریف ماژول کلاس مورد نظر حتما باید اینترفیس IModule را پیاده سازی کند. این اینترفیس فقط شامل یک متد است به نام Initialize.
*در این پروژه چون Viewهای برنامه صرفا جهت نمایش هستند در نتیجه نیاز به ایجاد ViewModel برای آنها نیست. در پروژههای اجرایی حتما برای هر View باید ViewModel متناظر با آن تهیه شود.
توضیح درباره متد Initialize
در این متد ابتدا با استفاده از Container موجود RegionManager را به دست میآوریم. با استفاده از RegionManager میتونیم یک CompositeUI طراحی کنیم. در فایل Shell مشاهده کردید که یک صفحه به دو ناحیه تقسیم شد و به هر ناحیه هم یک نام اختصاص دادیم. دستور زیر به یک ناحیه اشاره خواهد داشت:
regionManager.Regions["WorkspaceRegion"]
#ماژول لیست کتاب ها:
ابتدا یک Class Library به نام ModuleBook بسازید و همانند ماژول قبلی نیاز به یک Window و یک کلاس داریم:
BookWindow که کاملا مشابه به CategoryView است.
<UserControl x:Class="FirstPrismSample.ModuleBook.BookView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="LightGray" FontFamily="Tahoma" FlowDirection="RightToLeft"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Text="لیست کتاب ها"/> <ListView Grid.Row="1" Margin="10" Name="lvBook"> <ListView.View> <GridView> <GridViewColumn Header="کد" Width="50" /> <GridViewColumn Header="عنوان" Width="200" /> <GridViewColumn Header="نویسنده" Width="150" /> </GridView> </ListView.View> </ListView> </Grid> </UserControl>
کلاس BookModule که پیاده سازی و توضیحات آن کاملا مشابه به CategoryModule میباشد.
using Microsoft.Practices.Composite.Events; using Microsoft.Practices.Composite.Modularity; using Microsoft.Practices.Composite.Presentation.Events; using Microsoft.Practices.Composite.Regions; using Microsoft.Practices.Unity; using FirstPrismSample.Common; using FirstPrismSample.Common.Events; namespace FirstPrismSample.ModuleBook { [Module(ModuleName = "moduleBook")] public class BookModule : IModule { private readonly IUnityContainer m_Container; private readonly string moduleName = "ModuleBook"; public BookModule(IUnityContainer container) { m_Container = container; } ~BookModule() { var eventAggregator = m_Container.Resolve<IEventAggregator>(); var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>(); viewRequestedEvent.Unsubscribe(ViewRequestedEventHandler); } public void Initialize() { var regionManager = m_Container.Resolve<IRegionManager>(); var view = new BookView(); regionManager.Regions["WorkspaceRegion"].Add(view, moduleName); regionManager.Regions["WorkspaceRegion"].Deactivate(view); var eventAggregator = m_Container.Resolve<IEventAggregator>(); var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>(); viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, true); } public void ViewRequestedEventHandler(string moduleName) { if (this.moduleName != moduleName) return; var moduleServices = m_Container.Resolve<IModuleServices>(); moduleServices.ActivateView(m_WorkspaceBName); } } }
برای این ماژول هم ابتدا View مورد نظر را ایجاد میکنیم:
<UserControl x:Class="FirstPrismSample.ModuleNavigator.NavigatorView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Grid> <StackPanel VerticalAlignment="Center"> <TextBlock Text="انتخاب ماژول" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Tahoma" FontSize="24" FontWeight="Bold" /> <Button Command="{Binding ShowModuleCategory}" Margin="5" Width="125">طبقه بندی کتاب ها</Button> <Button Command="{Binding ShowModuleBook}" Margin="5" Width="125">لیست کتاب ها</Button> </StackPanel> </Grid> </UserControl>
public interface INavigatorViewModel { ICommand ShowModuleCategory { get; set; } ICommand ShowModuleBook { get; set; } string ActiveWorkspace { get; set; } IUnityContainer Container { get; set; } event PropertyChangedEventHandler PropertyChanged; }
*خاصیت ActiveWorkspace برای تعیین workspace فعال تعریف شده است.
حال به پیاده سازی مثال بالا میپردازیم:
public class NavigatorViewModel : ViewModelBase, INavigatorViewModel { public NavigatorViewModel(IUnityContainer container) { this.Initialize(container); } public ICommand ShowModuleCategory { get; set; } public ICommand ShowModuleBook { get; set; } public string ActiveWorkspace { get; set; } public IUnityContainer Container { get; set; } private void Initialize(IUnityContainer container) { this.Container = container; this.ShowModuleCategory = new ShowModuleCategoryCommand(this); this.ShowModuleBook = new ShowModuleBookCommand(this); this.ActiveWorkspace = "ModuleCategory"; } }
public class ShowModuleCategoryCommand : ICommand { private readonly NavigatorViewModel viewModel; private const string workspaceName = "ModuleCategory"; public ShowModuleCategoryCommand(NavigatorViewModel viewModel) { this.viewModel = viewModel; } public bool CanExecute(object parameter) { return viewModel.ActiveWorkspace != workspaceName; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { CommandServices.ShowWorkspace(workspaceName, viewModel); } }
public class ShowModuleBookCommand : ICommand { private readonly NavigatorViewModel viewModel; private readonly string workspaceName = "ModuleBook"; public ShowModuleBookCommand( NavigatorViewModel viewModel ) { this.viewModel = viewModel; } public bool CanExecute( object parameter ) { return viewModel.ActiveWorkspace != workspaceName; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute( object parameter ) { CommandServices.ShowWorkspace( workspaceName , viewModel ); } }
public static void ShowWorkspace(string workspaceName, INavigatorViewModel viewModel) { var eventAggregator = viewModel.Container.Resolve<IEventAggregator>(); var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>(); viewRequestedEvent.Publish(workspaceName); viewModel.ActiveWorkspace = workspaceName; }
عدم وابستگی ماژول ها
xcopy "$(TargetDir)FirstPrismSample.ModuleBook.dll" "$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
مانند:
مراحل بالا برای هر ماژول باید تکرار شود(ModuleNavigation , ModuleBook , ModuleCategory). بعد از Rebuild پروژه در فولدر bin پروژه اصلی یک فولدر به نام Module ایجاد میشود که اسمبلی هر ماژول در آن کپی خواهد شد.
ایجاد Bootstrapper
حال نوبت به Bootstrapper میرسد(در پست قبلی در باره مفهوم Bootstrapper شرح داده شد). در پروژه اصلی یعنی جایی که فایل App.xaml قرار دارد کلاس زیر را ایجاد کنید.
public class Bootstrapper : UnityBootstrapper { protected override void ConfigureContainer() { base.ConfigureContainer(); Container.RegisterType<IModuleServices, ModuleServices>(); } protected override DependencyObject CreateShell() { var shell = new Shell(); shell.Show(); return shell; } protected override IModuleCatalog GetModuleCatalog() { var catalog = new DirectoryModuleCatalog(); catalog.ModulePath = @".\Modules"; return catalog; } }
متد GetModuleCatalog برای تعیین مسیر ماژولها در پروژه کاربرد دارد. در این متد با استفاده از خاصیت ModulePath کلاس DirectoryModuleCatalog تعیین کرده ایم که ماژولهای پروژه در فولدر Modules موجود در bin اصلی پروژه قرار دارد. اگر به دستورات کپی در Post Build Event قسمت قبل توجه کنید میبینید که دستور ساخت فولدر وجود دارد.
"$(SolutionDir)FirstPrismSample\bin\$(ConfigurationName)\Modules\" /Y
در پایان باید فایل App.xaml را تغییر دهید به گونه ای که متد Run در کلاس Bootstapper ابتدا اجرا شود.
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var bootstrapper = new Bootstrapper(); bootstrapper.Run(); } }
اجرای پروژه:
بعد از اجرا، با انتخاب ماژول مورد نظر اطلاعات ماژول در Workspace Content Control لود خواهد شد.
ادامه دارد...