【MVVM Light】新手初識MVVM,你一看就會

1、前言前端

     做爲一個初入軟件業的新手,各類設計模式與框架對我是眼花繚亂的。因此當我接觸到這些新知識的時候就但願本身能總結幾個步驟,以便更好更方便的在平常工做中進行使用。編程

      MVVM顧名思義就是Model-View-View Model的縮寫。老司機們一直說綁定綁定,我就納悶了View是展現,Model是模型,那View Model怎麼寫處理的邏輯呢?它是如何將Model和View聯繫到一塊兒的呢?這是我第一次聽到MVVM時產生的疑惑。通過了一些編程經歷,大體明白了整個過程。本文不會過度強調MVVM中一些特別深刻的技術(我暫時也沒那本事),只是從一個初學者的角度去學會如何最快速的使用MVVM。設計模式

      本文將以MVVM Light做爲例子,由於它是個輕量化的MVVM框架,很是方便使用。之後會逐步介紹些其餘的MVVM框架,如DevExpress的等等。知識是互通的,明白了其中一個,另外一種也差很少不離其宗了。框架

 

2、準備異步

     下載MVVM Light的方式多種多樣,可使用NuGet包管理器或者直接登陸官網,一搜就找到了。this

      本項目安裝完MVVM Light後能夠看到引用:spa

        還有一個ViewModel文件夾:設計

 

3、MVVM日誌

     假設咱們有這樣一個產品的ModelIsChecked屬性你們一看就知道是用於在前端與CheckBox有聯繫而設置的屬性。code

namespace StudyMVVM
{
    public class ProductInfo
    {
        public bool IsChecked { get; set;}
        public string ProductName { get; set; }
        public string ProductIcon { get; set; }
        public string ProductUrl { get; set; }
        public string OldVersion { get; set; }
        public string NewVersion { get; set; }
    }
}  

     假設咱們有一個WPF頁面MainView.xaml,也就是View是這麼寫的:首先別管那個 ItemsSource,下面會慢慢說到

<Grid Name="GridName" Grid.Row="2" Margin="30,5" >
     <ListBox  Name="lb_Update"  VerticalAlignment="Center" Height="115" ItemsSource="{Binding UpdateProducts}" Margin="0,6,0,10"></ListBox>
</Grid>  

     那麼咱們想要把多個對象的屬性填充到一個ListBoxItem裏,而後將若干個ListBoxItem放到ListBox裏,因此:

<Style TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Grid Width="510" Height="120" MaxHeight="150" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="22*"/>
                                <ColumnDefinition Width="32*"/>
                                <ColumnDefinition Width="68*"/>
                                <ColumnDefinition Width="100*"/>
                                <ColumnDefinition Width="154*"/>
                                <ColumnDefinition Width="105*"/>
                                <ColumnDefinition Width="29*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"></RowDefinition>
                                <RowDefinition Height="*"></RowDefinition>
                            </Grid.RowDefinitions>

                            <Grid Grid.Column="1" Grid.RowSpan="2">
                                <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsChecked}"/>
                            </Grid>

                            <Grid Grid.Column="2" Grid.RowSpan="2">
                                <Image Source="{Binding ProductIcon}" Width="50"></Image>
                            </Grid>

                            <Grid Grid.Column="3" Grid.RowSpan="2">
                                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="15" Text="{Binding ProductName}"/>
                            </Grid>

                            <Grid Grid.Column="4" Grid.Row="0">
                                <TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,5" Text="{Binding OldVersion}"/>
                            </Grid>

                            <Grid Grid.Column="4" Grid.Row="1">
                                <TextBlock VerticalAlignment="top" HorizontalAlignment="Center" Margin="0,5,0,0" Text="{Binding NewVersion}"/>
                            </Grid>

                            <Grid Grid.Column="5" Grid.RowSpan="2">
                                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">
                                     <Hyperlink NavigateUri="{Binding ProductUrl}"  Click="Hyperlink_Click">日誌</Hyperlink>
                                </TextBlock>
                            </Grid>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>      

       咱們能夠很是清楚的看到Model中的屬性都綁定到了View中!下面就是很關鍵的ViewModel了,咱們還沒用到上述的ItemsSource呢。

        在MainViewModel.cs中,是這樣的:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.IO;
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Diagnostics;

namespace StudyMVVM.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
          public List<ProductInfo> UpdateProducts { get; set; } //
          public MainViewModel()
          {
                  UpdateProducts = new List<ProductInfo>();
                  for(int i=0;i<10;i++)
                  {
                         ProductInfo productinfo = new ProductInfo();
                         productinfo.IsChecked = true;
                         productinfo.ProductName = str_Name;
                         productinfo.ProductIcon = str_Path;                      
                         productinfo.ProductUrl = "www.baidu.com";
                         productinfo.OldVersion = "0.0.1";
                         productinfo.NewVersion = "0.0.2";
                         UpdateProducts.Add(productinfo);
                  }
          }
    }
}

     這樣,多個ProductInfo的對象被包裝在名爲UpdateProducts內,而且經過ItemsSource綁定到ListBox中,數據就這樣填充上了。

 

