[VB6][VBA][VB.net][自製控制項] 再談 Timer 控制項 - 時間解析度過低的解決方法(三)自製「使用者控制項」

[VB6][VBA][VB.net][自製控制項] 再談 Timer 控制項 - 時間解析度過低的解決方法(三)自製「使用者制項」

  1. 前文介紹了VB.net 用 Stopwatch 提升Timer 解析度的方法,在最後提到了做成獨立的控制項再封裝成 dll 來使用,現在就做它一下。
  2. 下圖是專案完成後,實際拿來測試時的擷圖:

    image image
  3. 我們一步一步進行:

準備活動:

  1. 為使產品能有專業化的外觀(只是外觀啦)決定先做一個 Icon,當它被放在工具箱時會有一張像樣的臉孔。
    1. 最好是包含(16x16、32x32)二種 Format 的 .ico 檔,我用(Axialis IconWorkshop)這套工具。
    2. 手邊如沒有 Icon 工具可用其他繪圖工具或小畫家也可以(圖形格式 ico、png、bmp 等都可)。 

      image
  2. 想一下這個控制項和 VB.net 原來的 Timer 要有哪些不同處,我的想法是這幾項:
    1. 時間的解析度要更高,最好是能辨識到 0.1ms 以上。
    2. 功能也要多一些,要能提供二組計時週期事件(大週期中含有小週期)。
    3. 要有只執行一個計時週期的功能,時間到了就自動 Disabled 不再繼續觸發事件。
    4. 要能輸出方波,並且可調整高低波數比。例如 3:7  的波形就連續3個週期高值(1),再接著7個週期的低值(0)。
    5. 既然是用 Stopwatch 為主角,那就再加送一個傳回 CPU clock 速度的屬性吧。
  3. 準備一段清淨的時間再加一顆清醒的腦袋(這個很重要)。

