NGame

آموزش مقدماتی MVVM در سی شارپ

5 ارسال در این موضوع قرار دارد

با سلام . حتماً همیشه در برنامه هایی که تا کنون نوشته اید با مشکل تغییر در رابط کاربری مواجه بوده اید.

اینکه یک بخش از رابط کاربری را بخواهید حذف کنید تغییر دهید و.... آزار دهنده به نظر میرسد چون بعد از تغییر رابط کاربری اسامی اشیاء را باید به گونه ای تغییر دهید که در بک کد های شما نیز وجود دارد مثلا لیستی با نام Lst را در بک کد مقدار دهی نموده اید پس از تغییر رابط کاربری باید به یک لیست ویو حتما همین نام را بدهید . وای به روزی که اصلا نفهمید اون آبجکت چی بوده! و اصلا این قسمت کدتون داره چی کار میکنه.

خب برای حل این مشکل یک روش کد نویسی وجود داره که شاید با نام MVVM یا MVC به گوشتون خورده باشه . 
اما MVVM مخفف چیه ؟ 

کلمه MVVM از سه بخش تشکیل شده : 
M : مدل (Model)
V : ویو (View)
و VM : ویو مدل (View Model)

بخش مدل فقط شامل کلاس های ما میشه. به عنوان مثال من توی یک برنامه موسیقی یک کلاس دارم مربوط به آلبوم خواننده که شامل اسم خواننده ، سال انتشار آلبوم ، تعداد ترک های آلبوم ، ترانه سرایان ، تنظیم کنندگان ، آی دی آلبوم و.... میباشد که به این کلاس ها که در آینده باهاشون توی رابط کاربری سر و کار هم داریم میگیم مدل.

بخش ویو : این بخش از همه آشنا تره همون رابط کاربری صفحه است که با Xaml به عنوان مثال توی UWP و WPF میسازیم .

اما بخش View Model : این بخش ارتباط دهنده مدل ها و ویو ها به هم دیگه هست . این بخش تا حدودی شبیه همون بک کد های کثیف خودمونه ولی یه تفاوت هایی داره که بعداً متوجه میشیم .

توی فرآیند اجرای یک برنامه اول ویو لود میشه -> بعد ویو مدل رو فراخونی میکنه و مقادیر رو ازش میگیره و با معجزه بایندینگ به نمایش در میاره -> ویو مدل هم که در صورت لزوم به مدل ها دسترسی داره .

خب حالا برای تست یه پروژه میسازیم به اسم ColorMVVM که در اینجا من پروژه رو از نوع C# UWP میسازم .  سپس توی سولوشن اکسپلورر دو تا Folder به اسم های View و ViewModel میسازم . ادامه مطلب رو توی پست بعدی مینویسم . :)

پسند شده توسط 1 کاربر

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر


خب تا به اینجای کار فقط با یه سری مفاهیم اونم نه چندان ملموس آشنا شدیم یه پروژه هم ساختیم با دو تا پوشه . 

برای اینکه کارمون منظم تر باشه توی پوشه View یک Blank Page ایجاد میکنیم به نام MainView. به صورت دل به خواه نام گزاری های خودتون رو هدفمند کنید من برای فایل هایویو پسوند View برای فایل های ViewModel از پسوند VM استفاده میکنم و برای مدل ها هم از پسوند Model .

هر صفحه (ویو) که میسازیم یک ViewModel هم باید براش بسازیم . البته توجه داشته باشید که آیه ای جهت ساخت ویو مدل نازل نشده! اگر پیجی مثل About میخواید بسازید کهبه بک کد نیاز نداره یا خیلی محدود هست بک کدش میتونید از این کار صرف نظر کنید ولی فراموش نکنید این کار رو فقط برای ویو هایی انجام بدید که بک کد ندارند.
خب پس تو پوشه ViewModel یک کلاس با نام MainViewVM یا MainVM یا هر نام دلخواهی بسازید . من از نام استفاده میکنم . 

MVVM1.png

خب حالا نوبت به ارتباط دادن View به ViewModel میرسه . همونطور که کمی قبل تر اشاره کردم ویو مسئول فراخوانی ویو مدل هست . پس به کد های Xaml مربوط به MainView مراجعه میکنیم و این سه خط کد را زمل اضافه میکنیم : 


    <Page.DataContext>
        <VM:MainViewVM/>
    </Page.DataContext>
    

