以前、Timerについて「【wpf】よくわかるtimer処理(一定時間間隔イベント)」の記事に記載しましたが、今回はそれを使ったタイマープログラムを作成してみました。
簡易的ではありますが、ミーテイングや発表など決められた時間の中で発言する場合など、こういうちょっとしたタイマーがあれば便利かと思います。
使い方、並びにソースコードの紹介と、Visual Studio 2022でリビルドできるプロジェクト丸ごとダウンロードできるようにもしていますので、自由にカスタマイズして利用して頂ければと思います。
ただし、バグがあるかもしれませんので、その点はご了承ください。
タイマーの画面
今回作ったタイマーは次のようなものになります。

本タイマーには次の機能があります。
- カウントの開始値を設定
- 現在のカウント値を変更
- フォントの指定
- フォント色の指定
- サイズの拡大・縮小
- カウントアップ時の動作設定(カウント停止、ループ、マイナスカウント)
- カウントアップ時の処理登録(プログラムの終了、コマンドの実行)
ダウンロード
次のリンクからダウンロードが可能です。
使い方
右端のボタンをクリックすることで、カウントダウンの開始、一時停止、カウンターのリセット(開始時間に戻す)が可能です。
また、カウンターをダブルクリックすると、現在のカウント値を変更することができます。

また、右クリックで次のメニューが表示されます。

| レイアウト | 数字のフォントと色の設定を行います。 |
| 現在のカウントダウン値を変更 | 現在表示されているカウントダウンの値を変更します。 |
| カウントダウンの開始時間を設定 | カウントダウンの開始値を設定します。 |
| カウントアップ後の動作 | カウントアップ(カウンターが0)時の動作を次の3つから選択します。 1.カウントダウンを停止する。 2.カウントダウンをループする。 3.マイナスになってもカウントダウンを続ける。 |
| カウントアップ時の処理 | カウントアップ(カウンターが0)時に実行した処理の設定します。 次の2つを同時に設定することが可能です。 1.プログラムを終了する。 2.コマンドを実行する。 |
| 終了 | プログラムを終了します。 |
レイアウト
フォントと文字色の設定が可能です。
「初期値に戻す」ボタンをクリックすると、フォントは「Harlow Solid Italic」、文字色は黒が設定されます。

Windowsに標準搭載されているフォントには日本語フォントもあり、当然このプログラムでも日本語フォントの選択は可能なのですが、ドロップダウンリスト上の表記はすべて英語になっています。
英語表記を日本語表記に変換するのが面倒だったので、そのままにしてしまいました。
現在のカウントダウン値を変更
編集モードに移行し、現在表示中のカウントダウンを変更できます。
カウンターをダブルクリックするのと同じです。

カウントダウンの開始時間を設定
カウントダウンの開始時間を設定します。
カウントダウンを停止していなければ、開始時間設定中も裏でカウントダウンが継続され、設定後すぐにカウントダウン値が表示されます。
カウントダウンを停止しても、開始時間設定後はすぐにカウントダウン値が表示されてしまいますので、今設定した開始時間からカウントダウンを再開したい場合はリセットボタンを押して下さい。

カウントダウン後の動作
カウントアップ(カウンターが0)になった時、カウントを停止するか、再び開始時間からカウントを開始するか、マイナスのままカウントを継続するかを選択します。
どれかをチェックすると、他の2つが解除されます。

「カウントダウンをループする」をチェックし、カウントアップ時の処理で「コマンドの実行」をチェックすると、一定時間間隔で任意のコマンドを実行することが可能です。
カウントアップ時の処理
カウントアップ(カウンターが0)になった時の動作を指定します。
「プログラムを終了する」にチェックを入れると、カウントアップ時にプログラムが終了します。
「コマンドを実行する」にチェックを入れると、(実行したいコマンド[ ])で指定されたコマンドが実行されます。
両方がチェックされている場合、コマンドを実行してからプログラムを終了しますので、カウントアップ直後に1度だけコマンドを実行し、本プログラムを終了させることが可能です。

プロジェクト一式のダウンロード先
Visual Stuido 2019のプロジェクト一式は下記からダウンロード可能です。
XAMLのソースコード
XAML側はコメントを全く入れていないのですが、単純に必要なコントロール(テキストボックス、ラベル、コンボボックス、コンテキストメニュー、スタックパネル、グリッドなど)を張り付けているだけです。
以下のような構造になっていますので、これを念頭にXAMLを見ていただければ理解が深まるかと思います。

