【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

     假设我们有这样一个产品的Model:IsChecked属性大家一看就知道是用于在前端与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)。

又假设前端有一个进度条,当你按下按钮下载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 MainViewModel()
          {
                MaxValue = 100;
                MinValue = 0;
                ProgressValue = 0;
          }

          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。不过我还是喜欢用上述最原始的方法。

至于ViewModelLocator怎么使用,博园有相当多的牛人及文章,想要深入->O了解的可以去搜下。

其实DataContext在你引入MVVM框架之后就应该进行绑定了,写在这里只是为了提醒大家其重要性!

七、大结局

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

时间: 2024-09-30 18:33:08

【MVVM Light】新手初识MVVM,你一看就会的相关文章

【MVVM Light】Messager的使用

一.前言       在MVVM编程的模式中,有时候我们会遇到一个很尴尬的情况: 若干个xaml.cs都复用一个ViewModel,当ViewModel想传递一个特定的消息给某一个xaml.cs的时候,我们该怎么做? 或者当一个.cs不方便在另一个.cs文件中new出来使用,但是我确实想传递消息给那个.cs,该怎么办? 今天就说说MVVM Light中的Messager类,它继承自IMessager接口. 二.使用方法       发送页面中: Messenger.Default.Send<TM

MVVM Light Toolkit使用指南

原文:MVVM Light Toolkit使用指南 原文地址:  https://blog.csdn.net/ldld1717/article/details/77040077 概述 MVVM Light Toolkit是一个Android MVVM 轻量级工具库,主要目的是更快捷方便的构建Android MVVM应用程序,工具库添加了一些Data Binding 不支持的属性,还有添加对控件事件的封装,同时提个一个全局消息通道方便ViewModel 之间的通信,Toolkit主要包括两部分Bi

MVVM Light须要注意的10个问题

MVVM Light须要注意的10个问题 从使用XAML技术基础開始(实际上并非非常久曾经).我便关注MVVM(Model – View – ViewModel)模式.偶然接触到MVVM Light不久后便喜欢上它的工作方式. 不光我包含业余和专业开发者在内的非常多开发者都喜欢这个函数库. 依照开发者意愿,MVVM Light 不是一个框架而是函数库,该函数库注重于探究建立一个MVVM结构而且提供一些额外的帮助类以便于应用. MVVM Light在发展过程中改变了非常多.非常多元素被增加又有非常

windows universal app中使用mvvm light

新建空的universal app project,在windows 8.1 和 windows phone 8.1 的project中分别添加“MVVM Light libraries only (PCL) NuGet” 包 在shared project 中新建view model 的class using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using System; using System.Collections.

WPF学习10:基于MVVM Light 制作图形编辑工具(1)

图形编辑器的功能如下图所示: 除了MVVM Light 框架是一个新东西之外,本文所涉及内容之前的WPF学习0-9基本都有相关介绍. 本节中,将搭建编辑器的界面,搭建MVVM Light 框架的使用环境. 界面 <Window x:Class="GraphEditor.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http:

MVVM Light需要注意的10个问题

MVVM Light需要注意的10个问题 从使用XAML技术基础开始(实际上并不是很久以前),我便关注MVVM(Model – View – ViewModel)模式.偶然接触到MVVM Light不久后便喜欢上它的工作方式.不光我包括业余和专业开发人员在内的很多开发人员都喜欢这个函数库.按照开发者意愿,MVVM Light 不是一个框架而是函数库,该函数库注重于探究建立一个MVVM结构并且提供一些额外的帮助类以便于应用. MVVM Light在发展过程中改变了很多,很多元素被加入又有很多元素被

MVVM Light Toolkit

DispatcherHelper  因为 ViewModel 是一个 POCO,它不能访问 Dispatcher 属性,因此我需要通过另一种方式来访问主线程,以将操作加入队列中.这是 MVVM Light DispatcherHelper 组件的作用. CheckBeginInvokeOnUI: 顾名思义,此方法首先执行检查.如果此方法的调用方已经在主线程上运行,则无需进行调度.在这种情况下会直接在主线程上立即执行委托.但如果此调用方是在后台线程上,则执行调度. RaisePropertyCha

WPF学习11:基于MVVM Light 制作图形编辑工具(2)

本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTargetBitmap绑定为Image的图片源,这样可以为后续的导出图片功能提供很大的便利. 对拖动栏XAML进行如下修改: <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="

MvvmLight学习篇—— Mvvm Light Toolkit for wpf/silverlight系列(导航)

一.Mvvm Light Toolkit for wpf/silverlight系列之准备工作 二.Mvvm Light Toolkit for wpf/silverlight系列之搭建mvvmlight开发框架 三.Mvvm Light Toolkit for wpf/silverlight系列之数据绑定 四.Mvvm Light Toolkit for wpf/silverlight系列之Command和Events 五.Mvvm Light Toolkit for wpf/silverli