2種のタイマー
.NET Framework の WindowsForms アプリケーションを作成するときにフォームデザイナにペタッと貼り付けるだけで簡単なタイマーは System.Windows.Forms.Timer です。
しかし、このタイマーは CPU 動作が忙しいときなどは実行優先度が下げられてしまいます。リアルタイム性が必要なアプリケーションでは利用できません。
リアルタイム性が大幅に失われるのはイヤだけれども、実行優先度が高いスレッドを起動するまでもないという場合は、System.Threading.Timer スレッディングタイマーの利用が検討できます。

下記のコードは、System.Windows.Forms.Timer と System.Threading.Timer と同じ動作を実施するコードです。
Form1 に下記のコンポーネントを配置してください。
・timer1
・Button button10 (タイマの開始)
・Button button11 (タイマの停止)
・pictureBox1
それぞれに Ticks イベントとClick イベントと Paint イベントを設定してください。
・Button button20 (タイマの開始)
・Button button21 (タイマの停止)
・pictureBox2
それぞれに Click イベントと Paint イベントを設定してください。
timer1 が System.Windows.Forms.Timer です。
timer2 が System.Threading.Timer です。timer2 のほうはフォームデザイナで気軽に配置するようなものではないので自分のコードで生成廃棄、また、それに関連付けられたメソッドを定義します。
メソッドはコールバック関数です。これは別スレッドで動いているので Form1 や、それに配置されたコンポーネントに気軽にアクセスできないので、BeginInvoke のデリゲート経由でアクセスする必要があります。難しいのはその辺だけでしょうか。
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace aaa
{
public partial class Form1 : Form
{
// タイマ間隔.
const int MSEC_INTERVAL = 100;
// 濃度の増加レベル.
const int TICKS_LEVEL = 16;
// それぞれのタイマのカウンタ.
int GlobalCounter1 = 0;
int GlobalCounter2 = 0;
// スレッディングタイマー.
System.Threading.Timer timer2;
public Form1()
{
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e )
{
// System.Windows.Forms.Timer を設定する.
timer1.Interval = MSEC_INTERVAL;
// System.Threading.Timer を設定する.
timer2 = new System.Threading.Timer(
Callback_Timer2, // タイマと関連付けられたメソッド.
null, // ここでパラメータを渡すこともできる.
Timeout.Infinite, // すぐにタイマが動き出さないようにする.
MSEC_INTERVAL // タイマ間隔.
);
}
private void Form1_FormClosing( object sender, FormClosingEventArgs e )
{
// System.Threading.Timer は生成と破棄を管理する必要がある.
if ( timer2 != null )
{
timer2.Dispose();
timer2 = null;
}
}
private void button10_Click( object sender, EventArgs e )
{
timer1.Start();
}
private void button11_Click( object sender, EventArgs e )
{
timer1.Stop();
}
private void timer1_Tick( object sender, EventArgs e )
{
GlobalCounter1 += TICKS_LEVEL;
if ( GlobalCounter1 > 255 )
{
GlobalCounter1 = 0;
}
pictureBox1.Invalidate( true );
}
private void pictureBox1_Paint( object sender, PaintEventArgs e )
{
PictureBox pbx = ( sender as PictureBox );
if ( pbx == null )
{
return;
}
// 青レベルにだけ反映する.
byte level = (byte)( GlobalCounter1 );
Color c = Color.FromArgb( 0x00, 0x00, level );
// 塗りつぶす.
using ( SolidBrush sb = new SolidBrush( c ))
{
e.Graphics.FillRectangle( sb, pbx.ClientRectangle );
}
}
private void button20_Click( object sender, EventArgs e )
{
// スレッディングタイマーをすぐに実行する.
bool ret = timer2.Change( 0, MSEC_INTERVAL );
if ( !ret )
{
MessageBox.Show( "Error: timer2 Start." );
}
}
private void button21_Click( object sender, EventArgs e )
{
// スレッディングタイマーを停止する.
bool ret = timer2.Change( Timeout.Infinite, Timeout.Infinite );
if ( !ret )
{
MessageBox.Show( "Error: timer2 Stop." );
}
}
// System.Threading.Timer の timer2 と関連付けられたメソッド.
private void Callback_Timer2( object parameter )
{
// ここでパラメータを parameter を受け取ることもできる.
try
{
GlobalCounter2 += TICKS_LEVEL;
if ( GlobalCounter2 > 255 )
{
GlobalCounter2 = 0;
}
// 別スレッドでうごいているので pictureBox2.Invalidate( true ); と安易にコールできない.
if ( pictureBox2 != null )
{
pictureBox2.BeginInvoke(( MethodInvoker ) delegate
{
pictureBox2.Invalidate( true );
});
}
}
catch ( Exception excp )
{
Console.WriteLine( $"{excp.Message}" );
}
}
private void pictureBox2_Paint( object sender, PaintEventArgs e )
{
PictureBox pbx = ( sender as PictureBox );
if ( pbx == null )
{
return;
}
// 緑レベルにだけ反映する.
byte level = (byte)( GlobalCounter2 );
Color c = Color.FromArgb( 0x00, level, 0x00 );
// 塗りつぶす.
using ( SolidBrush sb = new SolidBrush( c ))
{
e.Graphics.FillRectangle( sb, pbx.ClientRectangle );
}
}
}
}