C#のWinFormsのトラックバーで値の変更に合わせて重たい処理を行う方法を考える。

コンピュータ

とりあえずサンプルプログラム。

namespace TrackBarLock01;

public partial class Form1 : Form
{
    readonly Label _trackValueLabel = new()
    {
        Size = new System.Drawing.Size(80, 30),
        Location = new System.Drawing.Point(10, 200),
    };
    readonly TrackBar _track = new()
    {
        Size = new System.Drawing.Size(300, 30),
        Location = new System.Drawing.Point(10+80+10, 200),
        Value = 0,
        Maximum = 255,
        Minimum = 0,
    };
    readonly Label _messageLabel = new()
    {
        Size = new System.Drawing.Size(250, 30),
        Location = new System.Drawing.Point(10, 30+10+30),
        Text = "Message",
    };
    static bool _heavyFilterExecFlag = false;
    static async Task HeavyFilter()
    {
        // フラグON
        _heavyFilterExecFlag = true;

        // 重たいフィルター
        await Task.Delay(5000);

        // フラグOFF
        _heavyFilterExecFlag = false;
    }
    public Form1()
    {
        InitializeComponent();

        this.Controls.AddRange([_trackValueLabel,_track,_messageLabel]);

        // トラックバーの値が変更された
        _track.ValueChanged += async (s, e) =>
        {
            // ラベルの値変更
            _trackValueLabel.Text = String.Format("{0}", _track.Value);

            // フィルターが実行中の場合出直す。(ラベルの値は変更する)
            if (_heavyFilterExecFlag)
            {
                // 実行中メッセージ
                _messageLabel.Text = "実行中につきキャンセル";
                return;
            }

            // 開始メッセージ
            _messageLabel.Text = "フィルター開始";

            // フィルター実行
            await HeavyFilter();

            // 完了メッセージ
            _messageLabel.Text = "フィルター完了";

        };

        // フォームロード
        this.Load += (s, e) =>
        {
            // この変更で_track.ValueChangedイベントが発生する
            _track.Value = 127;
        };
    }
}

実行するとフォームのロードイベントでトラックバーの値に127をセットしているので、イベントが発生してフィルターが開始される。

重たいフィルターはTask.Delay(5000)で表現していて、別スレッドで実行することでトラックバーなどのGUIはフィルター実行中でも操作可能になっています。

重たいフィルターの実行フラグを用意し、実行中にトラックバーの変更イベントが発生してもフィルターが実行されないよう二重起動防止のロジックを組み込んでみました。

フィルターの二重起動はしないようになりましたが、トラックバーは移動していて値は変化しているのに、その値を元にフィルターが実行されていません。

フィルター実行中はトラックバーの変更禁止にしてしまう方法もありますが、フィルター実行後トラックバーの値が変化している場合フィルターを再実行するようにしてみます。

変更したプログラムソース。

namespace TrackBarLock01;

public partial class Form1 : Form
{
    readonly Label _trackValueLabel = new()
    {
        Size = new System.Drawing.Size(80, 30),
        Location = new System.Drawing.Point(10, 200),
    };
    readonly TrackBar _track = new()
    {
        Size = new System.Drawing.Size(300, 30),
        Location = new System.Drawing.Point(10+80+10, 200),
        Value = 0,
        Maximum = 255,
        Minimum = 0,
    };
    readonly Label _messageLabel = new()
    {
        Size = new System.Drawing.Size(250, 30),
        Location = new System.Drawing.Point(10, 30+10+30),
        Text = "Message",
    };
    static bool _heavyFilterExecFlag = false;
    static async Task HeavyFilter()
    {
        // フラグON
        _heavyFilterExecFlag = true;

        // 重たいフィルター
        await Task.Delay(5000);

        // フラグOFF
        _heavyFilterExecFlag = false;
    }
    public Form1()
    {
        InitializeComponent();

        this.Controls.AddRange([_trackValueLabel,_track,_messageLabel]);

        // トラックバーの値が変更された
        _track.ValueChanged += async (s, e) =>
        {
            // ラベルの値変更
            _trackValueLabel.Text = String.Format("{0}", _track.Value);

            // フィルターが実行中の場合出直す。(ラベルの値は変更する)
            if (_heavyFilterExecFlag)
            {
                // 実行中メッセージ
                _messageLabel.Text = "実行中につきキャンセル";
                return;
            }

            // トラックバーの値を保存
            var backupValue = _track.Value;
            int currentValue = backupValue;
            do {
                backupValue = currentValue;

                // 開始メッセージ
                _messageLabel.Text = String.Format("フィルター開始。値:{0}", currentValue);

                // フィルター実行
                await HeavyFilter();

                // 完了メッセージ
                _messageLabel.Text = String.Format("フィルター終了。値:{0}", currentValue);

                // 保存していた値と現在のトラックバーの値が異なる場合再実行
                currentValue = _track.Value;
            } while( backupValue != currentValue );
        };

        // フォームロード
        this.Load += (s, e) =>
        {
            // この変更で_track.ValueChangedイベントが発生する
            _track.Value = 127;
        };
    }
}

フィルター終了のタイミングでトラックバーの値に変化が無いか確認し、変換がある場合while()でループするようにしました。
トラックバーを動かし続けるとフィルターも実行し続けますが、フィルターの実行待ちが溜まらないようにしたつもりです。

コメント