<ul id="treeview" data-role="treeview"> <li data-expanded="true"> <span class="k-sprite folder"></span> My Web Site <ul> <li data-expanded="true"> <span class="k-sprite folder"></span>images <ul> <li><span class="k-sprite image"></span>logo.png</li> <li><span class="k-sprite image"></span>body-back.png</li> <li><span class="k-sprite image"></span>my-photo.jpg</li> </ul> </li> <li data-expanded="true"> <span class="k-sprite folder"></span>resources <ul> <li data-expanded="true"> <span class="k-sprite folder"></span>pdf <ul> <li><span class="k-sprite pdf"></span>brochure.pdf</li> <li><span class="k-sprite pdf"></span>prices.pdf</li> </ul> </li> <li><span class="k-sprite folder"></span>zip</li> </ul> </li> </ul> </li> </ul>
- Convert.Net
- Regular Expression Tester : که جهت تست Regexهای نوشته شده استفاده میشود.
- Encoding & Decoding : جهت تبدیل انواع رشتههای Encoded ویا Decoded به یکدیگر استفاده میشود و از html - url - EScape-js - Base64 - string و ... پشتیبانی میکند.
- Encryption & Decryption : جهت Encrypt و Decrypt انواع رشته استفاده میشود که از انکریپتورهای معروفی همچون AES - Rijndael - DES - SHA - TripleDES پشتیبانی میکند.
- Language Translation : یک دیکشنری Multi Language آنلاین در اختیار شما برای ترجمه متون قرار میدهد.
- C# & VB.Net Convertor : برای تبدیل کدهای C# به Vb و برعکس استفاده میشود و طبق تست هایی که روش به شخصه انجام دادم در اکثر موارد بدون خطا تا حدود 90 درصد تبدیلات رو به صورت موفق انجام میده ولی مانند سایر Convertorها با Lambda Expression کمی مشکل دارد.
- Xml & Json Browser : برای مشاهده و تبدیل Xml به Json و برعکس بسیار مفید است ..
- Linq Tester : برای تست کوئریهای Linq استفاده میشود . (برای استفاده از این امکان باید Roslyne روی سیستم شما نصب باشد)
- Database.Net
SQL Server 2000/2005/2008/2012/2014/Express/LocalDB SQL Server Compact 3.1/3.5/4.0 (*.sdf;*.*) SQL Azure 10/11 MS Access 97/2000/2002/2003 (*.mdb;*.mde;*.*), 2007/2010/2013 (*.accdb;*.accde;*.*) MS Excel 97/2000/2002/2003(*.xls;*.*), 2007/2010/2013 (*.xlsx;*.xlsm;*.xlsb;*.*) Firebird SuperServer/Embedded 1.5/2.0/2.1/2.5 (*.gdb;*.fdb;*.*) SQLite 3.x (*.db;*.db3;*.sqlite;*.*) MySQL 5.x, MariaDB 5.x/10.x PostgreSQL 8.x/9.x Oracle 10g/11g/12c IBM DB2 9.x/10.x IBM Informix 11.x/12.x Sybase ASE 15.x dBASE IV (*.dbf) Visual FoxPro (*.dbc) Data Sources (OleDB)(*.udl;*.*) ODBC DSN (Data Source Name)(*.dsn;*.*) OData (Open Data Protocol) v1/v2/v3/v4
- Resource.Net
سایر نرم افزارهای این تیم هم مانند نرم افزارهای معرفی شده بین کاربران محبوبیت زیادی کسب کرده اند که میتوانید برای کسب اطلاعات بیشتر و دانلود این نرم افزارها به وب سایت این تیم مراجعه فرمائید.
+ وابستگی WindowsBase هم یک فایل استاندارد مجموعه دات نت است و نه خارج از آن و در WPF از آن استفاده میشود. بنابراین مشکلی ندارد (چون حتی اگر یک dll بومی ویندوز هم باشد، با توجه به اینکه عملا دو نگارش X86 و X64 دات نت وجود دارند، بسته به سیستم مورد استفاده، یکی از آنها به صورت خودکار در ابتدای کار نصب دات نت فریم ورک، نصب خواهد شد).
با توجه به آخرین نگارشهای موجود Angular و React، انتخاب شما برای انجام یک پروژه بزرگ کدام است؟
مدتها از کلاس DelegateCommand معرفی شده در این آدرس استفاده میکردم. این کلاس یک مشکل جزئی دارد و آن هم عدم بررسی مجدد قسمت canExecute به صورت خودکار هست.
خلاصهای برای کسانی که بار اول هست با این مباحث برخورد میکنند؛ یا MVVM به زبان بسیار ساده:
در برنامه نویسی متداول سیستم مایکروسافتی، در هر سیستمی که ایجاد کرده و در هر فناوری که ارائه داده از زمان VB6 تا امروز، شما روی یک دکمه مثلا دوبار کلیک میکنید و در فایل اصطلاحا code behind این فرم و در روال رخدادگردان آن شروع به کد نویسی خواهید کرد. این مورد تقریبا در همه جا صادق است؛ از WinForms تا WPF تا Silverlight تا حتی ASP.NET Webforms . به عمد هم این طراحی صورت گرفته تا برنامه نویسها در این محیطها زیاد احساس غریبی نکنند. اما این روش یک مشکل مهم دارد و آن هم «توهم» جداسازی رابط کاربر از کدهای برنامه است. به ظاهر یک فایل فرم وجود دارد و یک فایل جدای code behind ؛ اما در عمل هر دوی اینها یک partial class یا به عبارتی «یک کلاس» بیشتر نیستند. «فکر میکنیم» که از هم جدا شدند اما واقعا یکی هستند. شما در code behind صفحه به صورت مستقیم با عناصر رابط کاربری سروکار دارید و کدهای شما به این عناصر گره خوردهاند.
شاید بپرسید که چه اهمیتی دارد؟
مشکل اول: امکان نوشتن آزمونها واحد برای این متدها وجود ندارد یا بسیار سخت است. این متدها فقط با وجود فرم و رابط کاربری متناظر با آنها هست که معنا پیدا میکنند و تک تک عناصر آنها وهله سازی میشوند.
مشکل دوم: کد نوشته فقط برای همین فرم جاری آن قابل استفاده است؛ چون به صورت صریح به عناصر موجود در فرم اشاره میکند. نمیتونید این فایل code behind رو بردارید بدون هیچ تغییری برای فرم دیگری استفاده کنید.
مشکل سوم: نمیتونید طراحی فرم رو بدید به یک نفر، کد نویسی اون رو به شخصی دیگر. چون ایندو لازم و ملزوم یکدیگرند.
این سیستم کد نویسی دهه 90 است.
چند سالی است که طراحان سعی کردهاند این سیستم رو دور بزنند و روشهایی رو ارائه بدن که در آنها فرمهای برنامه و فایلهای پیاده سازی کنندهی منطق آن هیچگونه ارتباط مستقیمی باهم نداشته باشند؛ به هم گره نخورده باشند؛ ارجاعی به هیچیک از عناصر بصری فرم را در خود نداشته باشند. به همین دلیل ASP.NET MVC به وجود آمده و در همان سالها مثلا MVVM .
سؤال:
الان که رابط کاربری از فایل پیاده سازی کننده منطق آن جدا شده و دیگر Code behind هم نیست (همان partial class های متداول)، این فایلها چطور متوجه میشوند که مثلا روی یک فرم، شیءایی قرار گرفته؟ از کجا متوجه خواهند شد که روی دکمهای کلیک شده؟ اینها که ارجاعی از فرم را در درون خود ندارند.
در الگوی MVVM این سیم کشی توسط امکانات قوی Binding موجود در WPF میسر میشود. در ASP.NET MVC چیزی شبیه به آن به نام Model binder و همان مکانیزمهای استاندارد HTTP این کار رو میکنه. در MVVM شما بجای code behind خواهید داشت ViewModel (اسم جدید آن). در ASP.NET MVC این اسم شده Controller. بنابراین اگر این اسامی رو شنیدید زیاد تعجب نکنید. اینها همان Code behind قدیمی هستند اما ... بدون داشتن ارجاعی از رابط کاربری در خود که ... اطلاعات موجود در فرم به نحوی به آنها Bind و ارسال میشوند.
این سیم کشیها هم نامرئی هستند. یعنی فایل ViewModel یا فایل Controller نمیدونند که دقیقا از چه کنترلی در چه فرمی این اطلاعات دریافت شده.
این ایده هم جدید نیست. شاید بد نباشه به دوران طلایی Win32 برگردیم. همان توابع معروف PostMessage و SendMessage را به خاطر دارید؟ شما در یک ترد میتونید با مثلا PostMessage شیءایی رو به یک فرم که در حال گوش فرا دادن به تغییرات است ارسال کنید (این سیم کشی هم نامرئی است). بنابراین پیاده سازی این الگوها حتی در Win32 و کلیه فریم ورکهای ساخته شده بر پایه آنها مانند VCL ، VB6 ، WinForms و غیره ... «از روز اول» وجود داشته و میتونستند بعد از 10 سال نیان بگن که اون روشهای RAD ایی رو که ما پیشنهاد دادیم، میشد خیلی بهتر از همان ابتدا، طور دیگری پیاده سازی بشه.
ادامه بحث!
این سیم کشی یا اصطلاحا Binding ، در مورد رخدادها هم در WPF وجود داره و اینبار به نام Commands معرفی شدهاست. به این معنا که بجای اینکه بنویسید:
<Button Click="btnClick_Event">Last</Button>
بنویسید:
<Button Command="{Binding GoLast}">Last</Button>
حالا باید مکانیزمی وجود داشته باشه تا این پیغام رو به ViewModel برنامه برساند. اینکار با پیاده سازی اینترفیس ICommand قابل انجام است که معرفی یک کلاس عمومی از پیاده سازی آنرا در ابتدای بحث مشاهده نمودید.
در یک DelegateCommand، توسط متد منتسب به executeAction، مشخص خواهیم کرد که اگر این سیم کشی برقرار شد (که ما دقیقا نمیدانیم و نمیخواهیم که بدانیم از کجا و کدام فرم دقیقا)، لطفا این اعمال را انجام بده و توسط متد منتسب به canExecute به سیستم Binding خواهیم گفت که آیا مجاز هستی این اعمال را انجام دهی یا خیر. اگر این متد false برگرداند، مثلا دکمه یاد شده به صورت خودکار غیرفعال میشود.
اما مشکل کلاس DelegateCommand ذکر شده هم دقیقا همینجا است. این دکمه تا ابد غیرفعال خواهد ماند. در WPF کلاسی وجود دارد به نام CommandManager که حاوی متدی استاتیکی است به نام InvalidateRequerySuggested. اگر این متد به صورت دستی فراخوانی شود، یکبار دیگر کلیه متدهای منتسب به تمام canExecute های تعریف شده، به صورت خودکار اجرا میشوند و اینجا است که میتوان دکمهای را که باید مجددا بر اساس شرایط جاری تغییر وضعیت پیدا کند، فعال کرد. بنابراین فراخوانی متد InvalidateRequerySuggested یک راه حل کلی رفع نقیصهی ذکر شده است.
راه حل دومی هم برای حل این مشکل وجود دارد. میتوان از رخدادگردان CommandManager.RequerySuggested استفاده کرد. روال منتسب به این رخدادگردان هر زمانی که احساس کند تغییری در UI رخ داده، فراخوانی میشود. بنابراین پیاده سازی بهبود یافته کلاس DelegateCommand به صورت زیر خواهد بود:
using System;
using System.Windows.Input;
namespace MvvmHelpers
{
// Ref.
// - http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/
// - http://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/
public class DelegateCommand<T> : ICommand
{
readonly Func<T, bool> _canExecute;
bool _canExecuteCache;
readonly Action<T> _executeAction;
public DelegateCommand(Action<T> executeAction, Func<T, bool> canExecute = null)
{
if (executeAction == null)
throw new ArgumentNullException("executeAction");
_executeAction = executeAction;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { if (_canExecute != null) CommandManager.RequerySuggested += value; }
remove { if (_canExecute != null) CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_executeAction((T)parameter);
}
}
}
استفاده از آن هم در ViewModel ساده است. یکبار خاصیتی به این نام تعریف میشود. سپس در سازنده کلاس مقدار دهی شده و متدهای متناظر آن تعریف خواهند شد:
public DelegateCommand<string> GoLast { set; get; }
//in ctor
GoLast = new DelegateCommand<string>(goLast, canGoLast);
private bool canGoLast(string data)
{
//ex.
return ListViewGuiData.CurrentPage != ListViewGuiData.TotalPage - 1;
}
private void goLast(string data)
{
//do something
}
مزیت کلاس DelegateCommand جدید هم این است که مثلا متد canGoLast فوق، به صورت خودکار با به روز رسانی UI ، فراخوانی و تعیین اعتبار مجدد میشود.
namespace ReUsableQueries.Model { public class Student { public int Id { get; set; } public string Name { get; set; } public string LastName { get; set; } public int Age { get; set; } [ForeignKey("BornInCityId")] public virtual City BornInCity { get; set; } public int BornInCityId { get; set; } } public class City { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Student> Students { get; set; } } }
using System.Data.Entity; using ReUsableQueries.Model; namespace ReUsableQueries.DAL { public class MyContext : DbContext { public DbSet<City> Cities { get; set; } public DbSet<Student> Students { get; set; } } }
public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { var city1 = new City { Name = "city-1" }; var city2 = new City { Name = "city-2" }; context.Cities.Add(city1); context.Cities.Add(city2); var student1 = new Student() {Name = "Shaahin",LastName = "Kiassat",Age=22,BornInCity = city1}; var student2 = new Student() { Name = "Mehdi", LastName = "Farzad", Age = 31, BornInCity = city1 }; var student3 = new Student() { Name = "James", LastName = "Hetfield", Age = 49, BornInCity = city2 }; context.Students.Add(student1); context.Students.Add(student2); context.Students.Add(student3); base.Seed(context); } }
var context = new MyContext(); var query= context.Students.Where(x => x.Name.Contains(name)).Where(x => x.LastName.Contains(lastName)).Where( x => x.Age == age);
var query= context.Students.Where(x => x.Name.Contains(name)).Where(x => x.LastName.Contains(lastName)).Where( x => x.Age == age).OrderBy(x=>x.LastName).Skip(skip).Take(take);
var query = context.Students.Where(x => x.Name.Contains(name)).Where(x => x.LastName.Contains(lastName)).Where ( x => x.Age == age).Where(x => x.BornInCityId == 1).OrderBy(x => x.Age);
namespace ReUsableQueries.Quries { public static class StudentQueryExtension { public static IQueryable<Student> FindStudentsByName(this IQueryable<Student> students,string name) { return students.Where(x => x.Name.Contains(name)); } public static IQueryable<Student> FindStudentsByLastName(this IQueryable<Student> students, string lastName) { return students.Where(x => x.LastName.Contains(lastName)); } public static IQueryable<Student> SkipAndTake(this IQueryable<Student> students, int skip , int take) { return students.Skip(skip).Take(take); } public static IQueryable<Student> OrderByAge(this IQueryable<Student> students) { return students.OrderBy(x=>x.Age); } } }
var query = context.Students.FindStudentsByName(name).FindStudentsByLastName(lastName).SkipAndTake(skip,take);
var query = context.Students.AsQueryable(); if (searchByName) { query= query.FindStudentsByName(name); } if (orderByAge) { query = query.OrderByAge(); } if (paging) { query = query.SkipAndTake(skip, take); } return query.ToList();
کلیدهای مربوط به Request
ضروری؟ | نام کلید | مقدار |
بله | "owin.RequestBody" | یک Stream همراه با request body. اگر body برای request وجود نداشته باشد، Stream.Null به عنوان placeholder قابل استفاده است. |
بله | "owin.RequestHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای درخواست. |
بله | "owin.RequestMethod" | رشتهایی حاوی نوع فعل متد HTTP مربوط به درخواست (مانند GET and POST ) |
بله | "owin.RequestPath" | path درخواست شده به صورت string |
بله | "owin.RequestPathBase" | قسمتی از path درخواست به صورت string |
بله | "owin.RequestProtocol" | نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 ) |
بله | "owin.RequestQueryString" | رشتهای حاوی query string ؛ بدون علامت ? (مانند foo=bar&baz=quux ) |
بله | "owin.RequestScheme" | رشتهایی حاوی URL scheme استفاده شده در درخواست (مانند HTTP or HTTPS ) |
ضروری؟ | نام کلید | مقدار |
بله | "owin.ResponseBody" | یک Stream جهت نوشتن response body در خروجی |
بله | "owin.ResponseHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای response |
خیر | "owin.ResponseStatusCode" | یک عدد صحیح حاوی کد وضعیت HTTP response ؛ حالت پیشفرض 200 است. |
خیر | "owin.ResponseReasonPhrase" | یک رشته حاوی reason phrase مربوط به status code ؛ اگر خالی باشد در نتیجه سرور بهتر است آن را مقداردهی کند. |
خیر | "owin.ResponseProtocol" | یک رشته حاوی نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 )؛ اگر خالی باشد؛ “owin.RequestProtocol” به عنوان مقدار پیشفرض در نظر گرفته خواهد شد. |
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net461" /> <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net461" /> <package id="Owin" version="1.0" targetFramework="net461" />
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { } } }
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { app.Use(async (ctx, next) => { await ctx.Response.WriteAsync("Hello"); }); } }
Func<IOwinContext, Func<Task>, Task> handler
app.Use(async (ctx, next) => { var response = ctx.Environment["owin.ResponseBody"] as Stream; using (var writer = new StreamWriter(response)) { await writer.WriteAsync("Hello"); } });
using System; using Microsoft.Owin.Hosting; namespace SimpleOwinConsoleApp { class Program { static void Main(string[] args) { using (WebApp.Start<Startup>("http://localhost:12345")) { Console.WriteLine("Listening to port 12345"); Console.WriteLine("Press Enter to end..."); Console.ReadLine(); } } } }
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace SimpleOwinCoreApp { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinCoreApp.Middlewares { public class SimpleMiddleware { private readonly RequestDelegate _next; public SimpleMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext ctx) { // قبل از فراخوانی میانافزار بعدی await ctx.Response.WriteAsync("Hello DNT!"); await _next(ctx); // بعد از فراخوانی میانافزار بعدی } } }
app.UseMiddleware<SimpleMiddleware>();
"Microsoft.AspNetCore.Owin": "1.0.0"
app.UseOwin(pipeline => { pipeline(next => new MyKatanaBasedMiddleware(next).Invoke) });
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerMiddleware { private readonly RequestDelegate _next; private readonly IpBlockerOptions _options; public IpBlockerMiddleware(RequestDelegate next, IpBlockerOptions options) { _next = next; _options = options; } public async Task Invoke(HttpContext context) { var ipAddress = context.Request.Host.Host; if (IsBlockedIpAddress(ipAddress)) { context.Response.StatusCode = 403; await context.Response.WriteAsync("Forbidden : The server understood the request, but It is refusing to fulfill it."); return; } await _next.Invoke(context); } private bool IsBlockedIpAddress(string ipAddress) { return _options.Ips.Any(ip => ip == ipAddress); } } }
using System.Collections.Generic; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerOptions { public IpBlockerOptions() { Ips = new[] { "192.168.1.1" }; } public IList<string> Ips { get; set; } } }
using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; namespace SimpleOwinAspNetCore.Middleware { public static class IpBlockerExtensions { public static IApplicationBuilder UseIpBlocker(this IApplicationBuilder builder, IConfigurationRoot configuration, IpBlockerOptions options = null) { return builder.UseMiddleware<IpBlockerMiddleware>(options ?? new IpBlockerOptions { Ips = configuration.GetSection("block_list").GetChildren().Select(p => p.Value).ToArray() }); } } }
{ "block_list": [ "192.168.1.1", "localhost", "127.0.0.1", "172.16.132.151" ] }
public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("blockedIps.json"); Configuration = builder.Build(); }
app.UseIpBlocker(Configuration);
پروژه C# to JavaScript Compiler
بررسی واژه کلیدی static
در دات نت میکرو برخی کلاس ها را که در رابطه با پورت ها بودند و همچنین برای ساخت اکستنشن برای html helper،دات نت آنها را بصورت استاتیک تعریف کرده است.چگونه می توان از GC در کلاس ها،متغییر ها و تابع های استاتیک سود برد؟
ممنون،
مقدمات راهبری (Navigation) در سیلورلایت را در اینجا میتوانید مطالعه نمائید : +
مطلبی را که در فصل فوق نخواهید یافت در مورد نحوهی بکارگیری الگوی MVVM جهت پیاده سازی Navigation در یک برنامهی سیلورلایت است؛ علت آن هم به این بر میگردد که این فصل پیش از مباحث Binding مطرح شد.
صورت مساله:
یکی از اصول MVVM این است که در ViewModel نباید ارجاعی از View وجود داشته باشد (ViewModel باید در بیخبری کامل از وجود اشیاء UI و ارجاع مستقیم به آنها طراحی شود)، اما برای پیاده سازی مباحث Navigation نیاز است به نحوی به شیء Frame قرار داده شده در صفحهی اصلی یا قالب اصلی برنامه دسترسی یافت تا بتوان درخواست رهنمون شدن به صفحات مختلف را صادر کرد. اکنون چکار باید کرد؟
راه حل:
یکی از راه حلهای جالبی که برای این منظور وجود دارد استفاده از امکانات کلاس Messenger مجموعهی MVVM Light toolkit است. از طریق ViewModel برنامه، آدرس صفحهی مورد نظر را به صورت یک پیغام به View مورد نظر ارسال میکنیم و سپس View برنامه که به این پیغامها گوش فرا میدهد، پس از دریافت آدرس مورد نظر، نسبت به فراخوانی تابع Navigate شیء Frame رابط کاربری برنامه اقدام خواهد کرد. به این صورت ViewModel برنامه به View خود جهت اعمال راهبری برنامه، گره نخواهد خورد.
روش پیاده سازی:
ابتدا ساختار پروژه را در نظر بگیرید (این شکل دگرگون شدهی Solution explorer مرتبط است با productivity tools نصب شده):
در پوشهی Views ، دو صفحه اضافه شدهاند که توسط user control ایی به نام menu لیست شده و راهبری خواهند شد. مونتاژ نهایی هم در MainPage.xaml صورت میگیرد.
کدهای XAML مرتبط با منوی ساده برنامه به شرح زیر هستند (Menu.xaml) :
<UserControl x:Class="MvvmLight6.Views.Menu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:MvvmLight6.ViewModels" mc:Ignorable="d"
FlowDirection="RightToLeft" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<vm:MenuViewModel x:Key="vmMenuViewModel" />
</UserControl.Resources>
<StackPanel DataContext="{Binding Source={StaticResource vmMenuViewModel}}">
<HyperlinkButton Content="صفحه یک" Margin="5"
Command="{Binding DoNavigate}"
CommandParameter="/Views/Page1.xaml"
/>
<HyperlinkButton Content="صفحه دو" Margin="5"
Command="{Binding DoNavigate}"
CommandParameter="/Views/Page2.xaml"
/>
</StackPanel>
</UserControl>
کدهای ViewModel مرتبط با این View که کار Command گردانی را انجام خواهد داد به شرح زیر است:
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace MvvmLight6.ViewModels
{
public class MenuViewModel
{
public RelayCommand<string> DoNavigate { set; get; }
public MenuViewModel()
{
DoNavigate = new RelayCommand<string>(doNavigate);
}
private static void doNavigate(string url)
{
Messenger.Default.Send(url, "MyNavigationService");
}
}
}
تمام آیتمهای منوی فوق یک روال را صدا خواهند زد : DoNavigate . تنها تفاوت آنها در CommandParameter ارسالی به RelayCommand ما است که حاوی آدرس قرارگیری فایلهای صفحات تعریف شده است. این آدرسها با کمک امکانات کلاس Messenger مجموعهی MVVM light toolkit به View اصلی برنامه ارسال میگردند.
کدهای XAML مرتبط با MainPage.xaml به شرح زیر هستند:
<UserControl x:Class="MvvmLight6.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:usr="clr-namespace:MvvmLight6.Views"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="268" />
</Grid.ColumnDefinitions>
<usr:Menu Grid.Column="1" />
<sdk:Frame Margin="5"
Name="frame1"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Grid.Column="0" />
</Grid>
</UserControl>
و کار دریافت پیغامها (یا همان آدرس صفحات جهت انجام راهبری) و عکس العمل نشان دادن به آنها توسط کدهای ذیل صورت خواهد گرفت:
using System;
using GalaSoft.MvvmLight.Messaging;
namespace MvvmLight6
{
public partial class MainPage
{
public MainPage()
{
registerMessenger();
InitializeComponent();
}
private void registerMessenger()
{
Messenger.Default.Register<string>(this, "MyNavigationService", doNavigate);
}
private void doNavigate(string uri)
{
frame1.Navigate(new Uri(uri, UriKind.Relative));
}
}
}
ابتدا یک Messenger در اینجا رجیستر میشود و سپس به ازای هر بار دریافت پیغامی با token مساوی MyNavigationService ، متد doNavigate فراخوانی خواهد گردید.
کدهای این مثال را از اینجا میتوانید دریافت کنید.