<Window x:Class="MyTimer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyTimer"
mc:Ignorable="d"
Title="MainWindow" Height="71" Width="203" WindowStyle="None" AllowsTransparency="False" MouseLeftButtonDown="Window_MouseLeftButtonDown" WindowStartupLocation="CenterScreen" Topmost="True" Closed="Window_Closed" Loaded="Window_Loaded">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{x:Null}" />
</Style>
<Style TargetType="Image">
<Setter Property="Stretch" Value="None" />
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
</Style>
<BooleanToVisibilityConverter x:Key="BoolVisibilityConverter" />
<ColorConverter x:Key="ColorConverter"/>
</Window.Resources>
<Grid Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Viewbox Stretch="Uniform" Grid.Row="0" >
<StackPanel>
<TextBox x:Name="uxEditPanel" Visibility="Collapsed" TextWrapping="Wrap" Margin="2,0,0,2" Text="00:00:00" PreviewKeyDown="uxEditPanel_PreviewKeyDown" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="{Binding ElementName=uxFontList,Path=Text}"/>
<TextBlock x:Name="uxCounter" TextWrapping="Wrap" Margin="2,0,0,2" Text="00:00:00" HorizontalAlignment="Center" VerticalAlignment="Center" PreviewMouseDown="uxCounter_PreviewMouseDown" FontFamily="{Binding ElementName=uxFontList,Path=Text}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="レイアウト" >
<StackPanel Orientation="Horizontal">
<Label Content="フォント" Width="50" />
<ComboBox Width="200" x:Name="uxFontList" Margin="0,2,10,2" HorizontalAlignment="Left" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button x:Name="uxDefaultFont" Height="20" VerticalContentAlignment="Center" Click="uxDefaultFont_Click" BorderThickness="1" Content="初期値に戻す"></Button>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="文字色" Width="50"/>
<ComboBox x:Name="uxFontColor" Width="200" Margin="0,2,10,2" ItemsSource="{Binding}" SelectedValue="{Binding Color}" Height="24"
VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalAlignment="Left"
SelectionChanged="uxFontColor_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border Width="20" Height="Auto" Margin="5,0" BorderThickness="1" BorderBrush="Black" >
<Border.Background>
<SolidColorBrush Color="{Binding Color}"/>
</Border.Background>
</Border>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button x:Name="uxDefaultColor" Height="20" VerticalContentAlignment="Center" Click="uxDefaultColor_Click" BorderThickness="1" Content="初期値に戻す"></Button>
</StackPanel>
</MenuItem>
<MenuItem Header="現在のカウントダウン値を変更" Click="EditCountDownMenuItem_Click" ></MenuItem>
<MenuItem Header="カウントダウンの開始時間を設定" Click="SetCountDownMenuItem_Click" ></MenuItem>
<MenuItem Header="カウントアップ後の動作" >
<MenuItem Header="カウントダウンを停止する" x:Name="uxAfterStop" Click="uxCountUpMode_Click" IsCheckable="True"></MenuItem>
<MenuItem Header="カウントダウンをループする" x:Name="uxAfterLoop" Click="uxCountUpMode_Click" IsCheckable="True"></MenuItem>
<MenuItem Header="マイナスになってもカウントダウンを続ける" x:Name="uxAfterNone" Click="uxCountUpMode_Click" IsCheckable="True"></MenuItem>
</MenuItem>
<MenuItem Header="カウントアップ時の処理">
<MenuItem Header="プログラムを終了する" x:Name="uxIsShutdown" IsCheckable="True"></MenuItem>
<MenuItem Header="コマンドを実行する" x:Name="uxIsCommand" IsCheckable="True"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="(実行したいコマンド" Margin="0,0,10,0"/>
<TextBox x:Name="uxCommand" Width="200" />
<TextBlock Text=" )" />
</StackPanel>
</MenuItem>
<MenuItem x:Name="uxShutdownMenuItem" Header="終了" Click="uxShutdownMenuItem_Click" ></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</Viewbox>
<StackPanel Grid.Column="1" Height="63" VerticalAlignment="Top" HorizontalAlignment="Right" Width="30">
<Button x:Name="uxStart" HorizontalAlignment="Center" VerticalAlignment="Top" Click="uxStart_Click">
<Image Source="Images/arrow_run_16xLG.png" />
</Button>
<Button x:Name="uxStop" HorizontalAlignment="Center" VerticalAlignment="Top" Click="uxStop_Click">
<Image Source="Images/Pause_17x16.png" />
</Button>
<Button x:Name="uxReset" HorizontalAlignment="Center" VerticalAlignment="Top" Click="uxReset_Click">
<Image Source="Images/Restart_16x.png" />
</Button>
</StackPanel>
</Grid>
</Window>
C#側のソースコード
C#側のコードについては、次のような構造になっています。

