عنوان:

‫راهبری در Silverlight به کمک الگوی MVVM


نویسنده: وحید نصیری
تاریخ: ۱۳۸۹/۰۵/۰۸ ۲۲:۲۵:۰۰
آدرس: www.dntips.ir

مقدمات راهبری (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 فراخوانی خواهد گردید.
کدهای این مثال را از اینجا می‌توانید دریافت کنید.

نظرات

  • مهمان در ۱۳۸۹/۰۵/۰۸ ۲۳:۳۵:۳۰
    سلام آقای نصیری
    جا داره یک خسته نباشید بهتون بگم، من واقعاً از آموزش های MVVM شما استفاده کردم و از این بابت از شما ممنونم.
  • وحید نصیری در ۱۳۸۹/۰۵/۰۸ ۲۳:۴۵:۱۸
    لطف دارید. سلامت باشید.
  • مهمان در ۱۳۸۹/۰۶/۲۰ ۱۱:۱۲:۴۴
    با سلام من یک سوالی در باره RelayCommand دارم من از وبی استفاده می کنم و زمان استفاده از RelayCommand اون رو نمی شناسه میشه بگین تو چه nameSpace هست
  • وحید نصیری در ۱۳۸۹/۰۶/۲۰ ۱۷:۴۰:۲۲
    ارجاعی را به اسمبلی‌های MVVM Light toolkit اضافه کنید.
  • مهمان در ۱۳۸۹/۰۶/۲۰ ۲۱:۰۹:۳۶
    سلام
    چطوری یک Concrete Class رو از یک صفحه به یک صفحه دیگر پاس بدم؟(با استفاده از این الگو یا هر روش دیگر)
  • وحید نصیری در ۱۳۸۹/۰۶/۲۰ ۲۱:۴۰:۱۰
    یک روش : برای پاس دادن یک وهله از یک کلاس به کلاسی دیگر در Silverlight یا WPF (مثلا در حین Navigation) می‌تونید از امکانات کلاس Messenger مجموعه‌ی MVVM Light toolkit استفاده کنید.
    توضیحات مرتبط با هر کدام در جزوه MVVM (برای WPF) یا کتاب Silverlight منتشر شده موجود است.

    مثال فوق هم به همین ترتیب یک رشته را از یک کلاس به کلاس دیگر پاس داده است.
  • مهمان در ۱۳۸۹/۰۶/۲۰ ۲۲:۱۵:۵۷
    صورت مسئله اینجور هستش که ، یک صفحه دارم که از Frame برای راهبری استفاده میکنه(بدون هیچگونه کتابخونه ای و با استفاده از الگوی MVVM) ، به این صورت که متد Navigate فریم مربوطه به یک Func در یک VM وصل شده که در Command یک دکمه آنرا فراخوانی میکند. در واقع هیچ اختیاری بین اشیا VM ندارم!
  • وحید نصیری در ۱۳۸۹/۰۶/۲۰ ۲۳:۰۸:۵۹
    این هم خوبه؛ شبیه به ارسال اطلاعات به کمک یک delegate یا event . از سر ناچاری!
    - ضمنا MVVM Light toolkit‌ سورس باز است. کلاس Messenger آن‌را جدا کنید و استفاده کنید (اگر از کل آن نمی‌خواهید استفاده کنید).

    یک روش هم اینجا دیدم که خیلی جالب است:
    http://forums.silverlight.net/forums/p/198684/463126.aspx
    از NavigateUri یک HyperlinkButton استفاده کرده. فقط UriMapper را هم تنظیم کرده برای زیبایی کار.
    (نیاز به هیچ کتابخانه جانبی هم ندارد. نیازی به دخالت MVVM هم ندارد.و مهم‌تر از همه، نیازی به کد نویسی هم اصلا ندارد.)

    ولی برای پاس دادن یک وهله از صفحه جاری به صفحه بعد (مثل لینک داخل صفحات وب)، کلاس Messenger واقعا تر و تمیز و عالی است. (نیازی هم به استفاده از کوئری استرینگ یا هر روش دیگری نیست)
  • وحید نصیری در ۱۳۸۹/۰۶/۲۰ ۲۳:۱۴:۳۶
    علاوه بر این‌ها برای انتقال وهله از MEF هم می‌شود استفاده کرد (managed extensibility framework)، که می‌تواند singleton باشد یا به ازای هر بار استفاده وهله سازی شود. (در موردش قبلا مطلب نوشتم در سایت)
  • مهمان در ۱۳۸۹/۰۸/۰۹ ۲۳:۲۱:۰۲
    از زحمتی که برای مجموعه آموزشی سیلورلایت کشیدید واقعا متشکرم.