ビューモデルからタスクバーの進捗表示を更新する

.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 のプロパティとコマンドを必要な箇所にバインドしています。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です