發展活動:

  1. 建立專案環境:
    1. 準備一個空資料夾命名為「我的專案資料夾」,然後執行 Visual Studio .net 2010(用 2005、2008 應該也行)。
    2. [檔案][新增專案]選擇[類別庫專案]輸入名稱為 myControl → 按下確定。
    3. 在方案總管的根節點右鍵擇[加入][元件][元件類別]輸入名稱為 myTimer → 按下新增。
    4. [檔案][全部儲存]瀏覽到[我的專案資料夾]DblClick 選擇它再按[選擇資料夾]然後[儲存]。
    5. 在方案中加入一個測試專案[檔案][加入][新增專案]選擇[Windows Form 應用程式專案]輸入名稱為 myTimer_測試專案→ 按下確定。
    6. 在方案總管的 myTimer_測試專案 上面按右鍵擇[設為啟動專案]。
    7. [檔案][全部儲存](這次不會再有提示框了)。
  2. 程式碼撰寫:
    1. 先做一次[建置][重建專案]這時你的專案資料夾會長得像下圖(工具箱會出現 myControl 標籤,裡面會有個齒輪狀的控制項):

      image
    2. 在方案總管的 myControl 上面按右鍵擇[在檔案總管開啟]然後把你的圖示檔複製進去,重新整理後就會看到它了。
    3. 在圖示檔名身上按右鍵選擇[加入至專案]後再於屬性欄設為[內嵌資源]如下圖(一)。
    4. 編輯 myTimer.vb 先加入下程式碼,然後[重建專案]一次 。

      Imports System.Diagnostics
      Imports System.ComponentModel
      Imports System.Drawing
      Imports System.Windows.Forms
      
      <DefaultEventAttribute("Tick"), System.Drawing.ToolboxBitmap(GetType(myTimer), "__Ku_Timer.ico")> _
      Public Class myTimer
      #Region "---宣告---"
          Private 第一計時器 As Stopwatch                            '---第一個計時器控管工作總時間。
          Private 第二計時器 As Stopwatch                            '---第二個計時器控管計時小秒。
          Private 小秒週期 As Single = Stopwatch.Frequency / 10000   '---因為要產生 t/10 的事件所以定 0.1ms 為小秒週期。
          Private new_工作時間毫秒數 As Integer = 0                  '---可決定是否產生 TimeUp() 事件,預設0表示不限時。
          Private new_閃爍訊號_Hi_count = 5                          '---輸出閃爍訊號的 Hi_Lo 週期長度比,預設5為1:1(高5_低5)
          Private new_Enabled As Boolean                             '---啟動/停止。
          Private new_Interval As Single = 100                       '---高解析度 Timer 預設週期為 0.1秒。
          Public Enum em方波形狀
              '---決定方波形狀的列舉常數---
              [高1_低9] = 1
              [高2_低8] = 2
              [高3_低7] = 3
              [高4_低6] = 4
              [高5_低5] = 5
              [高6_低4] = 6
              [高7_低3] = 7
              [高8_低2] = 8
              [高9_低1] = 9
          End Enum
          Public Enum em方波訊號值
              '---決定方波訊號 High、Low 值的列舉常數---
              [Low] = 0
              [High] = 1
          End Enum
      #End Region
      #Region "---事件---"
          Event Flash(ByVal sender As Object, ByVal e As EventArgs, ByVal V As em方波訊號值)
          Event Hires_Tick_10x(ByVal sender As Object, ByVal e As EventArgs, ByVal T As Single)
          Event Tick(ByVal sender As Object, ByVal e As EventArgs, ByVal T As Single)
          Event TimeUp(ByVal sender As Object, ByVal e As EventArgs)
      #End Region
      End Class

    1. 再[重建專案]一次工具箱就有它的身影了,下圖可參考。

      image image
    2. 繼續加入屬性和方法的程式碼:

      #Region "---屬性---"
          <Description("當下電腦的 CPU 運算速度"), Browsable(True), Category("其他")> _
          Public ReadOnly Property CPU_GHz() As String
              Get
                  Return Format(Stopwatch.Frequency / 10 ^ 9 * 1024, "##.##0 GHz")
              End Get
          End Property
          <Description("限定工作時間毫秒數"), Browsable(True), Category("行為")> _
          Public Property 工作時間毫秒數設定() As Integer
              Get
                  Return new_工作時間毫秒數
              End Get
              Set(ByVal value As Integer)
                  new_工作時間毫秒數 = value
              End Set
          End Property
          <Description("啟動/停止 Timer"), Browsable(True), Category("行為")> _
          Public Property Enabled() As Boolean
              Get
                  Return new_Enabled
              End Get
              Set(ByVal value As Boolean)
                  new_Enabled = value
                  If DesignMode Then Exit Property
                  If new_Enabled Then
                      第一計時器 = New Stopwatch
                      第一計時器.Restart()
                      計時開始()
                  Else
                      第一計時器 = Nothing
                  End If
              End Set
          End Property
          <Description("事件週期長度(ms)"), Browsable(True), Category("行為")> _
          Public Property Interval() As Single
              Get
                  Return new_Interval
              End Get
              Set(ByVal value As Single)
                  If value < 0 Then value = 0
                  new_Interval = Math.Round(value, 3)
              End Set
          End Property
          <Description("設定輸出閃爍波(方波)的週期樣式"), Browsable(True), Category("行為")> _
          Public Property 方波形狀() As em方波形狀
              Get
                  Return new_閃爍訊號_Hi_count
              End Get
              Set(ByVal value As em方波形狀)
                  new_閃爍訊號_Hi_count = value
              End Set
          End Property
      #End Region
      #Region "---方法---"
          Private Sub 計時開始()
              Static tCounts As Integer
              Dim 小秒週期數 As Integer            '---把一個 Interval 週期均分為10 等分決定定方波形狀---
              第二計時器 = New Stopwatch           '---產生小秒時脈基準---
              第二計時器.Restart()                 '---重新啟動小秒計時---
              Do While (Not DesignMode AndAlso new_Enabled AndAlso new_Interval > 0 AndAlso Container IsNot Nothing)
                  tCounts = 第二計時器.ElapsedTicks
                  If tCounts >= new_Interval * 小秒週期 Then
                      第二計時器.Restart()         '---小秒重新置0---
                      小秒週期數 += 1 : If 小秒週期數 = 10 Then 小秒週期數 = 0 '---統計進行了幾個小秒---
                      If 小秒週期數 >= 0 Then
                          If 事件綜合處理(小秒週期數, tCounts) Then Exit Do    '---傳回 True 表示時間到
                      End If
                  End If
                  Application.DoEvents()
              Loop
          End Sub
          Private Function 事件綜合處理(ByVal 小秒週期數 As Integer, ByVal tCounts As Integer) As Boolean
              '---根據小秒數決定發出哪些事件---
              RaiseEvent Hires_Tick_10x(Me, New System.EventArgs, tCounts / Stopwatch.Frequency)
              If 小秒週期數 = 0 Then
                  RaiseEvent Tick(Me, New System.EventArgs, tCounts / Stopwatch.Frequency * 10)
                  RaiseEvent Flash(Me, New System.EventArgs, em方波訊號值.Low)   '---送出方波 Low ---
              ElseIf 小秒週期數 = new_閃爍訊號_Hi_count Then                     '
                  RaiseEvent Flash(Me, New System.EventArgs, em方波訊號值.High)  '---送出方波 High ---
              End If
              '---檢查是否 Time Up---
              If new_工作時間毫秒數 <> 0 Then
                  If 第一計時器.ElapsedMilliseconds >= new_工作時間毫秒數 Then
                      new_Enabled = False
                      RaiseEvent TimeUp(Me, New System.EventArgs)
                      小秒週期數 = 0
                      Return True
                  End If
              End If
              Return False
          End Function
      #End Region

 

 

 

 

  • 再重建專案一次就完成了(屬性視窗裡該有的都有了)。

    image

 

 

測試:

  1. 在測試專案的 Form1 加入程式碼,然後按[F5]執行。

    Public Class Form1
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Me.Text = MyTimer1.CPU_GHz
        End Sub
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            With MyTimer1
                .工作時間毫秒數設定 = 30    '---執行 30ms 後自動停止.
                .Interval = 10              '---設為 10ms 可解析至 1ms
                .Enabled = True
            End With
        End Sub
        Private Sub Tick(ByVal sender As Object, ByVal e As System.EventArgs, ByVal T As Single) Handles MyTimer1.Tick
            Debug.Print(Format(T, "#0.#####0 s"))
        End Sub
        Private Sub Flash(ByVal sender As Object, ByVal e As System.EventArgs, ByVal V As myControl.myTimer.em方波訊號值) Handles MyTimer1.Flash
            Debug.Print(Format(vbTab & V.ToString))
        End Sub
        Private Sub Hires_Tick_10x(ByVal sender As Object, ByVal e As System.EventArgs, ByVal T As Single) Handles MyTimer1.Hires_Tick_10x
            Debug.Print(vbTab & vbTab & Format(T, "#0.#####0 s"))
        End Sub
    End Class
  2. 結果如下:

    image

  3. 把輸出組件的 myControl.dll 直接加入到工具箱,就可以看到它的外形變美了。

 

結語:

  1. 這篇貼文只在提示建立[使用者控制項]的過程。
  2. 因為接觸 VB.net 時間不長(過去都在玩 VB6 從今年四月才開始學 VB.net)裡面所用的方法和程式碼還很幼稚園級,所以參考就好。

 


ku3