Timerについては、こちらの記事で紹介した関数を使っています。
一定間隔で実行させたい処理はTimerMethodというメソッドに記述していますが、カウントアップ時の処理分岐(タイマー停止、ループ、マイナスカウント、プログラム終了、任意のコマンド実行)が必要なので、その分が少しだけ複雑になっています。
また、任意のコマンド実行時に例外が発生するとプログラムが異常終了してしまいますので、それを防ぐために try~catch で例外を握りつぶしています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.ComponentModel;
using System.Reflection;
using System.Globalization;
namespace MyTimer
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
const string LOOP_MODE = "Loop";
const string STOP_MODE = "Stop";
const string NONE_MODE = "None";
const string EDIT_COUNTER = "Counter";
const string EDIT_TIMER = "Timer";
const string EDIT_NONE = "";
const string DEFAULT_FONT = "Harlow Solid Italic";
const int DEFAULT_COLOR_NO = 7; //black
private DispatcherTimer _timer;
private TimeSpan Timer = new TimeSpan(0, 3, 0);
private TimeSpan _needle;
private TimeSpan _interval = new TimeSpan(0, 0,1);
private TimeSpan _zero = new TimeSpan(0, 0, 0);
private string _mode = STOP_MODE;
private string _target = EDIT_NONE;
public MainWindow()
{
InitializeComponent();
//時間間隔を1秒に設定し、呼び出したいメソッドを指定
_timer = CreateTimer(1000, TimerMethod);
//初期値を設定
_needle = Timer;
uxCounter.Text = Timer.ToString();
//フォント一覧の取得
uxFontList.DataContext = Fonts.SystemFontFamilies;
//色の一覧を取得
uxFontColor.DataContext = GetColorList();
}
/// <summary>
/// 画面表示時のイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//App.config から値を取得
_needle = Timer = TimeSpan.TryParse(Properties.Settings.Default.Timer,out TimeSpan ts) ? ts : Timer;
uxCounter.Text = Timer.ToString();
uxCommand.Text = Properties.Settings.Default.Command;
uxIsCommand.IsChecked = Properties.Settings.Default.IsCommand;
uxIsShutdown.IsChecked = Properties.Settings.Default.IsShutdown;
uxFontList.Text = Properties.Settings.Default.FontName;
uxFontColor.SelectedIndex = Properties.Settings.Default.FontColorNo;
_mode = Properties.Settings.Default.Mode;
//動作モードを復元
switch(_mode)
{
case STOP_MODE: uxAfterStop.IsChecked = true; break;
case LOOP_MODE: uxAfterLoop.IsChecked = true; break;
case NONE_MODE: uxAfterNone.IsChecked = true; break;
default: uxAfterStop.IsChecked = true;break;
}
}
/// <summary>
/// 画面終了時のイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Closed(object sender, EventArgs e)
{
_timer?.Stop();
//App.configに値を保存
Properties.Settings.Default.Timer = Timer.ToString();
Properties.Settings.Default.Command = uxCommand.Text;
Properties.Settings.Default.IsCommand = uxIsCommand.IsChecked;
Properties.Settings.Default.IsShutdown = uxIsShutdown.IsChecked;
Properties.Settings.Default.Mode = _mode;
Properties.Settings.Default.FontName = uxFontList.Text;
Properties.Settings.Default.FontColorNo = uxFontColor.SelectedIndex;
Properties.Settings.Default.Save();
}
/// <summary>
/// フォントを元に戻すボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxDefaultFont_Click(object sender, RoutedEventArgs e)
{
uxFontList.Text = DEFAULT_FONT;
}
/// <summary>
/// 終了メニュークリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxShutdownMenuItem_Click(object sender, EventArgs e)
{
Application.Current.Shutdown();
}
/// <summary>
/// タイマー割り込み時の処理
/// </summary>
private void TimerMethod()
{
//現在の経過時間から1秒マイナスする
_needle = _needle.Add(-_interval);
//現在の経過時間が0より小さくなったら初期値に戻す
if (_needle < _zero)
{
switch (_mode)
{
case STOP_MODE:
_needle = _zero;
_timer.Stop();
break;
case LOOP_MODE:
_needle = Timer;
break;
}
//カウントアップ時の処理で「コマンドを実行する」がチェックされ、コマンドが記述されている場合
if ((bool)uxIsCommand.IsChecked && uxCommand.Text.Trim() != "")
{
try
{
System.Diagnostics.Process.Start(uxCommand.Text);
}
catch
{
}
}
//カウントアップ時の処理で「プログラムを終了する」がチェックされている場合
if ((bool)uxIsShutdown.IsChecked)
{
Application.Current.Shutdown();
}
}
//現在の経過時間を表示
uxCounter.Text = _needle.ToString();
}
/// <summary>
/// 開始ボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxStart_Click(object sender, RoutedEventArgs e)
{
//編集内容のコミット
CommitCounterValue();
// タイマを開始
_timer?.Start();
}
/// <summary>
/// 停止ボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxStop_Click(object sender, RoutedEventArgs e)
{
//編集内容のコミット
CommitCounterValue();
//カウンターの開始
_timer?.Stop();
}
/// <summary>
/// カウントアップ動作モードを選択時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCountUpMode_Click(object sender, RoutedEventArgs e)
{
var menu = (MenuItem)sender;
var header = menu.Header.ToString();
if (header.Contains("停止") && (bool)menu.IsChecked)
{
_mode = STOP_MODE;
uxAfterLoop.IsChecked = uxAfterNone.IsChecked = false;
}
if (header.Contains("ループ") && (bool)menu.IsChecked)
{
_mode = LOOP_MODE;
uxAfterStop.IsChecked = uxAfterNone.IsChecked = false;
}
if (header.Contains("マイナス") && (bool)menu.IsChecked)
{
_mode = NONE_MODE;
uxAfterStop.IsChecked = uxAfterLoop.IsChecked = false;
}
}
/// <summary>
/// フォントのリセットボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxReset_Click(object sender, RoutedEventArgs e)
{
//編集内容のコミット
CommitCounterValue();
//カウンターの停止
_timer?.Stop();
_needle = Timer;
uxCounter.Text = Timer.ToString();
}
/// <summary>
/// 色のリセットボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxDefaultColor_Click(object sender, RoutedEventArgs e)
{
uxFontColor.SelectedIndex = DEFAULT_COLOR_NO;
}
/// <summary>
/// マウス左ボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ButtonState != MouseButtonState.Pressed) return;
this.DragMove();
}
/// <summary>
/// 時間表示部分のクリック処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCounter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(e.ClickCount == 2)
{
EditCounterValue(EDIT_COUNTER);
}
}
/// <summary>
/// タイマー生成
/// </summary>
/// <param name="interval"></param>
/// <param name="action"></param>
/// <returns></returns>
private DispatcherTimer CreateTimer(int interval, Action action)
{
// 優先順位を指定してタイマのインスタンスを生成
DispatcherTimer tm = new DispatcherTimer(DispatcherPriority.Background);
// インターバルを設定
tm.Interval = new TimeSpan(0, 0, 0, 0, interval);
// タイマメソッドを設定
tm.Tick += (e, s) => { action(); };
return tm;
}
/// <summary>
/// カウントダウン開始時間の設定
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SetCountDownMenuItem_Click(object sender, RoutedEventArgs e)
{
EditCounterValue(EDIT_TIMER);
}
/// <summary>
/// 現在のカウントダウン値の編集
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void EditCountDownMenuItem_Click(object sender, RoutedEventArgs e)
{
EditCounterValue(EDIT_COUNTER);
}
/// <summary>
/// 編集モードの開始
/// </summary>
/// <param name="targetName"></param>
private void EditCounterValue(string targetName)
{
_target = targetName;
if (targetName == EDIT_COUNTER)
{
uxEditPanel.Text = uxCounter.Text;
}
if(targetName == EDIT_TIMER)
{
uxEditPanel.Text = Timer.ToString();
}
uxEditPanel.Visibility = Visibility.Visible;
uxCounter.Visibility = Visibility.Collapsed;
}
/// <summary>
/// 編集結果の確定
/// </summary>
private void CommitCounterValue()
{
if (!TimeSpan.TryParse(uxEditPanel.Text, out TimeSpan value))
{
MessageBox.Show("指定された時間が不正です", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
//Timer編集時
if(_target == EDIT_TIMER)
{
Timer = value;
}
//カウンター編集時
if(_target == EDIT_COUNTER)
{
uxCounter.Text = value.ToString();
_needle = value;
}
}
_target = EDIT_NONE;
uxEditPanel.Visibility = Visibility.Collapsed;
uxCounter.Visibility = Visibility.Visible;
}
/// <summary>
/// エンターキーの入力処理(エンターで編集結果を確定)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxEditPanel_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
CommitCounterValue();
}
}
/// <summary>
/// すべての色を取得するメソッド
/// </summary>
/// <returns></returns>
private MyColor[] GetColorList()
{
return typeof(Colors).GetProperties(BindingFlags.Public | BindingFlags.Static)
.Select(i => new MyColor() { Color = (Color)i.GetValue(null), Name = i.Name }).ToArray();
}
/// <summary>
/// 文字色ドロップダウンリスト選択時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxFontColor_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
uxCounter.Foreground = new SolidColorBrush(((MyColor)uxFontColor.SelectedItem).Color);
}
}
/// <summary>
/// 色と色名を保持するクラス
/// </summary>
public class MyColor
{
public Color Color { get; set; }
public string Name { get; set; }
}
}
構成ファイル(app.config)について
画面から変更したフォントや文字色、ダウントダウン初期値、カウントダウン後の動作などの設定情報は、app.config ファイルに保存されています。
今回の場合、プログラムフォルダに存在するMyTimer.exe.config が該当しますが、実はここを読み書きしているのではなく、 次の場所に保管されている user.config が使われます。
C:\Users\ユーザー名\AppData\Local\MyTimer\実行ファイル名から始まるフォルダ自動採番されたフォルダ\1.0.0.0\user.config
例えば、hoge というユーザーが MyTimerを実行した場合、以下のようになります。
C:\Users\hoge\AppData\Local\MyTimer\MyTimer.exe_Url_kftd2iywhtkgtzeowjfyfs0ts3g4o2i5\1.0.0.0\user.config
では、実行ファイルが存在する MyTimer.config は削除してよいかというと、そうではありません。
これはMicrosoftの仕様のようで、プログラム起動時に MyTimer.config を必ず参照するようになっており、もし存在しなければプログラムはフリーズしたままになります。
MyTimer.config は、初回起動時に初期値を参照するだけかと思いきや、そうではありませんので、不要と判断して削除しないようにしてください。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyTimer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<userSettings>
<MyTimer.Properties.Settings>
<setting name="Timer" serializeAs="String">
<value />
</setting>
<setting name="Mode" serializeAs="String">
<value />
</setting>
<setting name="Command" serializeAs="String">
<value />
</setting>
<setting name="IsCommand" serializeAs="String">
<value>False</value>
</setting>
<setting name="IsShutdown" serializeAs="String">
<value>False</value>
</setting>
<setting name="FontName" serializeAs="String">
<value>Harlow Solid Italic</value>
</setting>
<setting name="FontColorNo" serializeAs="String">
<value>0</value>
</setting>
</MyTimer.Properties.Settings>
</userSettings>
</configuration>
まとめ
今回はTimerを使ったタイマープログラムを作成してみたので、その使い方とソースコードについて、簡単な解説を交えて紹介致しました。
Visual Studio 2022 でリビルドするためのプロジェクトもダウンロードできますので、必要に応じてカスタマイズしてご利用いただければと思います。
本来ならMVVMをつかうのが正統派なのかもしれませんが、メリットが全く感じられないので今回はC#でゴリゴリ作成しています。
私自身がWindowsFormに慣れているというものあるのですが、逆にMVVMでプログラミングされている技術者の数が圧倒的に少ないので、むしろ今回の書き方の方がシックリいく方が多いのではないかと思っています。
もし皆さんのプログラミングで何かのヒントになれば幸いです。
コメント