معرفی الگوی MVVM

دوشنبه 12 مرداد 1394

در این مقاله قصد داریم شما را با MVVM Pattern آشنا کنیم.

معرفی الگوی  MVVM

این مقاله آموزشی به دو بخش تقسیم می شود، بخش اول به صورت تئوری و توضیحات می باشد. بخش دوم یک نمونه پروژه به همراه Breakdown برای فهم بهتر قسمت های مهم کد است. کدی که در ادامه می آید، در 4.5.2 Net. نوشته شده و در 4.6 Net. برای تضمین سازگاری با Visual Studio 2015 تست شده است، بنابراین، این مقاله تا جایی که ممکن بوده است، به روز می باشد.

بخش اول:

MVVM Pattern یک افزونه روی  MVC Pattern است که عمدتا در توسعه وب استفاده می شود.این افزونه، به جای Model View Controller از یک Model، View و ViewModel گرفته شده است. بنابراین، اگر در MVVM Pattern تازه کار باشید، بدون شک از اهمیت بیش از حد این الگوها تعجب خواهید کرد. حق با شماست و دلیل آن بسیار ساده است. جداسازی کد. جداسازی کد به شما این امکان را می دهد که UI و کدهای خود را از هم جدا کنید.

مثال بسیار ساده آن، این است که شما روی کد Back end کار می کنید و برنامه نویس دیگری روی UI کار می کند، اگر از MVVM Pattern استفاده نکنید، هر تغییری که برنامه نویس UI اعمال می کند روی کد شما نیز تاثیر می گذارد. MVVM Pattern به حل این مشکل کمک می کند.

علاوه بر این، زمانی که از MVVM Pattern استفاده می کنید باید توجه داشته باشید که درحال حاضر در حالت اعلانی (declarative) برنامه نویسی می کنید.

هر بخش از MVVM چیست و چه کار می کند:

Model یک کلاس است که داده هایی را که می خواهید استفاده کنید، نمایش می دهد. علاوه بر این، Interface (رابط کاربری) INotifyPropertyChanged را نیز پیاده سازی می کند.

View همان Page یا usercontrol شماست، به صورتی که شما می خواهید از آن استفاده کنید.

این کلاس، نباید هیچ کد Back end ای داشته باشد اگرچه یک استثنا وجود دارد.

ViewModel یک کلاس است که همه کارهای سنگین را انجام می دهد. این همان قسمتی است که دیتابیس را فراخوانی می کند، یک observablecollection می سازد، و هر کد Back endای که شما نیاز به پیاده سازی آن دارید.

چرا DataBinding با MVVM اهمیت دارد:

DataBinding، کاربر را قادر می سازد تا شی ها را Bind (متصل)کند، بنابراین هرجایی که اشیای دیگر تغییر کنند، شی اصلی نیز تاثیر می پذیرد. دلیل اصلی اهمیت MVVM این است که UI همیشه به طور خودکار  با ساختار داده های درون برنامه هماهنگ و متقارن است.

 چگونه از DataBinding استفاده کنیم

DataBinding درXAML ، به سادگی انجام می شود. کد زیر را در نظر بگیرید.

<TextBox x:Name="textBox" Height="34" TextWrapping="Wrap" Text="{Binding Genre}" IsReadOnly="True"/>  

کد  XAML که در کادر بالا نوشته شده است نشان می دهد که متن TextBox به Genre متصل شده است.

حالا می خواهید چه کدی را با استفاده از #C  به جای استفاده از xaml بنویسید.کد زیر را در نظر بگیرید.

BindingSource binding = new BindingSource();  
binding.Add(new Models.ModelClass("Action");  
textbox.text = binding; 

اکنون که مراحل اولیه انجام شدند، بخش دوم کار را آغاز می کنیم.

بخش دوم

در ابتدا، کلاس Model را معرفی می کنیم.

در این مثال، می خواهیم ReleaseDate ،Name ،Genre را نمایش دهیم و همچنین می خواهیم  INotifyPropertyChanged Interface را پیاده سازی کنیم و در متد Set هر public property یک event را فراخوانی کنیم.

using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Linq;  
using System.Runtime.CompilerServices;  
using System.Text;  
using System.Threading.Tasks;  
  
namespace PracticeProject.Models  
{  
    public class ModelClass : INotifyPropertyChanged  
    {  
        private string name;  
        public string Name  
        {  
            get { return name; }  
            set  
            {  
                name = value;  
                NotifyPropertyChanged();  
            }  
        }  
        private string releaseDate;  
        public string ReleaseDate  
        {  
            get { return releaseDate; }  
            set  
            {  
                releaseDate = value;  
                NotifyPropertyChanged();  
            }  
        }  
        private string genre;  
        public string Genre  
        {  
            get { return genre; }  
            set  
            {  
                genre = value;  
                NotifyPropertyChanged();  
            }  
        }  
  
        public event PropertyChangedEventHandler PropertyChanged;  
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")  
        {  
            if (PropertyChanged != null)  
            {  
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));  
            }  
        }  
    }  
}  

 مرحله ی بعد،کلاس ViewModel. در این مثال، از مزایای استفاده از ObservableCollection بهره می گیریم و پارامتر ها را set می کنیم تا به یک کلاس Model برسیم. از عبارات Linq و Lambda تا ObservableCollection را در بالا بشماریم. فایل XML درون برنامه کاربردی قرار دارد، بنابراین ما از کلاس های  reflection و the StreamReader استفاده می کنیم تا این داده ها را فراخوانی کنیم وآن ها را در ObservableCollection قرار دهیم

