WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时,WPF会自动发出通知去更新UI。

恰当的模式可以让我们轻松达到“高内聚低耦合”,MVVM就是为WPF量身定做的,该模式充分利用了WPF的数据绑定机制,最大限度地降低了XAML和CS文件的耦合度,即UI显示和逻辑代码的耦合度,如需更换界面时,逻辑代码修改很少,甚至不用修改。与WinForm开发相比,我们一般在后置代码中会使用控件的名字来操作控件的属性来更新UI,而在WPF中通常是数据绑定来更新UI。在响应用户操作上,WinForm是通过控件的事件来处理,而WPF可以使用命令绑定的方式来处理,耦合度将降低。

我们可以通过下图来理解MVVM模式:

  View,UI界面,即XAML实现的页面,负责与用户交互,接收用户输入,把数据展现给用户。

  ViewModel,一个C# 类,是View的抽象,负责收集需要绑定的数据和命令,帮助View和Model之间的信息转换,将View的Command传送到Model,聚合Model对象,通过View类的DataContent属性绑定到View,同时也可以处理一些UI逻辑。

  Model,数据访问层,就是系统中的对象,可包含属性和行为。

  一般,View对应一个ViewModel,ViewModel可以聚合N个Model,ViewModel可以对应多个View,Model不知道View和ViewModel的存在。

  View与ViewModel连接可通过下面的方式

  (1)Binding Data:实现数据的传递;

  (2)Command:实现操作的调用;

  (3)AttachBehavior:实现控件加载过程中的操作;

示例讲解

一、Model

    class ButtonInfo
{
public string Content { get; set; }
}

ButtonInfo

    class DownLoadFileInfo
{
public string url = "";
public string fileName = ""; public DownLoadFileInfo(string _url, string _fileName)
{
url = _url;
fileName = _fileName;
}
}

DownLoadFileInfo

    class ProgressBarInfo
{
public long pbCurrentMaxLength { get; set; }
public long pbCurrentLength { get; set; }
public long pbTotalMaxLength { get; set; }
public long pbTotalLength { get; set; }
public ProgressBarInfo()
{ }
public ProgressBarInfo(long pbCurrentMaxLength, long pbCurrentLength, long pbTotalMaxLength, long pbTotalLength)
{
this.pbCurrentMaxLength = pbCurrentMaxLength;
this.pbCurrentLength = pbCurrentLength;
this.pbTotalLength = pbTotalLength;
this.pbTotalMaxLength = pbTotalMaxLength;
}
}

ProgressBarInfo

二、View

<Window x:Class="AutoUpdate_MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="201" Width="505">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="GESBrushes.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource SolidBrushBackground}">
<Button Content="{Binding Button.Content,Mode=TwoWay}" Command="{Binding Pause}" Name="btnPause" HorizontalAlignment="Left" Margin="108,113,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="关闭" Command="{Binding Close}" Name="btnClose" HorizontalAlignment="Left" Margin="251,113,0,0" VerticalAlignment="Top" Width="75" />
<ProgressBar Value="{Binding ProgressBar.pbCurrentLength,Mode=TwoWay}" Maximum="{Binding ProgressBar.pbCurrentMaxLength}" Name="pbCurrent" HorizontalAlignment="Left" Height="16" Margin="90,32,0,0" VerticalAlignment="Top" Width="355"/>
<ProgressBar Value="{Binding ProgressBar.pbTotalLength,Mode=TwoWay}" Maximum="{Binding ProgressBar.pbTotalMaxLength}" Name="pbTotal" HorizontalAlignment="Left" Height="16" Margin="90,65,0,0" VerticalAlignment="Top" Width="355"/>
<Label Content="当前进度:" Foreground="{StaticResource SolidBrushForeground}" Height="28" HorizontalAlignment="Left" Margin="25,25,0,0" Name="label1" VerticalAlignment="Top" FontWeight="Normal" FontStyle="Normal" FontStretch="{Binding}" />
<Label Content="总 进 度:" Foreground="{StaticResource SolidBrushForeground}" Height="28" HorizontalAlignment="Left" Margin="25,59,0,0" Name="label2" VerticalAlignment="Top" />
</Grid>
</Window>

MainWindow.xaml

    public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new DownLoadFile();
}
}

MainWindow.xaml.cs

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="SolidBrushBackground" Color="#FF3A9692"/>
</ResourceDictionary>

GESBrushes.xaml

三、ViewModel

(1)DownLoadFile类,一个实现INotifyPropertyChanged接口的类,目的是绑定数据属性。WPF中实现这个接口的类的属性成员才具有通知UI的能力。

    class DownLoadFile : INotifyPropertyChanged,IDisposable
{
public event PropertyChangedEventHandler PropertyChanged; #region [Object] ManualResetEvent _pauseEvent = new ManualResetEvent(true);
List<DownLoadFileInfo> _listFileInfo = new List<DownLoadFileInfo>();
List<string> _listUrl = new List<string>(); readonly int MAX_BUFFER_SIZE = ; long totalCurrentLength = ;
long totalLength = ; ProgressBarInfo _ProgressBarInfo;
ButtonInfo _ButtonInfo;
HttpWebRequest myrq;
HttpWebResponse myrp; #endregion #region [Property] public bool IsFinish { get; set; } private ProgressBarInfo _ProgressBar;
public ProgressBarInfo ProgressBar
{
get
{
return _ProgressBar;
}
set
{
_ProgressBar = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ProgressBar"));
;
}
}
} private ButtonInfo _Button;
public ButtonInfo Button
{
get { return _Button; }
set
{
_Button = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Button"));
}
}
} public ICommand Pause
{
get
{
return new PauseCommand(this);
}
} public ICommand Close
{
get
{
return new CloseCommand(this);
}
} #endregion #region DownLoadFile
public DownLoadFile()
{
_ProgressBarInfo = new ProgressBarInfo();
_ButtonInfo = new ButtonInfo();
_ButtonInfo.Content = "暂停";
Button = _ButtonInfo; //注意此次的赋值 _listUrl.Add(@"http://127.0.0.1/孙晓林周报(2014-12-11)1.txt");
_listUrl.Add(@"http://127.0.0.1/孙晓林周报(2014-12-11)2.txt"); for (int i = ; i < _listUrl.Count; i++)
{
string url = _listUrl[i];
string[] fileNames = url.Split('/');
if (fileNames.Length > )
{
string fileName = fileNames[fileNames.Length - ];
string fileFullName = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\" + fileName;//文件保存路径 DownLoadFileInfo fileInfo = new DownLoadFileInfo(_listUrl[i], fileFullName);
_listFileInfo.Add(fileInfo);
}
} for (int i = ; i < _listFileInfo.Count; i++)
{
HttpWebRequest myrq = (HttpWebRequest)HttpWebRequest.Create(_listFileInfo[i].url);
HttpWebResponse myrp = (HttpWebResponse)myrq.GetResponse();
totalLength += myrp.ContentLength;
}
//下载文件
Thread newThread = new Thread(new ThreadStart(PerformDownloading));
newThread.Start(); }
#endregion #region PerformDownloading
/// <summary>
/// 下载文件
/// </summary>
private void PerformDownloading()
{
try
{
if (_listFileInfo != null)
{
for (int i = ; i < _listFileInfo.Count; i++)
{
string url = _listFileInfo[i].url;
string fileName = _listFileInfo[i].fileName;
_ProgressBarInfo.pbCurrentLength = ;
//System.Net.ServicePointManager.DefaultConnectionLimit = 50;
System.GC.Collect();
myrq = (HttpWebRequest)HttpWebRequest.Create(url);
myrq.KeepAlive = false;
myrp = (HttpWebResponse)myrq.GetResponse(); _ProgressBarInfo.pbCurrentMaxLength = myrp.ContentLength;
_ProgressBarInfo.pbTotalMaxLength = totalLength; System.IO.Stream st = myrp.GetResponseStream();
System.IO.Stream so = new System.IO.FileStream(fileName, System.IO.FileMode.Create);
long totalDownloadedByte = ; byte[] buffer = new byte[MAX_BUFFER_SIZE];
int osize = ; while (true)
{
_pauseEvent.WaitOne();//阻止当前线程,直到当前 WaitHandle 收到信号。
osize = st.Read(buffer, , MAX_BUFFER_SIZE);
totalDownloadedByte += osize;
totalCurrentLength += osize;
_ProgressBarInfo.pbCurrentLength = totalDownloadedByte;
_ProgressBarInfo.pbTotalLength = totalCurrentLength;
ProgressBar = _ProgressBarInfo;
Thread.Sleep();
if (osize == )
{
break;
}
so.Write(buffer, , osize);
}
so.Close();
st.Close(); CloseHttpWebObject();
}
IsFinish = true;
}
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion #region PauseDownLoad
public void PauseDownLoad()
{ if (Button.Content == "暂停")
{
_ButtonInfo.Content = "继续";
Button = _ButtonInfo;
_pauseEvent.Reset();
}
else
{
_ButtonInfo.Content = "暂停";
Button = _ButtonInfo;
_pauseEvent.Set();
}
}
#endregion #region IDisposable 成员 public void Dispose()
{
_pauseEvent.Close();//释放由当前 WaitHandle 持有的所有资源。 for (int i = ; i < _listFileInfo.Count; i++)
{
_listFileInfo[i] = null;
}
CloseHttpWebObject();
Application.Current.Shutdown();
} private void CloseHttpWebObject()
{
if (myrq != null)
{
myrq.Abort();
}
if (myrp != null)
{
myrp.Close();
}
} #endregion
}

(2)CloseCommand类和PauseCommand类,实现ICommand接口的类,目的是绑定命令属性。WPF中实现ICommand接口的类才能作为命令绑定到UI。

 class CloseCommand:ICommand
{
private DownLoadFile _DownLoadFile;
public CloseCommand(DownLoadFile downLoadFile)
{
_DownLoadFile = downLoadFile;
}
#region Achieve Items
public bool CanExecute(object parameter)//定义用于确定此命令是否可以在当前状态下执行的方法,如果可以执行此命令,返回true,否则返回false。
{
return true;
} public event EventHandler CanExecuteChanged;//当出现影响是否执行该命令的更改时发生 public void Execute(object parameter)//定义在调用此命令时调用的方法
{
_DownLoadFile.Dispose();
}
#endregion
}

CloseCommand

 class PauseCommand : ICommand
{
private DownLoadFile _DownLoadFile;
public PauseCommand(DownLoadFile downLoadFile)
{
_DownLoadFile = downLoadFile;
}
public bool CanExecute(object parameter)
{
if (_DownLoadFile.IsFinish)
{
return false;
}
else
{
return true;
}
} public event EventHandler CanExecuteChanged; public void Execute(object parameter)
{
_DownLoadFile.PauseDownLoad();
}
}

PauseCommand

二、

WPF之MVVM模式讲解的更多相关文章

  1. 【WPF】MVVM模式的3种command

    原文:[WPF]MVVM模式的3种command 1.DelegateCommand 2.RelayCommand 3.AttachbehaviorCommand 因为MVVM模式适合于WPF和SL, ...

  2. 【转】【WPF】MVVM模式的3种command

    1.DelegateCommand 2.RelayCommand 3.AttachbehaviorCommand 因为MVVM模式适合于WPF和SL,所以这3种模式中也有一些小差异,比如RelayCo ...

  3. WPF之MVVM模式(2)

    我们都想追求完美 Every view in the app has an empty codebehind file, except for the standard boilerplate cod ...

  4. WPF之MVVM模式(1)

    MVVM模式 一.MVVM模式概述 MVVM Pattern : Model\View\ViewModel View:视图.UI界面 ViewModel:ViewModel是对Model的封装,通过一 ...

  5. WPF中MVVM模式的 Event 处理

    WPF的有些UI元素有Command属性可以直接实现绑定,如Button 但是很多Event的触发如何绑定到ViewModel中的Command呢? 答案就是使用EventTrigger可以实现. 继 ...

  6. WPF之MVVM模式(3)

    有种想写一个MVVM框架的冲动!!! 1.Model中的属性应不应该支持OnPropertyChanged事件? 不应该.应该有ViewModel对该属性进行封装,由ViewModel提供OnProp ...

  7. WPF中 MVVM模式的Slider Binding.

    对于Button的Command的绑定可以通过实现ICommand接口来进行,但是Slider并没有Command属性. 另外如果要实现MVVM模式的话,需要将一些Method和Slider的Even ...

  8. WPF采用MVVM模式(绑定:纯前台、命令:触发器绑定命令)

    MVVM绑定 view-viewModel-model,模型介绍省略,就是创建类,添加字段封装属性.注:控件的绑定只能绑定到属性上,不能绑定到字段上: 接下来就是代码 (view): <Wind ...

  9. WPF中MVVM模式下控件自有的事件绑定

    1.原因 在WPF中单纯的命令绑定往往不能满足覆盖所有的事件,例如ComboBox的SelectionChanged事件,DataGrid的SelectionChanged事件等等,这时就可以用事件绑 ...

随机推荐

  1. 运行nodejs的blog程序遇见问题

    我是运行这个教程的代码.可以在网上找到相关视频和代码. 第一个问题,数据库中没有创建对应的表就开始运行程序.node app.js 这个错误问题大家可以去重现一下 第二个问题,我也没有看明白,但是我根 ...

  2. sublime设置备份

    Settings-user { "font_face": "Consolas", "font_size": 13, "line_p ...

  3. StringBuilder和Append的一个程序及一个基础概念

    废话少说直接来说:比如在串口数据操作中,我们只想显示串口接收的字符串,好吧你用string[]吧,有多少个字符串(顺便说下二进制在C#中是以字符串形式出现的)就要分配多少个储存空间,自己试下,要你你干 ...

  4. python 学习笔记八 进程和线程 (进阶篇)

    什么是线程(thread)? 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执 ...

  5. nginx 配置文件备份

    1. /etc/nginx/sites-enabled/default 的原始文件 # You may add here your # server { # ... # } # statements ...

  6. 通过Mac远程调试iPhone/iPad上的网页(转)

    我们知道在 Mac/PC 上的浏览器都有 Web 检查器这类的工具(如最著名的 Firebug)对前端开发进行调试,而在 iPhone/iPad 由于限于屏幕的大小和触摸屏的使用习惯,直接对网页调试非 ...

  7. 分享 - 最初的JDBC操作步骤

    /* * 1. 注册 */ // 装载注册 SQLServer Driver Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDri ...

  8. Django ModelForm and Form

    django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm 关于django的表单系统,主要分两种 基于django.forms.Form 基于dj ...

  9. C语言运算符和优先级

    关于C语言运算符和优先级,经整理众多博客资料汇入自己的实战,如下:        a.算术运算        C语言一共有34种运算符,包括常见的加减乘除运算.        1) 加法:+ 还可以表 ...

  10. 【树莓派】树莓派网络配置:静态IP、无线网络、服务等

    一.网络配置之静态IP: 树莓派的默认网络为: haochuang@raspberrypi:~ $ vi /etc/network/interfaces # interfaces() file use ...