程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 淺談Excel開發:五 Excel RTD函數

淺談Excel開發:五 Excel RTD函數

編輯:C#入門知識

    上文介紹了Excel中的UDF函數,本文介紹一下同樣重要的RTD函數。從Excel 2002開始,Excel引入了一種新的查看和更新實時數據的機制,即real-time data簡稱RTD函數,他是一種Push-Pull的方式,及在需要更新數據的時候,RTD給Excel Push一個消息說要更新數據,Excel在收到消息後主動拉取Pull新的數據。RTD函數最開始的用途在於更新實時變化的數據,比如股票實時行情數據,實時天氣預報數據,球隊比賽得分數據等等。

    在過去,要實現這些功能,需要依賴一些其他諸如Dynamic Data Exchange(DDE)技術來訪問實時數據資源,但DDE和標准的Excel函數樣式有很大的不同,並且並不是為Excel獲取實時數據而設計的,缺乏健壯性,並且效率不高,RTD的引入解決了這些問題。

    本文首先介紹RTD的一些常用的使用場景,RTD函數的基本結構,注意事項,最後演示如何通過RTD函數來實現從Google Financial API中獲取實時行情數據。

    RTD函數很有用,如果您遇到以下情況,那麼您應當考慮使用RTD函數了:

    Excel RTD函數是一個實現了IRtdServer接口的Com組件,Excel通過該Com組件與實時數據進行交互。要實現RTD 函數,必須要實現IRtdServer這一接口,該接口位於 Microsoft.Office.Interop.Excel命名空間中,跳轉到定義,可以看到該接口的內部:

[()]
[(4160)]
{
    [(11)]
    ConnectData(TopicID, Strings, GetNewValues);

    [(13)]
    DisconnectData(TopicID);
    [(14)]
    Heartbeat();
       
    [(12)]
    RefreshData(TopicCount);
       
    [(10)]
    ServerStart(CallbackObject);
         
    [(15)]
    ServerTerminate();
}

    其中ServerStart參數中有類型為IRTDUpdateEvent接口,該接口的實現為:

[()]
[(4160)]
{
    [(11)]
    HeartbeatInterval { ; ; }

    [(12)]
    Disconnect();
    [(10)]
    UpdateNotify();
}

    巨硬的接口注釋寫的很清楚,不過這裡還是逐條解釋一下,先看IRtdServer接口,這裡依照一般的執行順序講解:

  • ServerStart方法,該方法是在Excel啟動後,第一個調用改RTD主題時執行的方法,該方法返回值為1表示啟動正常,0或者負表示失敗,後面的其他同主題的RTD函數不會被執行。方法唯一的一個參數為IRTDUpdateEvent類型,該對象用來向Excel通知我的新數據准備好了,請求刷新,然後Excel就會去調用RefreshData方法,一般滴,我們會在這個方法類進行一些初始化操作,最常見的就是創建一個IRTDUpdateEvent的局部變量,將該參數賦值給這個局部變量,以方便後面通知Excel刷新數據使用。
  • ConnectData方法,該方法是在當打開一個含有RTD函數的文檔,或者當用戶往單元格中輸入一個RTD函數之後就會執行。該方法第一個參數為TopicID,改ID唯一標識了改單元格內的RTD函數,他是Excel自動分配的,在有些情況下,我們需要定義好自己的數據結構,記錄下TopicID,然後在後面可以刷新指定的TopicID的單元格。第二個參數為Array類型的String對象,一般的利用該參數傳遞RTD中所需要的參數信息。 舉個例子來說,如果我們要編寫實時行情的RTD函數,那麼參數諸如 股票代碼,指標名稱(開盤價,Open)等,這些以數組的形式傳進來。第三個參數為Bool型的GetNewValues,它 用於確定是否總是獲取最新數據,如果這個參數傳入true,則每次保存Excel文檔以後,再次重新打開時,看到的不一定是上次保存時的數據,而是最新的實時數據,這也是一個引用類型的參數。通常如果不設置的話,再次打開包含RTD的表格時,顯示的是最後一次請求的數據。
  • Heartbeat 方法, Excel會調用此方法確定RTD Server服務是否可用,0和負數代表不可用,1代表可用,通常直接返回1。
  • RefreshData方法,參數TopicCount表示要更新的主題數量,用於返回給Excel。該方法返回值是一個二維數組,第一維是要更新數據的TopicID,也就是在ConnectData方法中的TopicID,第二維是該TopicID對應的獲取到的值。當獲取到新的數據的時候,調用ServerStart傳進去的IRTDUpdateEvent類型對象的UpdateNotify方法通知Excel有新數據需要更新,這時Excel就會調用RefreshData方法,然後根據該方法返回包含TopicID的二維數據對工作簿中的數據進行更新。
  • DisconnectData方法,該方法與ConnectData對應,TopcID唯一標識這個函數在Excel中的一個應用。當我們從Excel中移除一個主題(刪除所有采用相同參數的RTD函數)之後,DisconnectData將被調用,在這裡,可以釋放對這個主題的監控,並不再為其獲取新數據。
  • 當Excel不再需要從RTD Server獲取實時數據時被調用。在這裡,可以執行一些清理,例如清除緩存,關閉時鐘等等。至此,一個RTD Server的生命周期就結束了。

    上面的接口介紹完了,現在介紹下IRTDUpdateEvent接口,該接口比較重要的一個方法是UpdateNotify方法。該方法想Excel發出一個通知,提示有新數據需要更新,這是Excel就會調用RefreshData方法,從中讀取到更新後的數據。需要注意的是調用UpdateNofity方法必須要在Excel主線程中進行。

    上面簡單講解了RTD函數的基本原理,接下來演示如何通過Excel RTD來實現從Google Fiancial API中獲取實時行情並刷新數據,Google Financal API,提供了世界各大交易所的實時行情數據,其支持的市場及時效性在其官網上有說明,其使用方法可以參考這篇文章,這裡不細談。其主要思路如下,首先定義好請求的參數,我們的實時行情請求參數有兩個,一個是股票代碼,一個是指標的名稱。因為我們請求的是http,而不是注冊事件回調的方式,所以需要在RTD中使用timer控件去主動拉去請求,然後請求處理完成之後,將結果存儲到對象中,然後調用UpdateNotify方法通知Excel來更新。RTD函數其實是一個注冊為Com組件的類庫,所以我們首先創建一個名為YYGoogleFinancialRTD的類庫:

