您的位置:首页 > 理论基础 > 计算机网络

【原创】WP7中使用进度条——弥补几乎所有网络上与之相关的技术博文的内容缺陷

2013-05-24 23:57 337 查看
首先要声明一点:敢把这篇技术日志名字起的这么霸气侧漏,不是我欠揍,而是实属无奈!WP7发布到现在也有3年的时间了吧!可网络上的技术博文资源啊,真心让人觉得无助。我不是一个痴迷于写技术博文的人,但是如果自己摸索出来的东西网络上没有,或者不全面,这时候我就必须要写下相关的东西了。一方面自己做个总结记录;另一方面,当然想拿出来与大家分享交流,如果你遇到了类似的问题,相信本文章可以引导你顺利走出困境!

--------------------------------------------------------------------

最近抽空在对应用做上架前的最后一轮完善工作,前阵子因为时间的关系,并没有在应用中加入“进度条”显示。但是在耗时的任务中,只能眼巴巴的看着按钮处于“被按下”的状态而不晓得程序到底跑到哪一步了,甚至用户会怀疑是不是程序已经“死了”。总之,耗时任务不配备进度条显示是一个非常不好的用户体验!

之前没有接触过WP7中如何使用进度条,于是,像遇到其他问题一样,先合理利用网络资源。很显然应该如此做,尤其是在IT行业,更是要养成“站在巨人的肩膀上”做事情的习惯。网络上搜罗了一堆相关的技术博文,又一次发现了那些令人揪心的问题。之前从来没有写文字批判过,今天在进入正题之前,先叨叨几句。

一是很多人喜欢转载别人的技术博文,不管内容实用与否,正确与否,是否已经过时,均一字不差复制粘贴,完了在末尾加个文章出处就算了事。这是很多很多博客主的陋习,所以当你上网去查找相关技术的解决方案时,搜索结果可能有很多,但是打开链接,很多很多的文章都一字不差,唯一的差别就是:A文章发表在CSDN,B文章在博客园,C文章在百度空间……


二是网络上充盈着很多不实用的文章。就拿“WP7中使用进度条”举例说吧,居然有那么多的文章,都是单独讲进度条如何用,而不与任何的“任务”挂钩。试想:当你设计一款应用的时候,会出现一个场景,只有进度条孤零零的在那活跃而不关联其他进程吗?显然不会!这个时候,对一个学习者而言,类似那样的文章,恐怕意义也不大了吧。

正是因为这些“广为流传”的技术博文无一能解决我遇到的问题,便自己探索,总结出了如下的在WP7应用中使用进度条的方法。本文区别于其他类似文章有如下几点:

(1)将进度条与实际的处理任务结合起来,真正意义上让进度条成为应用中一个实实在在有用的东西,而不是一个孤零零的应用实例中,就只有一个进度条在那里不明原因的跑啊跑;

(2)WP7中有两类进度条,一种是从左向右不断循环的点状进度条(

),另外一种是可以显示进度的进度条(

)。本文创新性地给出了一种方法,可以针对运行时间不可以预测的任务进行时间量化,从而可以放弃使用某些场景下用户体验并不好的点状进度条,而去使用带有百分比信息的进度条。

下面正式给出WP7应用中完美使用进度条的程序框架!可以根据自己的真实场景稍加代码填充即可。

假象我们需要通过点击一个button完成某个任务(假如这个任务是对一幅图像进行某种处理),我定义这个button的点击事件函数为:

private void btn_Click(object sender, RoutedEventArgs e)
{
    //最初目的:点击按钮调用此函数完成图像处理任务
}