خب بعد از اضافه کردن این سه خط کد بدون شک ویژوال استدیو ارور میده بهتون چون VM رو اصلا تعریف نکردیم چی هست . برای تعریف خودکارش با موس روی VM:MainViewVM برید تا علامت چراغ زرد رنگ بیاد و Show Potential Fixes رو بزنید . 

MVVM2.png

سپس Add xmlns using:ColorMVVM.ViewModel رو انتخاب کنید و حالا ارور ویژوال استدیو بر طرف میشه و به ابتدای کد زمل شما این خط اضافه میشه .

xmlns:VM="using:ColorMVVM.ViewModel"

معمولا توی پروژه های بزرگ ممکنه ارور همچنان سر جاش بمونه یا ارور دیگه ای ظاهر بشه که با بیلد کردن پروژه مشکل حل میشه .

خب حالا میریم به سراغ ViewModel . کلاس ویو مدل رو باز میکنیم و کلاسش رو از نوع INotifyPropertyChanged ارث بری میکنیم به این صورت : 

class MainViewVM : INotifyPropertyChanged
{
}

از اونجایی که باز هم INotifyPropertyChanged رو نگفتیم کلاسش چیه بهتون ارور میده که با بردن موس روی کلمه و زدن Show Potential Fixes پیشنهاد using System.ComponentModel; رو به شما میده که انتخاب کنید تا به صورت خودکار رفرنسش به صفحه اضافه بشه . اما مجدداً ارور جدیدی به همین INotifyPropertyChanged داده میشه که مربوط به Implement Interface میشه . در نهایت کلاسی به شکل زیر خواهیم داشت : 
 

class MainViewVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
}

اما INotifyPropertyChanged چیست ؟ همانطور که از نام این اینترفیس کاملاً پیداست تغییر یک Property رو با ما گزارش میده. این تغییرات رو از طریق ایونتی که ساختیم با نام PropertyChanged به ما گزارش میده . ما معمولا مستقیماً با این ایونت سر و کار نداریم . در صورت فراخوانی شدن این ایونت رابط کاربری به صورت خودکار تغییر را دریافت نموده و تغییر لازم را در رابط کاربری ایجاد میکنه (معجزه بایندینگ که گفته بودم) 

خب تا به این جای کار یک پروژه ساختیم پوشه های ViewModel و View رو ایجاد کردیم . یک ویو و یک ویو مدل ایجاد کردیم و ارتباط بین ویو و ویو مدل رو برقرار کردیم همچنین خاصیت INotifyPropertyChanged رو به ویو مدل خودمون دادیم تا در صورت ایجاد هر گونه تغییر توی مقادیر گزارشش رو در ویو بتونیم دریافت کنیم . 

پسند شده توسط 1 کاربر

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

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

خب ابتدا توی ویو مدل این پراپرتی ها رو ایجاد میکنیم : 

class MainViewVM : INotifyPropertyChanged
{
    private string _colorname;
    public event PropertyChangedEventHandler PropertyChanged;
    public string ColorName
    {
        get => _colorname;
        set { _colorname = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ColorName")); }
    }
}

توی ویژوال استدیو 2015 به قبل اگه استفاده میکنید دستور get رو به این حالت تغییر بدید : get { return _colorname; } تا ارور برطرف بشه . 

در اینجا یک پراپرتی پابلیک جهت ارتباط با View و یک پراپرتی پرایوت جهت نگه داری مقادیر ورودی و خروجی این پراپرتی ایجاد کرده ایم . به این شکل که هر وقت بخواهیم مقدار ColorName رو بخوانیم ابتدا مقدار _colorname را دریافت میکنیم و سپس مقدار را بر میگردانیم و هنگام مقدار دهی ابتدا مقدار ورودی را در متغیر پرایوت ذخیره میکنیم و سپس ایونت PropertyChanged را فراخوانی میکنیم و در ورودی تابع نام متغیری که مقدارش تغییر کرده را وارد مینماییم . جهت اصولی تر شدن کار هم پیش از اینکه مقدار رو تغییر بدید و ایونت را فراخونی کنید میتونید اول چک کنید که مقدار value با مقدار _colorname متفاوت هست یا خیر و در صورت متفاوت بودن مقادیر تغییرات را ست کنید . 

خب ویو مدل ما تکمیل هست . حالا سراغ ویو میرویم . 

داخل ویو یک تکست باکس اضافه میکنیم . 

<TextBox Text="{Binding ColorName, Mode=TwoWay}" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200"/>

همانطور که مشاهده میکنید مقدار Text را با بایندینگ به ویو مدل و پراپرتی مورد نظرم مرتبط کردم اما Mode

توی بایندینگ سه نوع مد داریم . One Time / One Way / Two Way

حالت پیشفرض بایندینگ One Time هست. به این معنی که یک بار از پراپرتی مقدارش رو دریافت میکنه و دیگه تغییر نمیکنه . مثلاً اگه ColorName مقدار Red رو برگردونه همیشه مقدارش Red باقی میمونه (با اینکه مقدار Color Name توی ویو مدل تغییر میکنه توی ویو تغییر نمیکنه*

حالت  OneWay یعنی هر وقت توی ویو مدل مقدار تغییر کرد توی رابط کاربری تغییر کنه ولی مثلا من اگه دستی توی این تکست باکس چیزی بنویسم تغییر من باعث تغییر مقدار ColorName در ویو مدل نمیشه . 

حالت TwoWay دقیقاً همون حالت OneWay هست با این تفاوت که مثلا من اگه مقدار داخل تکست باکس رو تغییر بدم توی ویو مدل هم تغییر میکنه . (مثلاً از این طریق میتونید یک فرم ثبت نام ساده رو فقط با 3 یا 4 تا پراپرتی پابلک ساده بسازید . )

در اینجا من از حالت TwoWay استفاده کردم چون میخوام مقدار تغییر کنه . حالا به گرید اصلی صفحه بک گراند میدم . 

<Grid Background="{Binding ColorName}">
    <TextBox Text="{Binding ColorName, Mode=TwoWay}" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200"/>
</Grid>

خب برنامه ما به اتمام رسید . حالا به App.Xaml.Cs بروید و صفحه ابتدایی برنامه را تغییر دهید . 
در متد protected override void OnLaunched(LaunchActivatedEventArgs e) 

خط زیر را 
 

rootFrame.Navigate(typeof(MainPage), e.Arguments);

به صورت زیر تغییر دهید : 
 

rootFrame.Navigate(typeof(MainView), e.Arguments);

و حالا برنامه را اجرا کنید . پس از اجرای برنامه نام یک رنگ را به انگلیسی در تکست باکس وارد نمایید و محلی خارج از تکست باکس کلید کنید . رنگ پس زمینه برنامه به رنگ تایپ شده تغییر میکند . در صورتی که متن شما اسم یک رنگ نباشد رنگ پیش فرض ویندوز به پس زمینه اعمال میشود. 

تا به این قسمت فرا گرفتیم که چگونه یک ویو و ویو مدل را به هم ارتباط دهیم . تفاوت انواع بایندینگ را متوجه شدیم و با مفهوم INotifyPropertyChanged آشنا شدیم . 
در قسمت های بعدی قدری با موارد حرفه ای تر سر و کار خواهیم داشت 

سورس کد برنامه

پسند شده توسط 1 کاربر

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

با سلام. امیدوارم تا اینجای آموزش سخت نبوده باشه .
در این قسمت با Command آشنا میشیم . کامند ها همون ایونت ها هستن که به جای اینکه توی کد بیهایند (فایل xaml.cs) بنویسیم توی مدل ویو مینویسین . 

ابتدا یک کلاس با نام AppCommand در روت پروژه بسازید و این کد را داخل ان کپی کنید . 

using System;
using System.Windows.Input;


public class AppCommand : ICommand
{
#pragma warning disable CS0067 // The event 'AppCommand.CanExecuteChanged' is never used
    public event EventHandler CanExecuteChanged;
#pragma warning restore CS0067 // The event 'AppCommand.CanExecuteChanged' is never used
    public static AppCommand GetInstance()
    {
        return new AppCommand() { CanExecuteFunc = obj => true };
    }
    public Predicate<object> CanExecuteFunc
    {
        get;
        set;
    }

    public Action<object> ExecuteFunc
    {
        get;
        set;
    }

    public bool CanExecute(object parameter)
    {
        return CanExecuteFunc(parameter);
    }

    public void Execute(object parameter)
    {
        ExecuteFunc(parameter);
    }
}

برای استفاده از این کلاس در صفحه MainView یک باتن اضافه میکنیم . کد نهایی صفحه به صورت زیر خواهد بود: 

    <Page.DataContext>
        <VM:MainViewVM/>
    </Page.DataContext>

    <Grid Background="{Binding ColorName}">
        <Grid VerticalAlignment="Center" HorizontalAlignment="Center">

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBox Grid.Row="0" Text="{Binding ColorName, Mode=TwoWay}" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200"/>
            <Button Grid.Row="1" Content="About" Command="{Binding AboutCmd}" Margin="0,5" Width="120" HorizontalAlignment="Center"/>
        </Grid>
    </Grid>

خب حالا توی ویو مدل باید کامند رو تعریف کنیم 

public AppCommand AboutCmd { get; set; }

نکته خیلی مهم : 
تمام پراپرتی ها و کامند ها و.... ای که قراره از طریق زمل بتونیم بایندشون کنیم باید حتما و حتما به صورت property تعریف شده باشن یعنیpublic type PropertyName{get;set;} در غیر اینصورت بایندینگ انجام نمیشه مثلا اگه نوشته باشید public type pname; 

حالا کانستراکتور مربوط به ویو مدل خودمون رو میسازم و کد های زیر را داخلش مینویسیم : 

public MainViewVM()
{
    AboutCmd = AppCommand.GetInstance();
    AboutCmd.ExecuteFunc = ShowAbout;
}

بدون کوچک ترین شکی به خاطر اینکه هنوزShowAbout را تعریف نکردیم ازش ارور میگیره . پس ماوس رو روی ارورش ببرید و از پیشنهادات ویژوال استدیو گزینه اول یعنی Generate method 'MainViewVM.ShowAbout' را انتخاب کنید . حال تابعی به صورت زیر برای شما ایجاد میشود. 

private void ShowAbout(object obj)
{
    throw new NotImplementedException();
}

تابع را به صورت زیر تغییر میدهیم . 

private async void ShowAbout(object obj)
{
    await new MessageDialog("I am learning MVVM").ShowAsync();
}

حال برنامه را اجرا کنید . پس از اجرا روی کلید about کلیک کنید خواهید دید که پیام شما نمایش داده خواهد شد . 

پسند شده توسط 1 کاربر

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

با سلام.

تا کنون با مفاهیمی همچون  View , ViewModel , Model, Command و Binding و انواع آن آشنا شدیم .

در این قسمت میخواهیم با Converter آشنا شویم . به عنوان مثال متغیر public bool IsBusy در ویو مدل را میخواهیم تبدیل به Visibility برای یک ProgressRing نماییم . به صورت مستقیم این کار ممکن نیست پس یا باید در ویو مدل متغیر دیگری با نوع Visibility در نظر بگیریم یا یک کانورتر بنویسیم و آنرا در زمل فراخوانی کنیم . در اینجا از هر دو روش استفاده خواهیم کرد.

روش اول (تبدیل در داخل ویو مدل) : 

ابتدا متغیر IsBusy را به ویو مدل خود می افزائیم : 

private bool _isbusy;
public bool IsBusy
{
    get => _isbusy;
    set { _isbusy = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsBusy")); }
}

حالا یک متغیر از نوع Visibility نیازمندیم که آنرا نیز ایجاد میکنیم . 

private Visibility _loadindivis;
public Visibility LoadingIndicatorVisibility
{
    get => _loadindivis;
    set { _loadindivis = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LoadingIndicatorVisibility")); }
}

حالا برای ایجاد ارتباط بین دو متغیر در بخش set متغیر IsBusy مقدار LoadingIndicatorVisibility را نیز ست میکنیم . 

public bool IsBusy
{
    get => _isbusy;
    set
    {
        _isbusy = value;
        if (value) LoadingIndicatorVisibility = Visibility.Visible;
        else LoadingIndicatorVisibility = Visibility.Collapsed;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsBusy"));
    }
}

به این صورت حالا در هر جا که بخواهیم Visibility یک شیء را با تغییر IsBusy تغییر دهیم کافیست Visibility="{Binding LoadingIndicatorVisibility, Mode=OneWay}" را استفاده کنیم . اما در صورتیکه تعداد متغیر های نیازمند تبدیلمان زیاد شوند دائماً باید بر تعداد متغیر ها بیافزاییم در حالیکه به عنوان مثال همه آنها تعداد bool به Visibility هستند. در این موارد از  یک Converter واحد برای همیشه استفاده خواهیم کرد. خوبی این روش هم علاوه بر سادگی این هست که در پروژه های آینده هم در صورت نیاز به این نوع کانورتر دیگر نیاز به نوشتن مجدد آن نخواهد بود هر چند که کار دشوار هم نیست !

 

روش دوم (استفاده از Converter یا مبدل در زمل):

برای استفاده از این روش ابتدا متغیر های از نوع Visibility که پیش از این تعریف نمودیم را حذف میکنیم چون دیگر به آنها نیازی نداریم . سپس در روت پروژه یک پوشه جدید به نام Converters ایجاد میکنیم . در پوشه ایجاد شده یک کلاس جدید با نام BoolToVisibilityConverter ایجاد میکنیم و سپس آنرا از اینترفیس IValueConverter ارث بری میکنیم : 

class BoolToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

این کلاس حال دارای دو تابع Convert و ConvertBack میباشد که در اینجا ما فقط با تابع Convert سر و کار داریم . در تابع کانورت Value را دریافت میکنیم و سپس (در صورت نیاز با کمک Parameter و Language) تبدیلات را انجام میدهیم. در این آموزش فقط با value کار میکنیم و باید این مورد را در نظر داشته باشید که مقدار parameter و language قابل Bind نیستند (از نوع DependencyProperty نیستند)  جهت اطلاعات بیشتر در مورد این موضوع جستجو کنید . 

با توجه به نیازمان تابع کانورت را به صورت زیر تبدیل مینماییم : 

public object Convert(object value, Type targetType, object parameter, string language)
{
    var val = System.Convert.ToBoolean(value);
    if (val) return Visibility.Visible;
    else return Visibility.Collapsed;
}

حال در زمل یک پراگرس رینگ جهت تست این مبدل اضافه میکنیم 

    <Page.DataContext>
        <VM:MainViewVM/>
    </Page.DataContext>

    <Grid Background="{Binding ColorName}">
        <Grid VerticalAlignment="Center" HorizontalAlignment="Center">

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBox Grid.Row="0" Text="{Binding ColorName, Mode=TwoWay}" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200"/>
            <Button Grid.Row="1" Content="About" Command="{Binding AboutCmd}" IsEnabled="{Binding AboutCmd.CanExecuteFunc, Mode=OneWay}" Margin="0,5" Width="120" HorizontalAlignment="Center"/>
        </Grid>
        <ProgressRing Visibility="{Binding IsBusy,Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" IsActive="True" Width="100" Height="100"/>
    </Grid>

بدون شک در بخش Visibility="{Binding IsBusy,Converter={StaticResource BoolToVisibilityConverter}}" به ارور بر میخوریم به این دلیل که ریسورس این کانورتر را به زمل اضافه نکرده ایم. پس در ریسورس های صفحه این مورد را اضافه میکنیم : 


    <Page.Resources>
        <Converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
    </Page.Resources>
    

حال در ویومدل جهت دیدن تغییرات این تغییرات را اعمال میکنیم . 

public MainViewVM()
{
    AboutCmd = AppCommand.GetInstance();
    AboutCmd.ExecuteFunc = ShowAbout;
    IsBusy = false;
}

حال در کلیک کلید About این تغییرات را اعمال کنید . 

private async void ShowAbout(object obj)
        {
            //await new MessageDialog("I am learning MVVM").ShowAsync();
            IsBusy = true;
            await Task.Delay(5000);
            IsBusy = false;
        }

 

حال با اجرای برنامه و زدن کلید about قادر به مشاهده پراگرس رینگ برای مدت 5 ثانیه خواهید بود.

پسند شده توسط 1 کاربر

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

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

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !


ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید


ورود به حساب کاربری