網路城邦
上一篇 回創作列表 下一篇   字體:
[Excel VBA] 抄錄 DDE OnCalculate 之預約開關版
2019/06/11 20:47:12瀏覽1542|回應0|推薦4

發佈前一篇「記錄 DDE 傳入的每個 tick」之後,不少朋友來函索取了「抄錄DDE_OnCalculate.v1.0.xls」(後來有 v1.2 版,上週2019/6/4在那篇文章裡補充說明)。其中有位朋友使用後,來信希望我可以做個預設開始抄錄時間的功能,讓他可以把電腦和程式開著,到了預設的時間點就開始抄錄。因為他想抄錄數據的時候,人不能在電腦前面操作。於是寫了這個可以預設開始/結束時間的版本。

※PS. 事實上,以前 「依固定時間抄錄DDE v2.5」(參閱「依固定間隔時間抄錄 DDE 傳入的資料」) 有些朋友提出預設開始/結束時間的要求,那個版本的程式已寫好,請待發表。

使用「抄錄DDE_OnCalculate.v1.4(預約開關版)」

在開啟這個檔案的時候,若你的 Excel 會先詢問是否允許執行檔案裡的巨集,此時看到檔案的畫面可能是這個檔案 "上次存檔時" 的狀態,例如:

當你選擇允許執行檔案裡的巨集之後,"最近一次抄錄時間" 會變成 "***" 一串星號,〝預約開始時間〞會變成距離現在時間的 10 分鐘後,而〝預約結束時間〞會變成距離現在時間的 1小時又10 分鐘後。當你按下右上方那個按鈕時,就會將這兩個時間設定為將要開始執行抄錄的時間與停止抄錄的時間。

當然,在按鈕之前,你可以先修改這兩個儲存格裡的日期/時間,例如:

程式初始化 Workbook_Open() 

當你讓 Excel 允許這個檔案裡的巨集執行的時候,Excel 會立即執行這個檔案裡的 Workbook_Open() 這個副程式 (在專案總管視窗的 ThisWorkbook裡),因此我將程式的一些初始動作寫在這個副程式裡。前面提到的***和 10 分鐘/1小時又10分鐘 等等這些程式本身的預設值,程式碼都是寫在 Workbook_Open() 這個副程式裡面,你可以去修改它們。這個副程式裡面還有一些變數值設定、按鈕上面的文字的設定,稍後解釋。

判斷與執行抄錄 Workbook_SheetCalculate()

OnCalculate 版本的抄錄程式是利用 Excel VBA 具有物件導向 (Object Oriented) 的事件驅動 (Event-Driven) 特性,藉著 On Calculate 事件來自動執行儲存格的抄錄。事實上,前面講到的 Workbook_Open() 就是 "此檔案被開啟" 時觸發 (trigger) 的一個 On Open事件(event);當我們自製的按鈕被按下的時候,就會觸發 CommandButton 的 On Click 事件;而每當 DDE 傳入資料的時候,因為工作表內容有變動就會觸發 On Calculate 事件,Workbook_SheetCalculate() 這個副程式就會被執行,所以我把執行抄錄的程式碼寫在這個副程式裡。

但是,每張工作表有變動的時候都會觸發 On Calculate 事件,所以這個副程式有一個 Sh 參數,將觸發此事件的 "工作表物件" 傳入副程式裡面,因此我們在副程參與式裡面判斷 Sh.Name(工作表名稱) 是不是 "DDE-Input"。此外,為避免重複抄錄到無變動的值,因此在 1.2 版增加了一段比對欄位值的邏輯,沒有欄位值改變就不抄錄 (請參閱「記錄 DDE 傳入的每個 tick」)。

預約開關版在這個副程式裡面特別的是: iScheduled 變數值要為 2 的時候才執行抄錄, iScheduled 這個公用變數是在 Module1 裡面被宣告,在 Workbook_Open() 裡面把它的初值設為 0,它的值會被 Control 工作表的 CommandButton1_Click() 和 Module1 的 procSet_Start()、procSet_Stop() 所改變,用來判斷預約設定的狀態,也用來控制抄錄的執行,本文稍後將解釋它的邏輯。

此外,程式裡面還有執行抄錄的時間判斷,必須 Now() > dScheduledStartTime 且Now() < dScheduledStopTime。dScheduledStartTime 這個公用變數記著要開始抄錄的時間,dScheduledStopTime 這個公用變數記著要結束抄錄的時間,變數值的是在按鈕時給的 (程式碼在 CommandButton1_Click() 裡面)。所以,On Calculate 被觸動的時候,也必須是在設定開始時間之後而且在設定結束時間之前,才執行抄錄。

(為使程式碼較容易閱讀,上面兩段邏輯判斷我用了兩層 if 句子)

 預約開始與預約停止

