.NET TIPS に WPF/Windowsフォーム:タスクバーのアイコンに進捗表示を出すには?という記事が掲載されていて、これをビューモデルから操作するのをちょっと作ってみたくなったので、やってみました。
やっていることはコードを見れば分かると思うので、先にコードを書いていきます。
まずは、WPF のプロジェクト(プロジェクト名は「TaskBarProgress01」としています)を作ってから、変更通知やコマンド処理を行うライブラリを NuGet から取得します。
ソリューション エクスプローラーでプロジェクトを右クリックして表示されるメニューから「NuGet パッケージの管理」を選択して「makcraft」で検索すると「MakViewModelBase」(拙作)がヒットするのでインストールします。MakVewModelBase クラスについては、説明用のページがあるので、そちらをご覧ください。
つぎに、プロジェクトに「ViewModels」フォルダを作り、ViewModels フォルダを右クリックして表示されるメニューから「追加」にマウスを合わせて表示されるサブメニューから「新しい項目」を選択します。表示される「新しい項目の追加」ダイアログ左の選択肢から「コード」を選び、真ん中の選択肢から「クラス」を選択し、名前を「MainViewModel」として「追加」ボタンをクリックします。
MainViewModel クラスのファイルが作られるので、次のように編集します。
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Shell;
using MakCraft.ViewModels;
namespace TaskBarProgress01.ViewModels
{
class MainViewModel : ViewModelBase
{
private const int COUNTDOWN_TIME = 10;
private TaskbarItemProgressState _progressState = TaskbarItemProgressState.None;
public TaskbarItemProgressState ProgressState
{
get { return _progressState; }
set
{
_progressState = value;
base.RaisePropertyChanged(() => ProgressState);
}
}
public double ProgressValue
{
get { return (COUNTDOWN_TIME - DisplayNumber) / (double)COUNTDOWN_TIME; }
}
private int _displayNumber = 0;
public int DisplayNumber
{
get { return _displayNumber; }
set
{
_displayNumber = value;
base.RaisePropertyChanged(() => DisplayNumber);
base.RaisePropertyChanged(() => ProgressValue);
}
}
private async void countDownExec()
{
ProgressState = TaskbarItemProgressState.Normal;
DisplayNumber = COUNTDOWN_TIME;
_isStartButtonEnabled = false;
for (int i = COUNTDOWN_TIME - 1; i >= 0; i--)
{
await Task.Run(() => System.Threading.Thread.Sleep(1000));
DisplayNumber = i;
}
ProgressState = TaskbarItemProgressState.None;
_isStartButtonEnabled = true;
base.InvalidateRequerySuggested();
DisplayNumber = 0;
}
private bool _isStartButtonEnabled = true;
private bool countDownCanExec(object param)
{
return _isStartButtonEnabled;
}
private ICommand _countDownCommand;
public ICommand CountDownCommand
{
get
{
if (_countDownCommand == null)
{
_countDownCommand = new RelayCommand(countDownExec, countDownCanExec);
}
return _countDownCommand;
}
}
}
}
DisplayNumber プロパティへカウントダウン中の数値を書き込むとタスクバーの進捗表示が更新されるようにしています。
MakViewModelBase ではプロパティ変更通知などが UI スレッドではないスレッド上で発行されると UI スレッド上で行うように補正する機能をもっていますが、このコードでは UI スレッド上への補正機能に関係なく、通知は UI スレッド上で行われます。つまり、awit Task.Run(() => 重たい処理); はスレッドプール上で実行されますが、DisplayNumber = i; は UI スレッド上で実行されます。このあたりの話は ufcpp さんの説明(文章中の「スレッド プール実行」のところ)があるので、興味のある方はそちらをご覧ください。
つぎに View である MainWindow.xaml です。
<Window x:Class="TaskBarProgress01.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:vm="clr-namespace:TaskBarProgress01.ViewModels"
xmlns:local="clr-namespace:TaskBarProgress01"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Window.TaskbarItemInfo>
<TaskbarItemInfo
ProgressState="{Binding ProgressState}" ProgressValue="{Binding ProgressValue}" />
</Window.TaskbarItemInfo>
<StackPanel>
<TextBlock
Text="TaskBar アイコンでの進捗の表示" FontSize="20" TextAlignment="Center"
Margin="10" />
<TextBlock
Text="{Binding DisplayNumber}" FontSize="50" Foreground="Cyan" TextAlignment="Center"
Margin="40" />
<Button
Content="Start" HorizontalAlignment="Center" Padding="20 2"
Command="{Binding CountDownCommand}" />
</StackPanel>
</Window>
こちらは特に説明は必要ないと思いますが、MainViewModel を DataContext として生成し、MainViewModel のプロパティとコマンドを必要な箇所にバインドしています。