MVVM中要控制进度条其实很方便,但是在开始之前我们要解决一个线程问题。
首先WPF的应用程序至少有连个线程,一个是管理UI的线程,一个是绘制UI的线程。我们需要实现类似复制进度的完成率显示,复制线程不能和UI线程相同,否则顾此失彼。一定要创建一个新线程执行操作。通常我们使用Thread类来实现,这次我使用了线程池ThreadPool来实现。
见截图:
窗体代码(窗体上加三个控件ProgressBar+TextBlock+Button):
<Window x:Class="ProgressDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ProgressDemo" Title="MainWindow" MinHeight="117" MinWidth="290" MaxHeight="117" Width="441"> <Window.DataContext> <vm:MainWindowViewModel /> </Window.DataContext> <Grid> <ProgressBar Height="22" Margin="20,12,12,0" VerticalAlignment="Top" Value="{Binding Schedule}"/> <TextBlock Height="23" HorizontalAlignment="Left" Margin="20,43,0,0" VerticalAlignment="Top" Text="{Binding Message}"/> <Button Content="Start" Height="23" Margin="0,43,12,0" Name="button1" VerticalAlignment="Top" HorizontalAlignment="Right" Width="75" Command="{Binding CopyCommand}"/> </Grid> </Window>
ViewModel代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Windows.Input; using Microsoft.Practices.Prism.Commands; using System.Threading; namespace ProgressDemo { public class MainWindowViewModel:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private double _schedule; ///<summary> ///进度百分比 ///</summary> public double Schedule { get { return _schedule; } set { if (_schedule != value) { _schedule = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Schedule")); } } } private string _message; ///<summary> ///显示的消息 ///</summary> public string Message { get { return _message; } set { if (_message != value) { _message = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Message")); } } } /// <summary> /// 模拟复制命令 /// </summary> public ICommand CopyCommand { get { return new DelegateCommand(() => { Random random = new Random(); int count = random.Next(5000, 10000); //使用线程池执行 ThreadPool.QueueUserWorkItem( new WaitCallback(obj => { for (int i = 1; i <= count; i++) { /* * 更新: * 1.完成进度的百分比 * 2.消息文字 * 3.按钮的状态 */ Schedule = i * 100.0 / count; Message = string.Format("{0:0.0}% completed.", Schedule); if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("CopyCommand")); Thread.Sleep(1); } }) ); }, ()=>{ //返回按钮状态是否CanExcute return Schedule == 0 || Schedule == 100; }); } } } }
首先WPF的应用程序至少有连个线程,一个是管理UI的线程,一个是绘制UI的线程。我们需要实现类似复制进度的完成率显示,复制线程不能和UI线程相同,否则顾此失彼。一定要创建一个新线程执行操作。通常我们使用Thread类来实现,这次我使用了线程池ThreadPool来实现。
见截图:

窗体代码(窗体上加三个控件ProgressBar+TextBlock+Button):
<Window x:Class="ProgressDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ProgressDemo" Title="MainWindow" MinHeight="117" MinWidth="290" MaxHeight="117" Width="441"> <Window.DataContext> <vm:MainWindowViewModel /> </Window.DataContext> <Grid> <ProgressBar Height="22" Margin="20,12,12,0" VerticalAlignment="Top" Value="{Binding Schedule}"/> <TextBlock Height="23" HorizontalAlignment="Left" Margin="20,43,0,0" VerticalAlignment="Top" Text="{Binding Message}"/> <Button Content="Start" Height="23" Margin="0,43,12,0" Name="button1" VerticalAlignment="Top" HorizontalAlignment="Right" Width="75" Command="{Binding CopyCommand}"/> </Grid> </Window>
ViewModel代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Windows.Input; using Microsoft.Practices.Prism.Commands; using System.Threading; namespace ProgressDemo { public class MainWindowViewModel:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private double _schedule; ///<summary> ///进度百分比 ///</summary> public double Schedule { get { return _schedule; } set { if (_schedule != value) { _schedule = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Schedule")); } } } private string _message; ///<summary> ///显示的消息 ///</summary> public string Message { get { return _message; } set { if (_message != value) { _message = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Message")); } } } /// <summary> /// 模拟复制命令 /// </summary> public ICommand CopyCommand { get { return new DelegateCommand(() => { Random random = new Random(); int count = random.Next(5000, 10000); //使用线程池执行 ThreadPool.QueueUserWorkItem( new WaitCallback(obj => { for (int i = 1; i <= count; i++) { /* * 更新: * 1.完成进度的百分比 * 2.消息文字 * 3.按钮的状态 */ Schedule = i * 100.0 / count; Message = string.Format("{0:0.0}% completed.", Schedule); if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("CopyCommand")); Thread.Sleep(1); } }) ); }, ()=>{ //返回按钮状态是否CanExcute return Schedule == 0 || Schedule == 100; }); } } } }
注意:
1.ViewModel实现了INotifyPropertyChanged接口,目的通知View数据的更改;
2.CopyCommand是模拟了复制大文件的操作,先创建了一个随机数,相当于文件的大小,然后用循环模拟复制进度。
ThreadPool.QueueUserWorkItem是执行线程池操作,并执行new WaitCallBack中的委托,此处我采用Lambda委托方式,直接将委托的方法代码写在回调函数中,这样的好处是能直接使用变量count,而不需要再传递到方法中;方法中还使用了Thread.Sleep(1),让线程池中的线程暂停1ms,目的不让程序跑得太快。每一次操作中,还更新了两个控件的显示内容,以及按钮的可用状态。
3.DelegateCommand是Prism中的类,需要引用Microsoft.Practices.Prism.dll