3.2 創建RTD

    如何創建RTD函數是本文的重點,這裡我們先新建一個名為FinancialRtd的類,然後讓他實現IRtdServer接口。利用VS的自動完成,實現其五個方法。和使用.NET 編寫UDF 一樣,我們需要在類名稱上加一些自定義屬性。這四個屬性中,與UDF相比多了一個ProgID屬性,該屬性唯一標識改RTD函數。

[()]
[()]
[(.AutoDual)]
[()]
: {
    xlRTDUpdate;
    tmrTimer;
    <> stockDatas;
    gfinancial = ;
}

    在實現五個方法之前,我們需要定義一些局部變量,第一個變量是xlRTDUpdate對象,該對象用來保存ServerStart中傳進來的對象的引用,以方便後面調用該對象的UpdateNotify方法。Timer控件用來定時的從http接口中獲取實時行情數據,List<RealStockData>對象用來保存所有的請求及其返回值。Gfinancial對象用來獲取從Google Financial API中獲取的實時行情。由於當前不再交易時間,所以我在收盤價格的基礎上加了一個Random來演示實時變化。這個在下載的代碼中您可以看到。這裡不多講。定義好這些變量之後我們就可以開始編寫代碼了。

    第一個要實現的方法是 ServerStart方法。在該方法中,我們將CallbackObject對象保存到之前定義好的局部變量中,供日後調用該對象的UpdateNotify方法。然後,我們初始化了Timer對象,將其設置為2s去獲取一次實時行情數據。最後返回1表示RTD服務正常開啟。timer的Elapsed方法我們後面介紹。

ServerStart(CallbackObject)
{
    xlRTDUpdate = CallbackObject;

    gfinancial = ();

    tmrTimer = ();
    tmrTimer.Interval = 2000;
    tmrTimer.Elapsed += tmrTimer_Elapsed;

    1;
}

第二個要實現的重要的方法是ConnectData方法:

ConnectData(TopicID, Strings, GetNewValues)
{
    GetNewValues = ;

    strStockCode = Strings.GetValue(0).ToString().ToLower();
    strIndex = Strings.GetValue(1).ToString();

    {
        (stockDatas == )
        {
            stockDatas = <>();
        }

        temp = ();

        temp.StockCode = strStockCode;
        temp.Index = strIndex;


        gfinancial.GetRealStock(temp);

        (temp.Value != )
        {
            stockDatas.Add(temp);
        }

    }
    {
        ;
    }

    (!tmrTimer.Enabled)
    {
        tmrTimer.Start();
    }

    (i = 0; i < stockDatas.Count; i++)
    {
        (stockDatas[i].StockCode.Equals(strStockCode, .OrdinalIgnoreCase) && stockDatas[i].Index.ToString().Equals(strIndex, .OrdinalIgnoreCase))
        {
            (stockDatas[i].TopicId == -1)
            {
                stockDatas[i].TopicId = TopicID;
            }

            stockDatas[i].Value;
        }
    }

    ;
}

    在該方法中,我們首先解析傳進來的參數,根據之前的約定,第一個參數為股票代碼,第二個參數為指標名稱。然後我們實例化了一個RealStockData對象,並給該對象的相關StockCode和Index賦值,然後去為該請求去請求一次實時行情,並將值存儲到對象的Value屬性中。然後將該請求加載到List集合對象中。最後循環判斷是否已經存在,如果已經存在,則直接返回值,否則將該次請求的Excel為其分配的TopicID存到該次對象的TopicId對象中。以便後面刷新時使用。最後如果輸入的參數條件不滿足要求,提示用戶請求格式不正確。每一個單元格的請求僅執行該方法一次。後面就通過刷新機制實現更新了。

    為了連續性,現在介紹timer的Elapse方法,該方法的實現如下:

tmrTimer_Elapsed(sender, e)
{
    gfinancial.GetRealStock(stockDatas);

    xlRTDUpdate.UpdateNotify();
}

     該方法很簡單,第一句去講我們之前保存的List集合的所有請求那過去請求實時行情,並將其返回值,保存到各元素的Value中,然後調用UpdateNotify方法通知Excel新的數據值已經獲取到了,新數據的值就存在stockDatas的各元素的Value屬性中。這時Excel收到UpdateNotify方法之後,就回去調用RefreshData方法,該方法的實現如下:

RefreshData(TopicCount)
{
    [,] rets = [2, stockDatas.Count];
    counter = 0;

    (data stockDatas)
    {
        (data.TopicId != -1)
        {
            rets[0, counter] = data.TopicId;
            rets[1, counter] = data.Value;
        }
        counter++;
    }

    TopicCount = stockDatas.Count;

    rets;
}

    該方法很簡單,首先我們創建了一個二維數組,第二維的大小即為所有有效請求的個數,這裡幾位stockDatas元素的個數。

    然後遍歷所有的請求,填充該二維數組,第一維值為TopicID,Excel會通過該ID去更新對應的單元格,第二維為單元格的值,即將該值填充到對應的TopicID中。另外我們還要通知Excel我們要請求刷新的單元格的個數,這個個數當然就是所有請求的個數啦。最後我們返回這個二位數組。

至此,最重要的幾個方法介紹完了。

    和ConnectData方法對應的DisconnectData方法在我們在Excel中刪除我們之前輸入的RTD函數時觸發,方法實現如下,操作就是從我們的所有請求集合中移除該TopicID對應的請求。

DisconnectData(TopicID)
{
    (i = stockDatas.Count - 1; i > 0; i--)
    {
        (stockDatas[i].TopicId == TopicID)
        {
            stockDatas.RemoveAt(i);
        }
    }

    ((stockDatas == || stockDatas.Count == 0) && tmrTimer.Enabled)
    {
        tmrTimer.Stop();
    }
}

Heartbeat方法僅僅返回1,表示我們的RTD還在運行中。

Heartbeat()
{
    1;
}

最後和ServerStart方法對應的方法ServerTerminate在關閉該RTD工作表時觸發,該方法的主要用來釋放資源,其實現如下:

ServerTerminate()
{
    xlRTDUpdate = ;

    (tmrTimer.Enabled)
    {
        tmrTimer.Stop();
    }

    tmrTimer.Elapsed -= (tmrTimer_Elapsed);
    tmrTimer.Dispose();
}

    至此,我們的函數編寫完了。

   完成之後,和UDF函數一樣,我們需要將該類庫注冊為Com組件,在Visual Studio編譯時勾選注冊為Com組件即可,提醒您的是,在Windows 7 及以上系統上,注冊Com組件涉及到要往注冊表些東西,所以需要當前的Visual Studio以管理員權限運行。

   本文介紹了Excel中一類非常重要的函數,及RTD函數,他是Excel提供的一種Push-Pull機制的數據更新機制,在一些對數據實時性要求較高的場合,比如直播比賽得分,實時天氣預報,交易所實時行情等方面用的很多。並且通過RTD的這種機制我們可以在此基礎上實現異步的UDF函數,即當請求發進來的時候,我們暫不處理,記下TopicID,然後放到處理隊列中,待處理完成後,調用UpdateNotify方法,然後刷新已經處理完的TopicID的單元格。下文將會介紹Excel中異步自定義函數,異步的UDF函數能夠極大地提高Excel插件的用戶體驗,能夠提高系統的可擴展性和穩定性。

   本文所有的代碼點擊此處下載,希望本文對您了解Excel中的RTD函數有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved