WPF之MVVM模式讲解
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模式讲解的更多相关文章
- 【WPF】MVVM模式的3种command
原文:[WPF]MVVM模式的3种command 1.DelegateCommand 2.RelayCommand 3.AttachbehaviorCommand 因为MVVM模式适合于WPF和SL, ...
- 【转】【WPF】MVVM模式的3种command
1.DelegateCommand 2.RelayCommand 3.AttachbehaviorCommand 因为MVVM模式适合于WPF和SL,所以这3种模式中也有一些小差异,比如RelayCo ...
- WPF之MVVM模式(2)
我们都想追求完美 Every view in the app has an empty codebehind file, except for the standard boilerplate cod ...
- WPF之MVVM模式(1)
MVVM模式 一.MVVM模式概述 MVVM Pattern : Model\View\ViewModel View:视图.UI界面 ViewModel:ViewModel是对Model的封装,通过一 ...
- WPF中MVVM模式的 Event 处理
WPF的有些UI元素有Command属性可以直接实现绑定,如Button 但是很多Event的触发如何绑定到ViewModel中的Command呢? 答案就是使用EventTrigger可以实现. 继 ...
- WPF之MVVM模式(3)
有种想写一个MVVM框架的冲动!!! 1.Model中的属性应不应该支持OnPropertyChanged事件? 不应该.应该有ViewModel对该属性进行封装,由ViewModel提供OnProp ...
- WPF中 MVVM模式的Slider Binding.
对于Button的Command的绑定可以通过实现ICommand接口来进行,但是Slider并没有Command属性. 另外如果要实现MVVM模式的话,需要将一些Method和Slider的Even ...
- WPF采用MVVM模式(绑定:纯前台、命令:触发器绑定命令)
MVVM绑定 view-viewModel-model,模型介绍省略,就是创建类,添加字段封装属性.注:控件的绑定只能绑定到属性上,不能绑定到字段上: 接下来就是代码 (view): <Wind ...
- WPF中MVVM模式下控件自有的事件绑定
1.原因 在WPF中单纯的命令绑定往往不能满足覆盖所有的事件,例如ComboBox的SelectionChanged事件,DataGrid的SelectionChanged事件等等,这时就可以用事件绑 ...
随机推荐
- 运行nodejs的blog程序遇见问题
我是运行这个教程的代码.可以在网上找到相关视频和代码. 第一个问题,数据库中没有创建对应的表就开始运行程序.node app.js 这个错误问题大家可以去重现一下 第二个问题,我也没有看明白,但是我根 ...
- sublime设置备份
Settings-user { "font_face": "Consolas", "font_size": 13, "line_p ...
- StringBuilder和Append的一个程序及一个基础概念
废话少说直接来说:比如在串口数据操作中,我们只想显示串口接收的字符串,好吧你用string[]吧,有多少个字符串(顺便说下二进制在C#中是以字符串形式出现的)就要分配多少个储存空间,自己试下,要你你干 ...
- python 学习笔记八 进程和线程 (进阶篇)
什么是线程(thread)? 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执 ...
- nginx 配置文件备份
1. /etc/nginx/sites-enabled/default 的原始文件 # You may add here your # server { # ... # } # statements ...
- 通过Mac远程调试iPhone/iPad上的网页(转)
我们知道在 Mac/PC 上的浏览器都有 Web 检查器这类的工具(如最著名的 Firebug)对前端开发进行调试,而在 iPhone/iPad 由于限于屏幕的大小和触摸屏的使用习惯,直接对网页调试非 ...
- 分享 - 最初的JDBC操作步骤
/* * 1. 注册 */ // 装载注册 SQLServer Driver Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDri ...
- Django ModelForm and Form
django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm 关于django的表单系统,主要分两种 基于django.forms.Form 基于dj ...
- C语言运算符和优先级
关于C语言运算符和优先级,经整理众多博客资料汇入自己的实战,如下: a.算术运算 C语言一共有34种运算符,包括常见的加减乘除运算. 1) 加法:+ 还可以表 ...
- 【树莓派】树莓派网络配置:静态IP、无线网络、服务等
一.网络配置之静态IP: 树莓派的默认网络为: haochuang@raspberrypi:~ $ vi /etc/network/interfaces # interfaces() file use ...