關於預定什麼時間開始、什麼時間結束,這個版本利用 Excel VBA 的 Application.OnTime 方法 (method),抄錄 DDE_OnCalculate V.1.2 沒有用到這個方法,依固定時間抄錄DDE v2.5」則是用Application.OnTime其詳細用法請參閱 Excel VBA 的說明文件(https://docs.microsoft.com/zh-tw/office/vba/api/excel.application.ontime)  

基本的想法是像這樣:

1.當使用者按下鈕設定開始/停止時間的時候,執行 Application.OnTime 預約 "開始時間(dScheduledStartTime)" 要執行一個副程式: procSet_Start()。

Application.OnTime dScheduledStartTime, "procSet_Start"

並且,要改變公共變數 iScheluled 變數的值 (=1) ,表示已經做了預約。

2.當設定的 "開始時間" 到的時候,procSet_Start() 副程式裡改變公共變數 iScheluled 變數的值 (=2),允許 Workbook_SheetCalculate() 副程式被觸發的時候執行抄錄。並且,在procSet_Start() 副程式裡面執行一行 Application.OnTime 預約 "結束時間(dScheduledStopTime)" 要執行一個副程式: procSet_Stop()。

Application.OnTime dScheduledStopTime, "procSet_Stop"

3.當設定的 "停止時間" 到的時候,procSet_Stop() 副程式裡改變公共變數 iScheluled 變數的值 (=0),不允許 Workbook_SheetCalculate() 副程式被觸發的時候執行抄錄。

取消預約與中止抄錄

不知你有沒有想到這個問題,如果按鈕預約抄錄的開始/停止時間,後來想要更改怎麼辦?如果已經開始抄錄,中途想要停止怎麼辦?

在前面關於開始與停止的解說中,你可以發現我的解説介紹了3個狀態:

● 尚未預約(包括已停止或已取消)
● 已預約但還未到開始時間
● 預約時間已到,正在抄錄 (停止時間還沒到)

因此我不是用布林變數,而是用一個整數變數 iScheduled 的值來表示狀態、進行控制,我用底下這個表來解說:

在前面的解說和秀出的程式碼裡面,已經展示了在Workbook_SheetCalculate() 的程式碼會檢查 iScheduled 的值來判斷當時是否被允許執行抄錄,也看到了在 procSet_Start() 和 procSet_Stop() 裡面,也就是當預約開始/停止時間到的時候,程式碼如何改變 iScheduled 的值來控制抄錄的執行。接下來我們要來看,當操作者手動按鈕預約設定或按鈕取消預約或中止抄錄的時候,程式要怎麼做?

一鈕三用途的 CommandButton1 

前面講過 Excel VBA 物件導向的事件驅動特性,在 Control 工作表的 CommandButton1_Click() 裡,我們可以設計當這個鈕被按下的時候要執行什麼。更進一步地說,我們可以設計讓程式,根據當時的 iScheduled 的狀態來決定要做什麼事。由於我設計 iScheduled 有 0, 1, 2 這三個值,所以用 Select Case 條件選擇句來做判斷-執行。

這裡的設計比較特別的是,每一種狀態執行了它應該做的事之後,如果改變了 iScheduled,下一次按鈕的時候會執行另一個狀態值應該做的事。換言之,這個按鈕有三種功能,由當時 iScheduled 的值決定。所以,程式每次改變  iScheduled 值的時候也同時改變 CommandButton1 這個按鈕上面顯示的文字 (caption),如果你喜歡的話,也可以改變它文字的顏色、底色 ... ... 等等屬性 (attribute,參見附註)。又為了讓操作者更明確的知道當時的狀態,另外也在 Control 工作表的 B4 儲存格顯示文字說明 "預約/抄錄狀態",例如:"已預約 (尚未開始抄錄) ... ...。前面提到過,當預約時間到的時候,被系統時計 (timer) 觸發而執行的  procSet_Start() 和 procSet_Stop() 也會改變 iScheduled 值,它們也是同時會改變按鈕上面顯示的文字和預約/抄錄狀態。

※附註:

如何察看 CommandButton 物件的屬性? (按 Alt+F11) 在 VBA 編輯器環境按 F4 鍵叫出 "屬性視窗",然後點工具列的 "設計模式",回到活頁簿畫面,用滑鼠指標點選 CommandButton (或頁面上其他物件),屬性視窗就會顯示該物件的所有屬性及其內容。(我用的是 Excel 2007 版)

※後記:

1.有位朋友來信說想知道我是如何將問題舖陳變成可以執行的程式,老實說,這個問題不容易回答,只能說是從求學、工作累積下來的知識、經驗吧! (廢話,等於沒回答) 觀察到向我索取程式的許多朋友是剛開始接觸程式設計、VBA的,因此這篇文章試圖多講解一些,但是,關於程式的 "邏輯" 有些本是不易三言兩語解釋清楚的 (有些人會說是 "技巧"),初學程式設計的人需要多花些時間揣摩。

2. 留言可能需等很久很久才會被發現,若有疑問請 email 給我way2cheng@gmail.com (但每天只看一次信箱)

3.需要此程式檔案的朋友們,可以來信向我要xls檔。來信主旨請寫明:

我要抄錄DDE_OnCalculate.v1.4(預約開關版)

4. 依固定時間抄錄DDE 的 預約開關版 程式已經寫好,將另文介紹。

5.本文相關文章連結如下:

[Excel VBA] 固定間隔時間記錄DDE傳入的資料

[Excel VBA] 記錄 DDE 傳入的 (每個) tick

[Excel VBA]如何把個股每5分鐘的成交價格記錄下來?


( 知識學習其他 )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:http://classic-blog.udn.com/article/trackback.jsp?uid=WayCheng&aid=127377611