DOBON.NET DOBON.NETプログラミング掲示板過去ログ

Windows.Forms.TimerをForm以外で使う方法

環境/言語:[WindowsXP VB.NET .NET Framework1.1]
分類:[.NET]

いつもお世話になっております。
Windows.Forms.TimerをForm以外で使いたいのですが、ヘルプには
ユーザー定義の間隔でイベントを発生させるタイマを実装します。このタイマは、
Windows フォーム アプリケーションで使用できるように最適化されていて、ウィンドウで使用する必要があります。
というウィンドウ以外では使えないとの記述があります。
しかしウィンドウ以外のクラスで使ってみてもしても動作自体は問題ないように見えます。

ヘルプの記述の「ウィンドウで使用する必要があります。」とはどの様な実装を行う必要があるということなのでしょうか?
どなたかお分かりになる方ご教授よろしくお願いします。

以下Forms.TimerをWindows フォーム デザイナで追加した場合のコードを参考にしたコード

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "

    Public Sub New()
        MyBase.New()

        ' この呼び出しは Windows フォーム デザイナで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後に初期化を追加します。

    End Sub

    ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    ' Windows フォーム デザイナで必要です。
    Private components As System.ComponentModel.IContainer

    ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。
    'Windows フォーム デザイナを使って変更してください。  
    ' コード エディタを使って変更しないでください。
    Friend WithEvents Timer1 As System.Windows.Forms.Timer
    Friend WithEvents Button1 As System.Windows.Forms.Button
    Friend WithEvents Button2 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
        Me.Button1 = New System.Windows.Forms.Button
        Me.Button2 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(8, 8)
        Me.Button1.Name = "Button1"
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Button1"
        '
        'Button2
        '
        Me.Button2.Location = New System.Drawing.Point(88, 8)
        Me.Button2.Name = "Button2"
        Me.Button2.TabIndex = 1
        Me.Button2.Text = "Button2"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.Button2)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private WithEvents _formsTimerTest As FormsTimerTest

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        _formsTimerTest = New FormsTimerTest
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        _formsTimerTest.TimerStart()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        _formsTimerTest.TimerStop()
    End Sub

    Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
        _formsTimerTest.Dispose()
        _formsTimerTest = Nothing
    End Sub

    Private Sub _formsTimerTest_TimerTick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _formsTimerTest.TimerTick
        Debug.WriteLine(Now.ToString)

        If Button1.BackColor.Equals(System.Drawing.Color.Red) Then
            Button1.BackColor = System.Drawing.SystemColors.Control
            Button2.BackColor = System.Drawing.SystemColors.Control
        Else
            Button1.BackColor = System.Drawing.Color.Red
            Button2.BackColor = System.Drawing.Color.Red
        End If
    End Sub
End Class

Public Class FormsTimerTest
    Implements IDisposable

    Private WithEvents _myTimer As Windows.Forms.Timer
    Private components As System.ComponentModel.IContainer
    Public Event TimerTick As EventHandler

    Public Sub New()
        Me.components = New System.ComponentModel.Container
        _myTimer = New System.Windows.Forms.Timer(Me.components)
        _myTimer.Interval = 1000
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
        Debug.WriteLine("FormsTimerTest Dispose")
        If Not (components Is Nothing) Then
            components.Dispose()
        End If
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overrides Sub Finalize()
        Debug.WriteLine("FormsTimerTest Finalize")
        MyBase.Finalize()
        Me.Dispose()
    End Sub

    Public Sub TimerStart()
        _myTimer.Start()
    End Sub

    Public Sub TimerStop()
        _myTimer.Stop()
    End Sub

    Private Sub _myTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _myTimer.Tick
        Debug.WriteLine("myTimer Tick")
        RaiseEvent TimerTick(Me, New EventArgs)
    End Sub

    Private Sub _myTimer_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles _myTimer.Disposed
        Debug.WriteLine("myTimer Dispose")
    End Sub
End Class
ウィンドウで、と言うのは、Windows 的に言うなら「メッセージループを持ったアプリケーションで」と言う意味になります、
System.Windows.Forms.Timer は、Windows メッセージの機構を利用してタイマ処理が行われます。このメッセージを受け取るのがメッセージループですが、.NET Framework ではメッセージループを開始するのには一般的に Application.Run メソッドを使用します。
// 大抵はプログラムのエントリポイントである Main メソッドに記述されますが、これが普通 VB では暗黙に定義されるので直接目には見えないんですよね。ただし、目に触れないだけで実際には存在しています。自前で記述することも可能です。

結論を言ってしまえば、Window アプリケーションならどこでも問題なく System.Windows.Forms.Timer を使えると言うことです。
> Windows.Forms.TimerをForm以外で使いたいのですが、

System.Threading.Timerというのがあります。
Hongliangさん、まどかさんありがとうございます。

System.Threading.TimerやSystem.Timers.Timerも使ってみたのですが、
マルチスレッド的に動作してしまうので、
複数同時に使った時にスレッドプールから呼び出される順番を制御しきれなくて
処理順番が前後してしまったので、
単純なSystem.Windows.Forms.Timerを利用しようと思いました。

WindowsアプリケーションならどこでもSystem.Windows.Forms.Timerを
使えるとのことですが、
フォームデザイナで生成されるコードのタイマ初期化時に渡されている
System.ComponentModel.IContainerインターフェイスもデザイナを使わない
クラスでの使用であれば渡す必要もないのでしょうか?
ご教授よろしくお願いします。
> フォームデザイナで生成されるコードのタイマ初期化時に渡されている
> System.ComponentModel.IContainerインターフェイスもデザイナを使わない
> クラスでの使用であれば渡す必要もないのでしょうか?

はい。
基本的に、あれは Form の Dispose を呼び出されるときに自動的にタイマなどのコンポーネントの Dispose を呼び出すようするための仕掛けです。
これを使わない場合、使い終わったら自前で Dispose するようにしますが、使い終わるタイミングが分からないのなら(タイマを持っているインスタンスと寿命が同じなら)そのまま放っておいても特に問題はないでしょう。
気にせずどうぞ。


> System.Threading.TimerやSystem.Timers.Timerも使ってみたのですが、
> マルチスレッド的に動作してしまうので、
> 複数同時に使った時にスレッドプールから呼び出される順番を制御しきれなくて
> 処理順番が前後してしまったので、
> 単純なSystem.Windows.Forms.Timerを利用しようと思いました。

ん〜……? なんかこうもやもや……。
Hongliangさんありがとうございます。

わかりづらい説明で混乱させてしまったでしょうか。
どうして使いこなせないかの説明すら上手にできないので
きっと自分にはまだスレッドプールの利用は早いのでしょう。

これで安心してSystem.Windows.Forms.Timerを利用することができます。
ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板