如果想在“处理图像”(记作任务A)的同时还要“显示进度条”(记作任务B),额外开启一个子线程去执行两个任务之一是必然的。但是,谁放在主线程中执行,谁又呆在子线程中呢?经过我反复实验表明:需要把显示进度条的任务B放在主线程中,而你原本想要完成的图像处理任务A则得受点委屈进入到子线程中运行。倘若颠倒过来,则会出现“显示进度条”的任务B被“处理图像”的任务A掩盖起来的现象,也就是说:进度条根本显示不出来!(PS:个人尝试之后发现上述现象,如有误,还请帮忙指正



接下来,框架就清晰起来了。

首先在xaml中添加所需的进度条控件ProgressBar(控件Name为progressBar)和用于显示当前进度百分比的控件TextBlock(控件Name为percentage),具体应该放在什么地方不用我说了吧,当然是在页面ContentPanel所属的<Grid>和</Grid>之间:

<ProgressBar Height="27" Visibility="Collapsed" Foreground="Teal" HorizontalAlignment="Left" Margin="66,460,0,0" Name="progressBar" VerticalAlignment="Top" Width="255"/>
<TextBlock Height="30" Visibility="Collapsed" Foreground="#FFD61111" HorizontalAlignment="Left" Margin="326,460,0,0" Name="percentage" Text="" VerticalAlignment="Top" />


接下来给出完整的程序框架。注意,在实现中使用了委托机制(委托的内容如不清楚可参考网上其他资料,也可看这里:http://www.pocketdigi.com/20110916/476.html):

必要的引用集:

using System.Threading;


程序代码:

public partial class xxxx : PhoneApplicationPage
{
    delegate void ProgressDelegate(int prog); //声明委托类型  
    ProgressDelegate progressDelegate;        //声明委托
    public xxxx()
    {
        InitializeComponent();
    }
    //……
    private void SetProgress(int prog)
    {
        progressBar.Value = prog;
        if (prog == 100)
        {
            progressBar.Visibility = Visibility.Collapsed;//如果达到100,则隐藏进度条
        }
    }
    private void btn_Click(object sender, RoutedEventArgs e)
    {
        progressBar.Visibility = Visibility.Visible;//点击按钮之前为了界面美观,进度条隐藏,进入此函数后使进度条可见
        progressBar.Value = 0;                      //初始值设为0
        percentage.Visibility = Visibility.Visible;
        progressDelegate = SetProgress;                   //委托事件
        new Thread(new ThreadStart(New_thread)).Start();  //开启新线程,用于处理上文中提到的任务A
    }

    private void New_thread()
    {
        //……用于处理任务A的代码
    }
}
值得一提的是上面代码段中New_thread()函数的实现。下***体看看任务A的处理过程如何与进度条所显示的进度关联起来。最最关键的是:如果任务A是时间不可预知的,如何将它的运行时间量化从而使用可以显示当前进度的这种进度条.

假如完成任务A需要经历3个阶段,分别由函数stage_1(),stage_2()和stage_3()依次完成。那么,你可以根据每个阶段的工作量比重对应分配进度,比如阶段1占比重30%,阶段2占40%,阶段3占30%,那么New_thread()便可以如下实现了:

public partial class xxxx : PhoneApplicationPage
{
    //……
    private void New_thread()
    {
        stage_1();
        for (int i = 0; i <= 30; i++)      //进度条从 0% - 30% 逐渐增加显示
        {
            this.Dispatcher.BeginInvoke(progressDelegate, i);
            Dispatcher.BeginInvoke(() => //注意:在非主线程中访问控件一定要把代码这么括起来才可以
            {
                percentage.Text = progressBar.Value.ToString() + "%";
            });
            Thread.Sleep(10);              //线程睡眠可以看出进度变化和百分比变化
        }

        stage_2();
        for (int i = 31; i <= 70; i++)     //进度条从 31% - 70% 逐渐增加显示
        {
            this.Dispatcher.BeginInvoke(progressDelegate, i);
            Dispatcher.BeginInvoke(() =>
            {
                percentage.Text = progressBar.Value.ToString() + "%";
            });
            Thread.Sleep(10);
        }

        stage_3();
        for (int i = 71; i <= 100; i++)    //进度条从 71% - 100% 逐渐增加显示
        {
            this.Dispatcher.BeginInvoke(progressDelegate, i);
            Dispatcher.BeginInvoke(() =>
            {
                percentage.Text = progressBar.Value.ToString() + "%";
            });
            Thread.Sleep(10);
        }
    }
    //……
}
在实现的过程中发现,在非主线程,即上面新建的New_thread线程中,不仅不可以直接访问控件,也无法完成对某些变量的分配内存操作。例如在New_thread()中,如果写出如下的代码,肯定是会报错的:

writeableBitmap img = new writeableBitmap(src);   //假设src是已经在线程外分配好内容的变量
改正的办法很简单:

Dispatcher.BeginInvoke(() =>
{    
    writeableBitmap img = new writeableBitmap(src);
});
这个时候要切记切记一点了!如果后面代码中直接会用到img这个变量,而且不想被莫名其妙的空引用错误而纠结,那就乖乖的在上面代码段后面增加这么一条简单的语句吧:

Thread.Sleep(100);   //线程等待

之所以会报错,是因为如果不进行线程等待,代码会在对img进行初始化之前就跑到后面的代码中去执行。显然,没有对img进行初始化当然会报空引用错误!

上面详细介绍了可以显示进度的进度条的使用方法。如果你因为某些原因需要使用点状循环的进度条,相信你已经知道如何做了吧!没错!修改相应的xaml代码,切记区分两种进度条的原则在这一个属性:

IsIndeterminate="True"
有这个属性就是

;没有则是

。然后稍加修改btn_Click()函数,把上文对应的代码段中的这几句删掉
progressBar.Value = 0;
percentage.Visibility = Visibility.Visible;
progressDelegate = SetProgress;
最后在New_thread()中去掉所有与进度条有关的交互代码,并记得在任务A执行完成后添加一句隐藏点状进度条的代码即可!是不是水到渠成,灰常简单?



上面就是自己摸索总结的WP7中完美使用进度条的方法,相信本文是对网络上现有的相关技术博文在内容和创新性方面的补充。如果你发现了任何有误的地方,感谢第一时间告知我
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