惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cybersecurity and Infrastructure Security Agency CISA
D
Darknet – Hacking Tools, Hacker News & Cyber Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Schneier on Security
L
Lohrmann on Cybersecurity
S
Securelist
P
Palo Alto Networks Blog
SecWiki News
SecWiki News
T
Troy Hunt's Blog
H
Hacker News: Front Page
AWS News Blog
AWS News Blog
Latest news
Latest news
Hacker News - Newest:
Hacker News - Newest: "LLM"
NISL@THU
NISL@THU
The Hacker News
The Hacker News
F
Full Disclosure
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
大猫的无限游戏
大猫的无限游戏
O
OpenAI News
P
Proofpoint News Feed
Know Your Adversary
Know Your Adversary
G
GRAHAM CLULEY
博客园_首页
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Security Latest
Security Latest
云风的 BLOG
云风的 BLOG
K
Kaspersky official blog
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
宝玉的分享
宝玉的分享
L
LINUX DO - 热门话题
博客园 - 叶小钗
L
LINUX DO - 最新话题
Martin Fowler
Martin Fowler
N
News | PayPal Newsroom
Project Zero
Project Zero
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
PCI Perspectives
PCI Perspectives
月光博客
月光博客
IT之家
IT之家
Recent Announcements
Recent Announcements
T
The Exploit Database - CXSecurity.com
D
DataBreaches.Net
J
Java Code Geeks
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
Google Online Security Blog
Google Online Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - Saar

LightSwitch 2011 实践之个人汽车仓库 —— 起步 让我们更快一点的Visual Studio的技巧们 实战 WPF Jump List 编程 实战Bing MAP REST服务的封装、调用 选好 Async 函数的返回类型 用WCF + Entity Framework 序列化时的陷阱 大家都来搭架构 —— VS 2010 Layer Diagram (层图) 基本使用 基于Entity Framework 4.1实现一个适用于单元测试的MockDbContext(下) 基于Entity Framework 4.1实现一个适用于测试的MockDbContext(上) C# 5.0功能之Async一瞥 C#不是C++ 之 委托(Delegate) C#不是C++ 之 泛型(Generic) Silverlight 学习笔记——MVVM模式实现主从数据显示 Silverlight 学习笔记——用MediaElement制作在线音乐播放器 Silverlight 学习笔记——应用程序模型 Visual Studio 2010 部分新功能概览-QS, HR, GFU Silverlight学习笔记——依赖属性和路由事件 Silverlight 学习笔记——布局 Visual Studio 2010 部分新功能概览-Dynamic & Named / Optional Parameters
利用IProgress轻松搞定进度条编程
Saar · 2011-07-05 · via 博客园 - Saar

毫无疑问,当一个任务需要较长时间才能完成进,如果有一个进度条显示进度,会比简单的显示一个Please Wait要让人感觉好很多。
然而,一旦涉及进度条,那么,程序至少需要同时做两件事(好吧,也许叫异步的做两件事更确切一点):第一,完成任务本身;第二,计算进度并更新UI。在C# 5.0 Aysnc中提供的IProgress<T>类,为进度条编程提供了便捷的方法。三步即可完成一个简单的进度条。
下面的示例基于Async CTP制作。要能编译、运行示例,需要以下准备:

    1. Visual Studio 2010 SP1.
    2. Async CTP(SP1 Refresh)
    3. 一个WPF应用程序,并且有一个进度条,顺便给进度条取个名,例如progressBar之类的 :-)

如果你还不知道Async是什么,点这里
好了,这样,我们就可以开始示例了。
第一步,写一个类,用于存放参数,我们用这些参数来计算进度。一个最简单的例子就是有两个整数值,一个是当前完成的值,另一个是总值。所以,我们就有了这样一个类:

   1:          //Step 1: Have a class to store current value / total value.
   2:          public class ProgressPartialResult
   3:          {
   4:              public int Current { get; set; }
   5:              public int Total { get; set; }
   6:          }

有些时候,计算进度可能相当的复杂,但是,原则是一样的,把需要的参数全部做在这个类里就行了。当然,也有一种简单的极品:这个类就是一个System.Integer :-)那你就不用自己写了。

第二步,创建一个IProgress<T>的对像,其中,T就是我们刚才创建的类,并且添加相应的处理逻辑,用来处理当进度更新发生时要做的事情。示例中,一旦进度更新,我们就更新UI里的进度条。计算也很简单:当前值/总值*100。

   1:          private void DoProgress()
   2:          {
   3:              //Step 2: Create an object of IProgress<T> to trace the progress & add event handler when new progress reported.
   4:              var progress = new Progress<ProgressPartialResult>();
   5:              progress.ProgressChanged += new ProgressEventHandler<ProgressPartialResult>(progress_ProgressChanged);
   6:              
   7:              //Step 3: Call async logic with progress report.
   8:              DoSomething(progress);
   9:          }
  10:   
  11:          void progress_ProgressChanged(object sender, MainWindow.ProgressPartialResult value)
  12:          {
  13:              progressBar.Value = (float)value.Current / value.Total * 100;
  14:          }

