在Silverlight中的DispatcherTimer的Tick中使用基于事件的异步请求

需求:在silverlight用户界面上使用计时器定时刷新数据。

在 Silverlight 中的 DispatcherTimer 的 Tick 事件 中使用异步请求数据时,会出现多次请求的问题,以下是ViewModel的代码,看样子没什么问题:

using System;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace AsyncTest.ViewModel
{
    public class MainPageViewModel : NotificationBase
    {
        private int count;
        public int Count
        {
            get
            {
                return count;
            }
            set
            {
                this.count = value;
                base.OnPropertyChanged("Count");
            }
        }

        protected WcfService.Service1Client client = null;
        private DispatcherTimer timer = null;
        protected int Interval = 1;

        public MainPageViewModel()
        {
            client = new WcfService.Service1Client();
            timer = new DispatcherTimer();
            timer.Interval = new System.TimeSpan(0, 0, Interval);
            timer.Tick += timer_Tick;
            timer.Start();
        }
        private void timer_Tick(object sender, EventArgs ee)
        {
            client.GetDataAsync();
            client.GetDataCompleted += ((s, e) => {
                this.Count++;
            });
        }
    }
}

然而,结果并不是我们预期的那样,每次请求成功后,Count会以这样的数列进行累加:1 3 6 10 15 21 。

经调试三天,排除在View层对ViewMode进行了多次初始化使Timer多次创建实例的可能,其他各种情况都排除后,最终把问题锁定在Tick的方法体。

后来又经过再三调试,终于知道特么的问题在哪了,卧槽我艹,允许我爆一句粗口,搞了两三天,妈蛋,才发现问题这么简单,仔细观察代码,可以看出这是基于事件的异步编程模型

问题:在Tick的方法里,client.GetDataCompleted+=((s,e)=>{//do something}); ,那么每次到了Tick执行周期的时候都会"+=" 一次事件,所以给了我们多次请求的错觉,实际是请求了一次,只是多次执行了Completed事件而已。

这就是为什么Count的结果不是我们预期的 1 2 3 4 5 6 ... ,修改的代码如下:

    private void timer_Tick(object sender, EventArgs ee)
        {
            client.GetDataAsync();
            client.GetDataCompleted += client_GetDataCompleted;
        }

        void client_GetDataCompleted(object sender, WcfService.GetDataCompletedEventArgs e)
        {
            //移除该事件,防止下次Tick事件执行时再次注册
            client.GetDataCompleted -= client_GetDataCompleted;
            this.Count++;
        }
 

将Completed的事件单独提出来,在每次进入到Completed后,使用 "-=" 取消注册该事件,再次进入到Tick的时候,重新请求,重新绑定就OK了,也可以直接在ViewModel的构造函数里绑定Completed事件,每次Tick里只请求client.GetDataAsync(),不需要在进行+=client_GetDataCompleted ,两种方法皆可。

注意:实际项目中用到的是client_GetDataCompeleted事件的e.Result,这里是为了方便调试写的一个Demo,使用Count的目的是为了监测Compeleted到底执行了几次。

心得:每次遇到问题,不要急躁,可以由复杂到简单,若不知问题在哪可使用排除法将问题逐一排除,最后查找到问题所在,仔细分析,不断调试,找到问题根本,得到解决。

完!

时间: 2024-10-07 23:52:36

在Silverlight中的DispatcherTimer的Tick中使用基于事件的异步请求的相关文章

C#中的异步调用及异步设计模式(三)——基于事件的异步模式

四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: ·                  “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序. ·                  同时执行多个操作,每个操作完成时都会接到通知(在通知中可以区分是完成了哪个操作). ·                  等待资源变得可用,但不会停止(“挂起”)您的应用程序. ·  

C#中的异步调用及异步设计模式(二)——基于 IAsyncResult 的异步设计模式

三.基于 IAsyncResult 的异步设计模式(设计层面) IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来实现原同步方法的异步调用,如 FileStream 类提供了 BeginRead 和 EndRead 方法来从文件异步读取字节,它们是 Read 方法的异步版本 Begin 方法包含同步方法签名中的任何参数,此外还包含另外两个参数:一个AsyncCallback 委托和一个用户定义的状态对象.委托用

ArcGIS API For Silverlight 中 用户自定义Symbol在C#中调用

ArcGIS API For Silverlight 中 用户自定义Symbol在C#中调用 1.创建帮助类,其中,Application.LoadComponent的参数/Tel.Jsyd.FileImport;component/Themes/Symbol.xaml,是自定义Symbol所在位置 public class SymbolHelper { static SymbolHelper() { ResourceDictionary rd = new ResourceDictionary(

Silverlight 图表下载到Excel文件中

一.Silverlight xaml.cs文件按钮触发方法 1.//下载图表        private void btnDown_Click(object sender, RoutedEventArgs e)        { DBServiceClient svc = new DBServiceClient();            svc.GenerateExcelReportAsync();            svc.GenerateExcelReportCompleted +=

将Excel导入DataGridView 中的"select * from [Sheet1$]"中[ ]里面表单名的动态获取

Sheet1$是Excel默认的第一个表名,如果改动:select * from [Sheet1$]"将查询失败,因此应根据选择自动获取excel表名: 1 OpenFileDialog ofd = new OpenFileDialog(); //选择文件路径 2 ofd.Title = "Excel文件"; 3 ofd.FileName = ""; 4 ofd.Filter = "Excel文件(*.xls)| *.xls"; 5 s

MATLAB检查指定路径中的子文件夹中的文件名中是否带有空格

测试文件夹为: clear;close all;clc; %% %程序实现的功能 %检查指定路径中的子文件夹中的文件名中是否带有空格,并去掉文件名中的空格 %% %程序中用到的之前不清楚的函数如下 %1)strfind(a,b):即找a中是否有b,如果a中有b,则输出b的位置序号.没有输出空数组 %2)isempty(a):判断数组是否为空 %3)strrep(a,b,c):就是把a中所有出现的b换为c %4)movefile(a,b):a移动为b,如C:\test1.jpg移动为C\test2

在JS中,将text框中数据格式化,根据不同的小数位数,格式化成对应的XXX,XXX,XXX.XX(2位小数) 或者XXX,XXX,XXX(0位小数)

//在JS中,将text框中数据格式化,根据不同的小数位数,格式化成对应的XXX,XXX,XXX.XX(2位小数) 或者XXX,XXX,XXX(0位小数) function formatNum(num, n) {//参数说明:num 要格式化的数字 n 保留小数位 num = String(num.toFixed(n)); var re = /(-?\d+)(\d{3})/; while (re.test(num)) num = num.replace(re, "$1,$2") ret

【ECharts】SSH+JQueryAjax+Json+JSP将数据库中数据填充到ECharts中

1导入包,搭建SSH框架. 导入JQuery的JS包,<script src="JS/jquery-1.7.1.js"></script> 导入ECharts的包.<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-6-10/esl.js"></script> 前提你的SSH已经搭好了,数据已经传递到了Struts层. 2在Action层,将数据封装成J

JSP隐含变量和Spring中Model在EL表达式中的读取顺序

偶然中存在着必然,必然中存在着偶然 偶然出现的bug,必然存是由代码的不合理甚至错误的 代码逻辑越长,越复杂,就越容易出现bug 之前项目里几次偶然出现了一个bug,简单的描述就是第一次新增了之后进行一个B操作,之后在新增一次,页面中的一个隐含变量会"记住"这个新增之后的id,因为这个需要连续两次新增且在第一次新增之后进行B操作之后才会出现,所以很长时间里面都是偶然出现. 定位问题的过程就是进行很多次的操作,然后逐个排除.尝试自己的猜测,再次进行代码级的排除.定位这种问题一定要有一定的