4、如何寫事件       

     當你在前端有個按鈕,想處理若干個ListBoxItem,好比下載全部Checked爲true的對象,你是否會懷念Winform的Click事件? 固然WPF也有Click事件。既然你已經用了MVVM,那麼請少用,最好不用Click事件去處理這些東西,特別是你要寫的事件是與你的ItemsSource所綁定的東西相關的。

    說白了,在例子裏就是和UpdateProducts有關係的,你就別用Click了。

    在View中假設有一個Button:它的Command綁定了GetCheckedUpdateProducts事件

<Grid Name="UpdateBtn" Grid.Row="0" Grid.Column="1">
       <Button Name="btn_update" Width="80" Height="30" Cursor="Hand" Content="更新"  Foreground="White" FontSize="14" Command="{Binding GetCheckedUpdateProducts}">
</Grid>

     在ViewModel中,注意引用GalaSoft.MvvmLight.Command;  

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace StudyMVVM.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
          public RelayCommand GetCheckedUpdateProducts { get; set; }

          public MainViewModel()
          {
                 this.GetCheckedUpdateProducts = new RelayCommand(GetProducts);
          }
    }

    private void GetProducts()
    {
         //Your Button Command: Download checked products
    }
}

將真正的事件邏輯GetProducts()賦值給RelayCommand GetCheckedUpdateProducts,前端經過Command=「{Binding GetCheckedUpdateProducts}」 便可。

 

5、RaisePropertyChanged

      這個RaisePropertyChanged是專門來照顧沒媽媽(ItemsSource)的孩子的(properties)。

      假設xaml前端有一個進度條,當你按下按鈕下載checked=true的產品時,進度條要實時顯示下載狀況:

<ProgressBar Grid.Row="0"   Height="10" VerticalAlignment="Top" Margin="10,0,8,0" Maximum="{Binding MaxValue}" Minimum="{Binding MinValue}" Value="{Binding ProgressValue}"/>   

      Maximum和Minimum通常是個定值,但ProgressValue是變化的,而且和Model裏屬性字段的沒半毛錢的關係啊,咋辦?我得告訴View我在改變啊,那麼在ViewModel

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace StudyMVVM.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
           public int MaxValue { get; set; }
           public int MinValue { get; set; }
           public int ProgressValue { get; set; }

public RelayCommand GetCheckedUpdateProducts { get; set; }
public MainViewModel() { MaxValue = 100; MinValue = 0; ProgressValue = 0;
this.GetCheckedUpdateProducts = new RelayCommand(GetProducts); } private void GetProducts()
    {
         BackgroundWorker bgWorker = new BackgroundWorker();
          bgWorker.DoWork += new DoWorkEventHandler(worker_Dowork);
          bgWorker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
          bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
          bgWorker.RunWorkerAsync();
    }
void worker_Dowork(object sender, DoWorkEventArgs e) { //do work } void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { UpdateMessage = (string)e.UserState; ProgressValue = e.ProgressPercentage; RaisePropertyChanged(() => ProgressValue); // I'm Here!!!! Hey! Look At Me ! RaisePropertyChanged(() => UpdateMessage); } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Result is Exception) { UpdateMessage = (e.Result as Exception).Message; } else { UpdateMessage = (string)e.Result; } } } }

PS:上述代碼還用到了BackgroundWorker,這是一個不錯的異步顯示進度條的控件,有興趣的能夠試試,很是方便使用。

 

6、DataContext

      看到這裏,有些新手以爲ViewModel中的東西能夠很順利成章的綁定到View上了,錯!不以爲奇怪嗎?憑什麼這個MainViewModel就要和上述的View創建聯繫,而不是和其餘的View有聯繫呢?

      爲了防止View上錯老婆(爲何我不說防止ViewModel找到隔壁老王呢?各位能夠思考想一想),咱們須要在某一個View指定其DataContext是哪一個ViewModel   

using YourProject.ViewModel;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Diagnostics;


namespace StudyMVVM
{
    /// <summary>
    /// MainView.xaml 的交互邏輯
    /// </summary>
    public partial class MainView : Window
    {
           public MainView()
           {
               this.DataContext = new MainViewModel();// find correct wife
           }   
    }
}

       還有一個辦法能指定DataContext,MVVM Light提供了ViewModelLocator.cs來幫助你綁定view的DataContext;Xaml裏也能夠綁定DataContext。不過我仍是喜歡用上述最原始的方法。至於ViewModelLocator怎麼使用,博園有至關多的牛人及文章,想要深刻了解的能夠去搜下。   

        其實DataContext在你引入MVVM框架以後就應該進行綁定了,寫在這裏只是爲了提醒你們其重要性!

 

7、大結局

      終於寫完了,科科,擺了個白!

相關文章
相關標籤/搜索