using System;  
using System.Collections.Generic;  
using System.Collections.ObjectModel;  
using System.ComponentModel;  
using System.Linq;  
using System.Runtime.CompilerServices;  
using System.Text;  
using System.Threading.Tasks;  
using System.Windows.Input;  
using System.Xml.Linq;  
using System.IO;  
using System.Reflection;  
using System.Windows;  
namespace PracticeProject.ViewModels  
{  
    public class ViewModelClass  
    {  
        public ObservableCollection<Models.ModelClass> Movies { get; set; }  
        StreamReader _textStreamReader;  
        public ViewModelClass()  
        {  
           LoadEmbeddedResource("PracticeProject.DataSources.Movies.xml");  
            XDocument xdoc = XDocument.Load(_textStreamReader);  
            var movies = xdoc.Descendants("Movies")  
                .Select(x =>  
                new Models.ModelClass  
                {  
                    Name = (string)x.Element("Name"),  
                    Genre = (string)x.Element("Genre").Value,  
                    ReleaseDate = (string)x.Element("ReleaseDate").Value  
                }  
                ).ToList();  
            Movies = new ObservableCollection<Models.ModelClass>(movies);  
        }  
        private void LoadEmbeddedResource(string resource)  
        {  
            try  
            {  
                Assembly _assembly;  
                _assembly = Assembly.GetExecutingAssembly();  
                _textStreamReader = new StreamReader(_assembly.GetManifestResourceStream(resource));  
            }  
            catch (Exception ex)  
            {  
                MessageBox.Show("Error Loading Embedded Resource: " + ex, "Error!", MessageBoxButton.OK, MessageBoxImage.Error);  
            }  
        }  
    }  
}  

مرحله بعدی، CodeBehind مربوط به View می باشد. این مثال، نشان می دهد که شما نباید هیچ Back endای برای آن داشته باشید، اگرچه طبق مثال زیر، برای یک خط قابل قبول است.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.Windows;  
using System.Windows.Controls;  
using System.Windows.Data;  
using System.Windows.Documents;  
using System.Windows.Input;  
using System.Windows.Media;  
using System.Windows.Media.Imaging;  
using System.Windows.Navigation;  
using System.Windows.Shapes;  
  
namespace PracticeProject.Views  
{  
    /// <summary>  
    /// Interaction logic for MainView.xaml  
    /// </summary>  
    public partial class MainView : UserControl  
    {  
        public MainView()  
        {  
            InitializeComponent();  
            DataContext = new ViewModels.ViewModelClass();  
        }  
    }  
} 

و درنهایت، کد XAML مربوط به View. این قسمت، به راستی سخت ترین قسمت استفاده از MVVM Pattern است. در ابتدا، ViewModel را در فضای نام فایل XAML تعریف می کنیم. یک Listbox می سازیم و Movies ObservableCollection را به آن متصل (Bind) می کنیم. سپس UpdateSourceTrigger را روی PropertyChanged تنظیم می کنیم. و در آخر، DisplayMemberPath را روی Name تنظیم می  کنیم. توجه داشته باشید که IsSynchronizedWithCurrentItem روی True تنظیم شده باشد. 

در ادامه، یک stackPanel می سازیم و DataContext آن را روی مسیری که به عنوان selected item در Listbox تنظیم شده، قرار می دهیم.این کار را انجام می دهیم تا مطمئن شویم، هر آیتمی که در Listbox انتخاب شد، متناظر با آن Bound Object نیز update شود.

حال، یک Lable و یک Textbox در stackpanel می سازیم و متن Textbox را به Genre و محتوای Lable را به ReleaseDate متصل می کنیم.

<UserControl x:Class="PracticeProject.Views.MainView"  
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
             xmlns:local="clr-namespace:PracticeProject.Views"  
             mc:Ignorable="d"  
             xmlns:vm="clr-namespace:PracticeProject.ViewModels"  
             d:DesignHeight="300" d:DesignWidth="300">  
    <Grid>  
        <ListBox x:Name="listBox" ItemsSource="{Binding Movies, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name" Height="100" IsSynchronizedWithCurrentItem="True"/>  
        <StackPanel Margin="10" VerticalAlignment="Bottom" DataContext="{Binding ElementName=listBox, Path=SelectedItem}">  
            <Label x:Name="label" Content="{Binding ReleaseDate}" Height="36"/>  
            <TextBox x:Name="textBox" Height="34" TextWrapping="Wrap" Text="{Binding Genre}" IsReadOnly="True"/>  
        </StackPanel>  
    </Grid>  
</UserControl>  

حال، می توانید برنامه را اجرا کنید و هر آیتمی که شما در Listbox انتخاب کنید، مقدار متناظر آن نیز، در  label و TextBox به روزرسانی (update) می شود.

اگر مفاهیم MVVM pattern را تا حدودی یاد بگیرید، می توانید آن را با یادگیری ICommand Interface توسعه دهید.

 

 

 

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • WPF
  • 5k بازدید
  • 8 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید