在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到底執行了幾次。

 

心得:每次遇到問題,不要急躁,可以由復雜到簡單,若不知問題在哪可使用排除法將問題逐一排除,最后查找到問題所在,仔細分析,不斷調試,找到問題根本,得到解決。

 

完!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM