WPF之MVVM模式讲解

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

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

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

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

二、

上一篇:二十八、rsync同步工具深入


下一篇:jQuery中的bind() live() delegate()之间区别分析