前段时间,抽空用WPF做了个邮件群发工具;接下来想用两三篇博客将开发过程中遇到的困惑和积累的经验跟大家分享下,也算是抛砖引玉了,尽管对WinForm开发很熟悉,但毕竟它们之间差别比较大——从界面的呈现到内部的控制等。本篇博客看标题就可清楚,我要说的是进度条(属性改变通知机制)的实现!
进度条在WinForm中实现很容易,代码如下:
1 private void button1_Click(object sender, EventArgs e) 2 { 3 int num = 100; 4 this.progressBar1.Maximum = num; 5 for (int i = 0; i <= num; i = i + 10) 6 { 7 this.progressBar1.Value = i; 8 Thread.Sleep(1000); 9 } 10 }
看似正常的逻辑,在WPF中却没有效果,其原因经测试和分析得出:应该是WinForm里控件和界面在同一个线程中,而WPF里它们不在同一个线程中。
现在切入正题,谈谈WPF中进度条(属性改变通知机制)的实现——
发送结果信息实体类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ComponentModel; 6 7 namespace EmailBatchSend 8 { 9 /// <summary> 10 /// 发送结果信息实体 11 /// </summary> 12 internal class SendResult : INotifyPropertyChanged 13 { 14 public event PropertyChangedEventHandler PropertyChanged; 15 16 private string _progressBarNumShow = string.Empty; 17 /// <summary> 18 /// 进度条数字显示 19 /// </summary> 20 public string ProgressBarNumShow 21 { 22 get 23 { 24 return this._progressBarNumShow; 25 } 26 set 27 { 28 this._progressBarNumShow = value; 29 OnPropertyChanged("ProgressBarNumShow"); 30 } 31 } 32 33 private string _sendResultMes = string.Empty; 34 /// <summary> 35 /// 发送结果消息 36 /// </summary> 37 public string SendResultMes 38 { 39 get 40 { 41 return this._sendResultMes; 42 } 43 set 44 { 45 this._sendResultMes = value; 46 OnPropertyChanged("SendResultMes"); 47 } 48 } 49 50 private string _sendFailEmails = string.Empty; 51 /// <summary> 52 /// 发送失败的结果消息 53 /// </summary> 54 public string SendFailEmails 55 { 56 get 57 { 58 return this._sendFailEmails; 59 } 60 set 61 { 62 this._sendFailEmails = value; 63 OnPropertyChanged("SendFailEmails"); 64 } 65 } 66 67 private int _currentSendNum; 68 /// <summary> 69 /// 当前正在发送的 第几个 70 /// </summary> 71 public int CurrentSendNum 72 { 73 get 74 { 75 return this._currentSendNum; 76 } 77 set 78 { 79 this._currentSendNum = value; 80 OnPropertyChanged("CurrentSendNum"); 81 } 82 } 83 84 private bool _sendControlIsEnabled = true; 85 /// <summary> 86 /// 发送邮件 控件是否可用 87 /// </summary> 88 public bool SendControlIsEnabled { 89 get 90 { 91 return this._sendControlIsEnabled; 92 } 93 set 94 { 95 this._sendControlIsEnabled = value; 96 OnPropertyChanged("SendControlIsEnabled"); 97 } 98 } 99 100 private void OnPropertyChanged(string propertyName) 101 { 102 if (this.PropertyChanged != null) 103 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 104 } 105 } 106 }
界面上的使用
图001
通过上面的代码,可以看出SendResult类实现了INotifyPropertyChanged——此接口的用途是向客户端发出某一属性值已更改的通知,这个接口的定义很简单,如下:
1 namespace System.ComponentModel 2 { 3 // 摘要: 4 // 向客户端发出某一属性值已更改的通知。 5 public interface INotifyPropertyChanged 6 { 7 // 摘要: 8 // 在更改属性值时发生。 9 event PropertyChangedEventHandler PropertyChanged; 10 } 11 }
此接口中只有一个PropertyChanged事件的定义,顾名思义:此事件会在属性值发生改变时被触发调用,且不需要实现,我比较好奇其内部的具体实现。重点看图001中的红色和紫色区域,通过{Binding 属性名}的方式分别设置了进度条的当前值和发送按钮的可用状态,而{Binding 属性名}的用法在WPF中很普遍,其使用有点儿类似于WebForm中的数据绑定控件中的<%#Eval("属性名")>,既然是类似,那同样的还需要给控件设置一个类似DataSource的东东——使控件的数据绑定和具体的数据对象关联起来,才能实现相应的功能。
1 SendResult sendResult = new SendResult(); 2 this.progressBar.DataContext = sendResult;
上面的代码就相当于是设置progressBar的数据源,只不过在WPF中它有另外一个名称叫:DataContext 数据上下文,几乎所有的WPF控件都有DataContext此属性,即都支持数据绑定。
到这里,进度条的实现已OK,效果图如下:
因为对WPF根本谈不上精通,如果有错解的地方,希望大家能指出或谈下自己的看法,感兴趣的朋友,可以留言相互交流学习!