مشخصات مقاله
استفاده از الگوی MVVM
کلیه حقوق مادی و معنوی این مقاله متعلق به آموزشگاه تحلیل داده می باشد و هر گونه استفاده غیر قانونی از آن پیگرد قانونی دارد .
استفاده از الگوی MVVM
پس از این آموزش خواهید توانست کد خود را براساس الگوی MVVM تغییر دهید. الگوی Model-View-ViewModel کمک می کند تامنطق برنامه نویسی را از رابط کاربری (ظاهر برنامه) جداسازی کنیم. تفکیک منطق و ظاهر برنامه زمینه طراحی و توسعه بسیاری از مسائل است و تست، نگهداری و تکامل برنامه شما را راحت تر می کند. همچنین می توان شانس استفاده مجدد از کد را تا حد زیادی بالا برد و به برنامه نویسان و طراحان رابط کاربری این اجازه را می دهد که هنگام توسعه بخش مربوط به خود در برنامه، به راحتی با هم تعامل داشته باشند.
در نمودار زیر نحوه کارکرد الگوی MVVM را مشاهده می کنید:
در Session App می توانید دو View models برای هر صفحه (Page) ایجاد کنید. به منظور تطبیق viewها و ViewModelها، باید موارد زیر را داشته باشید:
· SessionsView به SessionViewModel متصل می شود.
· SessionDetailsView به SessionDetailsViewModel متصل می شود.
معمولاً تمام View modelها در پوشه ViewModels تعریف می شوند. سپس هر برنامه نویس می تواند تشخیص دهد که برنامه از الگوی MVVM استفاده می کند و به راحتی می توان Viewها و ViewModelها را تطبیق داد. (هر برنامه نویس می تواند برنامه را براساس نیازهای برنامه، سازمان دهی کند)
حالا بیایید ViewModelها را ایجاد کنیم:
در پروژه ENEI.SessionsApp یک فولدر به نام ViewModels و یک کلاس به نام SessionViewModel ایجاد کنیم. نتیجه باید مشابه زیر باشد:
حالا باید کد SessionView.xaml.cs را تغییر دهیم. برای این منظور یک کلاس به نام SessionViewModel ایجاد میکنیم:
public class SessionViewModel
{
public SessionViewModel()
{
Sessions = new ObservableCollection<Session>();
}
public ObservableCollection<Session> Sessions { get; set; }
public async Task LoadDataAsync()
{
await Task.Run(() =>
{
if (Sessions.Count == 0)
{
var sessions = SessionsDataSource.GetSessions();
foreach (var session in sessions)
{
Sessions.Add(session);
}
}
});
}
} به این ترتیب لیست Sessionها و LoadDataAsync را در ViewModel تعریف کرده اید. حالا باید برای هر گزینه در منو یک دستور ایجاد کنید.
در SessionsView.xaml.cs یک event handler برای رخداد tap خواهید داشت (برای Like، Favorate، Share و SessionDetails) که در XAML به شکل زیر تعریف می شوند:
<Image.GestureRecognizers>
<TapGestureRecognizer x:Name="DetailsGesture" CommandParameter="{Binding}" Tapped="DetailsGesture_OnTapped" />
</Image.GestureRecognizers>
این event handler برای پیاده سازی الگوی MVVM مناسب نیست. از اینرو برای حل این مشکل یک اینترفیس به نام ICommand ایجاد کنید تا عمل مربوطه را فراخوانی کند. به این ترتیب باید برای هر گزینه Icommand را تعریف کنیم:
public ICommand LikeCommand { get; private set; }
public ICommand FavoriteCommand { get; private set; }
public ICommand ShareCommand { get; private set; }
public ICommand SessionDetailsCommand { get; private set; }
در سازنده هر کدام راباید مقدار دهی اولیه کنیم:
public SessionViewModel()
{
Sessions = new ObservableCollection<Session>();
LikeCommand = new Command(ApplyLike);
FavoriteCommand = new Command(ApplyFavorite);
ShareCommand = new Command(Share);
SessionDetailsCommand = new Command(SeeSessionDetails);
}
هر متد در کد فوق به شکل زیر خواهد بود:
ApplyLike
private void ApplyLike(object param)
{
var session = param as Session;
if (session != null)
{
session.NumLikes++;
}
}
ApplyFavorite
private void ApplyFavorite(object param)
{
var session = param as Session;
if (session != null)
{
session.IsFavorite = !session.IsFavorite;
}
}
Share
private void Share(object param)
{
var session = param as Session;
if (session != null)
{
var shareService = DependencyService.Get<IShareService>();
if (shareService != null)
{
var status = string.Format("Não percas a sessão {0} de {1}.", session.Name, session.Speaker.Name);
shareService.ShareLink("ENEI 2015", status, "https://enei.pt/");
}
}
}
SeeSessionDetails
private void SeeSessionDetails(object param)
{
var session = param as Session;
if (session != null)
{
MessagingCenter.Send(session, "SeeSessionDetails");
}
}
MessagingCenter یک کلاس است که می تواند Messageها را ارسال و دریافت کند. در این حالت زمانی که یک کاربر بخواهد جزئیات یک Session را مشاهده کند، View model بوسیله Session یک پیغام به View ارسال می کند و پس از آن از View به SessionDetailsView منتقل می شود.
در آخر کلاس SessionViewModel شما باید به شکل زیر تعریف شده باشند:
public class SessionViewModel
{
public SessionViewModel()
{
Sessions = new ObservableCollection<Session>();
LikeCommand = new Command(ApplyLike);
FavoriteCommand = new Command(ApplyFavorite);
ShareCommand = new Command(Share);
SessionDetailsCommand = new Command(SeeSessionDetails);
}
public ObservableCollection<Session> Sessions { get; set; }
public ICommand LikeCommand { get; private set; }
public ICommand FavoriteCommand { get; private set; }
public ICommand ShareCommand { get; private set; }
public ICommand SessionDetailsCommand { get; private set; }
private void ApplyLike(object param)
{
var session = param as Session;
if (session != null)
{
session.NumLikes++;
}
}
private void ApplyFavorite(object param)
{
var session = param as Session;
if (session != null)
{
session.IsFavorite = !session.IsFavorite;
}
}
private void Share(object param)
{
var session = param as Session;
if (session != null)
{
var shareService = DependencyService.Get<IShareService>();
if (shareService != null)
{
var status = string.Format("Não percas a sessão {0} de {1}.", session.Name, session.Speaker.Name);
shareService.ShareLink("ENEI 2015", status, "https://enei.pt/");
}
}
}
private void SeeSessionDetails(object param)
{
var session = param as Session;
if (session != null)
{
MessagingCenter.Send(session, "SeeSessionDetails");
}
}
public async Task LoadDataAsync()
{
await Task.Run(() = >
{
if (Sessions.Count == 0)
{
var sessions = SessionsDataSource.GetSessions();
foreach(var session in sessions)
{
Sessions.Add(session);
}
}
}
}
}
و SessionsView.xaml.cs باید به شکل زیر تغییر کند:
public partial class SessionsView : ContentPage
{
public SessionsView()
{
InitializeComponent();
MessagingCenter.Subscribe < Session > (this, "SeeSessionDetails", session = > {
Navigation.PushAsync(new SessionDetailsView(session), true);
});
}
protected override async void OnAppearing()
{
base.OnAppearing();
var viewmodel = BindingContext as SessionViewModel;
if (viewmodel != null)
{
await viewmodel.LoadDataAsync();
}
}
private void SessionsList_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
//workarround to clean the select item
if (SessionsList.SelectedItem == null)
{
return;
}
SessionsList.SelectedItem = null;
}
}
رویداد SessionsList_OnItemSelected تغییر نخواهد کرد. چون به این ترتیب گزینه ی انتخاب شده از حالت انتخاب خارج می شود.
باید در SessionsView.xaml نیز کمی تغییرات به شکل زیر ایجاد کنیم:
· SessionViewModel را به عنوان منبع صفحه تعریف می کنیم:
<ContentPage.Resources>
<ResourceDictionary>
<viewModels:SessionViewModel x:Key="SessionViewModel"/>
- اتصال SessionViewModel به BindingContext از طریق View
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:ENEI.SessionsApp.Converters;assembly=ENEI.SessionsApp"
xmlns:viewModels="clr-namespace:ENEI.SessionsApp.ViewModels;assembly=ENEI.SessionsApp"
x:Class="ENEI.SessionsApp.Views.SessionsView"
Title="1010 ENEI || Sessões"
BackgroundColor="White"
x:Name="ContentPage"
BindingContext="{StaticResource SessionViewModel}"
Icon="ic_action_users.png">
- تغییر TapGestureRecognizer برای هر گزینه
<TapGestureRecognizer CommandParameter="{Binding}" Command="{Binding SessionDetailsCommand, Source={StaticResource SessionViewModel}}"/>
هر Command به دستور مربوطه در View model متصل می شود. اما هر برنامه نویس باید در نظر داشته باشد که وقتی View بارگذاری شود، Binding Context هر تصویر بوسیله Session مربوط به هر آیتم از ListView تعیین می شود. به همین دلیل باید منبع اتصالی (binding's Source) که از View model، به عنوان یک منبع ایستا استفاده می کند، را مشخص کنیم. (فعلاً نمی توان اتصال مورد نظر را برقرار کرد و تعیین دستورات در شی model درست نیست. چون متعلق به View model هستند).
اگر برنامه را اجرا کنید مثل قبل اجرا می شود.
می توان View model را برای SessionDetailsView تعریف کرد. چون فقط یک Session را نمایش می دهد و کارایی مهم دیگری ندارد که بخواهید آن را تغییر دهید.