۱۳۸۹/۰۵/۰۸

راهبري در Silverlight به كمك الگوي MVVM


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