好了,我想你已经知道第三步要做什么了:异步完成任务并且汇报进度。

   1:          private async void DoSomething(IProgress<ProgressPartialResult> progress)
   2:          {
   3:              int total = 100;
   4:              for (int i = 0; i <= total; i++)
   5:              {
   6:                  await TaskEx.Delay(20); //Do things for 0.02 seconds.
   7:                  //Report the progress
   8:                  if (progress != null)
   9:                  {
  10:                      progress.Report(new ProgressPartialResult() { Current = i + 1, Total = total });
  11:                  }
  12:              }
  13:              progress.Report(new ProgressPartialResult() { Current = 0, Total = total });
  14:          }

稍微看一下代码,我们假设总量是100,进度for循环以后,首先做任务本身,我们假设每次做0.02秒。然后,就汇报一下进度。当退出循环以后,把进度置为0。

整理一下,整个事情其实是这样的,进度条编程围绕IProgress<T>的对象展开。每当我们在异步的做事的时候,我们就调用Report方法汇报一下进度,进度的参数我们通过一个预先定义好的类来传递。Report()方法会触发ProgressChanged事件,于是,就会调用事件处理者(Handler)逻辑。处理者则根据传递进来的参数,计算出当前的进度,并且做程序员预期它做的事,例如,更新UI上的进度条之类的。

大家可以试一下进度条行进时拖动UI做点别的事玩玩^v^希望本文能够给你带来一些娱乐 :-)

贴个完整的Code Behind。UI部分大家就自行搞定吧。最最后面附简陋的应用程序贴图。

using System;
using System.Threading.Tasks;
using System.Windows;
 
namespace WpfApplication3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
 
            this.DoProgress();
        }
 
        //Step 1: Have a class to store current value / total value.
        public class ProgressPartialResult
        {
            public int Current { get; set; }
            public int Total { get; set; }
        }
 
        private void DoProgress()
        {
            //Step 2: Create an object of IProgress<T> to trace the progress & add event handler when new progress reported.
            var progress = new Progress<ProgressPartialResult>();
            progress.ProgressChanged += new ProgressEventHandler<ProgressPartialResult>(progress_ProgressChanged);
            
            //Step 3: Call async logic with progress report.
            DoSomething(progress);
        }
 
        void progress_ProgressChanged(object sender, MainWindow.ProgressPartialResult value)
        {
            progressBar.Value = (float)value.Current / value.Total * 100;
        }
 
        private async void DoSomething(IProgress<ProgressPartialResult> progress)
        {
            int total = 100;
            for (int i = 0; i <= total; i++)
            {
                await TaskEx.Delay(20); //Do things for 2 seconds.
                //Report the progress
                if (progress != null)
                {
                    progress.Report(new ProgressPartialResult() { Current = i + 1, Total = total });
                }
            }
            progress.Report(new ProgressPartialResult() { Current = 0, Total = total });
        }
 
    }
}

ProgressBar

7/5:关于原文的一点点补充:

前文提到,进度条编程围绕IProress<T>展开。那么,为什么我们需要这个接口,换句话说,IProgress<T>给我们带来了什么?个人的理解如下,如果我们要异步的完成一个任务,例如DoSomething,并且我们已经有一系列函数可以帮且我们完成这个任务了,那么,调用这个函数时,我们可能会接触到以下一些函数的签名:

void DoSomething();

Task DoSomethingAsync();

Task DoSomethingAsync(CancellationToken ct);

Task DoSomethingAsync(CancellationToken ct, IProgress<int> progress);

再如果一下,如果我们想要知道DoSomething的进度,选哪一个会更合理一些?……好吧,每个人都有选择的权力^v^大家自己参?OK。

其实这四个函数就像四个员工,第一个员工闷头干活,什么都不管;第二个会找别人干活,他本身负责协调,适合同一时间多做几件事(好同志啊),但是,一件活他一旦开始做了,天塌下来他也会继续做;第三个员工,跟二个差不多,只是随叫随停;第四个不仅随叫随停,而且,经常汇报进度。

所以,回到IProgress<T>上来,因为有了它,我们调用有它的函数时,虽然不知道执行的细节,仍然可以轻易的知道事态的进度;在写函数时,如果想要提供一个“进度友好”的函数,不管是给自己还是给别人用,把IProgress<T>作为参数都是一个不错的选择。

Little knowledge is dangerous.