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

ボタン点滅のプログラミングについて

環境/言語:[XP,VB.NET2003,.NET Framework1.1]
分類:[.NET]

いつもお世話になります。ご教授の程よろしくお願い申し上げます。

 現在ボタン点滅のプログラムを作成しておりますが、処理概要と致しまして、
1)点滅するためのボタンをハッシュテーブルに格納
2)1秒間に1回点滅するためのスレッドにて、上記1)のハッシュテーブルに格納されているコントロールに対し指定された点滅色に変更する
と、しております。(別途コーディングをご参照)

ご質問ですが、
 System.Threading.Timerを使用した場合、フォームのボタンのプロパティを変更することは特に問題はないのでしょうか?
 上記1)と2)の処理においてボタン情報の整合性を保つため、また、上記2)は別スレッドであるため、SyncLockを使用する必要があると考えました。
 DOBONさんのマルチスレッドに関するご説明において(MSDNにも記述されておりました)別スレッドからフォームのコントロールを使用する場合は、デリゲートを使用するとございましたが、本処理におきましても必要となるのか?が気になっております。
 ご教授の程よろしくお願い申し上げます。
 最後ですがコード抜粋を添付させていただきます。

Public Class frmMain
    Inherits System.Windows.Forms.Form
    Private mclsBlinkLightCtrl As New clsBlinkLightCtrl
    Private Sub Recieves(btn as Button)
   mclsBlinkLightCtrl.BlinkCallOn(btn)
    End Sub

Public Class clsBlinkLightCtrl
    Private Shared blnBlinkCalling As Boolean = False
    Private Shared tmrOnePerSec As New System.Threading.Timer(New TimerCallback(AddressOf tmrOnePerSec_Tick), Nothing, 0, 1000)
    Public Shared Sub BlinkCallOn(ByVal objBtn As Button)
        SyncLock htBlinkCall.SyncRoot
            '点滅コントロールハッシュテーブルに該当ボタン格納
            If htBlinkCall.ContainsKey(objBtn) Then
            Else
                htBlinkCall.Add(objBtn, objBtn.BackColor.ToArgb)
            End If
        End SyncLock
    End Sub
    Private Shared Sub tmrOnePerSec_Tick(ByVal state As Object)
        Dim myEnumerator As IDictionaryEnumerator
        SyncLock htBlinkCall.SyncRoot
            myEnumerator = htBlinkCall.GetEnumerator()
            If blnBlinkCalling Then
                '点滅前
                blnBlinkCalling = False
                While myEnumerator.MoveNext
                    DirectCast(myEnumerator.Key, Button).BackColor = Color.FromArgb(DirectCast(myEnumerator.Value, Integer))
                End While
            Else
                '点滅色
                blnBlinkCalling = True
                While myEnumerator.MoveNext
                    DirectCast(myEnumerator.Key, Button).BackColor = Color.FromArgb(0, 255, 0)
                End While
            End If
        End SyncLock
    End Sub
2006/03/21(Tue) 05:38:12 編集(投稿者)

>DOBONさんのマルチスレッドに関するご説明において(MSDNにも記述されておりま
>した)別スレッドからフォームのコントロールを使用する場合は、デリゲートを
>使用するとございましたが、本処理におきましても必要となるのか?が気になっ
>ております。

基本的に別スレッドからコントロールにアクセスできません。
VB2005で試したところ,BackColorの変更ではなぜかエラーになりませんが
BackColorの代わりにTextを変更しようとするとエラーになりました。
デリゲートを使用してアクセスするか,タイマーをWindows.Forms.Timerに
してシングルスレッドで実行するか,どちらかにした方がよいかと思います。

タイマーをWindows.Forms.Timerに変えると以下のようになると思います。
(その他,適当にリファクタリング?してみました)

Option Strict On

Public Class Form1
    Dim BlinkLightCtrl As New BlinkLightCtrl
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BlinkLightCtrl.BlinkCallOn(Button1)
    End Sub
End Class

Public Class BlinkLightCtrl
    Private htBlinkCall As New Collections.Hashtable
    Private blnBlinkCalling As Boolean = False
    Private WithEvents Timer As New Timer
    Public Sub BlinkCallOn(ByVal objBtn As Button)
        Me.htBlinkCall.Add(objBtn, objBtn.BackColor.ToArgb)
    End Sub
    Public Sub New()
        Timer.Interval = 1000
        Timer.Enabled = True
    End Sub
    Private Sub tmrOnePerSec_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer.Tick
        For Each de As DictionaryEntry In Me.htBlinkCall
            DirectCast(de.Key, Button).BackColor = Color.FromArgb(CInt(IIf(blnBlinkCalling, de.Value, &HFF00FF00I)))
            Me.blnBlinkCalling = Not Me.blnBlinkCalling
        Next
    End Sub
End Class
スレッドタイマーの代わりに、System.Timers.Timerを使う方法も考えられます。この場合、SynchronizingObjectプロパティにフォームを指定することにより、自動的にマーシャリングが行われるようになります。

非同期メソッド
http://codezine.jp/a/article.aspx?aid=139&p=2#begin

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