zoukankan      html  css  js  c++  java
  • 【MVVM Light】新手初识MVVM,你一看就会

    一、前言

         作为一个初入软件业的新手,各种设计模式与框架对我是眼花缭乱的。所以当我接触到这些新知识的时候就希望自己能总结几个步骤,以便更好更方便的在日常工作中进行使用。

          MVVM顾名思义就是Model-View-View Model的缩写。老司机们一直说绑定绑定,我就纳闷了View是展示,Model是模型,那View Model怎么写处理的逻辑呢?它是如何将Model和View联系到一起的呢?这是我第一次听到MVVM时产生的疑惑。经过了一些编程经历,大致明白了整个过程。本文不会过分强调MVVM中一些特别深入的技术(我暂时也没那本事),只是从一个初学者的角度去学会如何最快速的使用MVVM。

          本文将以MVVM Light作为例子,因为它是个轻量化的MVVM框架,非常方便使用。以后会逐步介绍些其他的MVVM框架,如DevExpress的等等。知识是互通的,明白了其中一个,另一种也差不多不离其宗了。

    二、准备

         下载MVVM Light的方式多种多样,可以使用NuGet包管理器或者直接登录官网,一搜就找到了。

          本项目安装完MVVM Light后可以看到引用:

            还有一个ViewModel文件夹:

    三、MVVM

         假设我们有这样一个产品的ModelIsChecked属性大家一看就知道是用于在前端与CheckBox有联系而设置的属性。

    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中,数据就这样填充上了。

    四、如何写事件       

         当你在前端有个按钮,想处理若干个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}” 即可。

    五、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,这是一个不错的异步显示进度条的控件,有兴趣的可以试试,非常方便使用。

    六、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框架之后就应该进行绑定了,写在这里只是为了提醒大家其重要性!

    七、大结局

          终于写完了,科科,摆了个白!

  • 相关阅读:
    表单中input name属性有无[]的区别
    验证器
    模板输出替换
    模型中的数据操作
    2020 倒计时 1 天,Python 工程师找工作更难了?
    80后、90后扎心图鉴
    2019,我赚钱了
    AI在自动化测试领域的应用
    我被裁掉的那一天
    【阿里巴巴】飞猪技术质量招聘
  • 原文地址:https://www.cnblogs.com/lovecsharp094/p/5837668.html
Copyright © 2011-2022 走看看