web-dev-qa-db-ja.com

C#ProgressBarを正しく操作するにはどうすればよいですか?アーキテクチャの外観

C#で進行状況バーを操作するための正しいアーキテクチャについて知りたいです。しかし、これはC#だけに適用されるのではないかもしれません。これについては、アーキテクチャを調べる必要があるためです。

質問を例を挙げて説明します。ユーザーがボタンを押すと、プログラムは巨大なアルゴリズムで動作し始めます。アルゴリズムの一部に非常に時間がかかっています。この部分は非常に低いレベルにあり、UIプロジェクトやUIクラスからはかけ離れています。ユーザーに進行状況バーを表示して、このアルゴリズムの進行状況を確認できるようにしたいと思います。

より良い方法は何ですか?アルゴリズムがある下位レベルのプロジェクトからのプログレスバーのビューを接続(参照)しても問題ありませんか?または、既存のUIプロジェクトに進行状況バーのビューを作成し、アルゴリズムの状態に関する情報を(アルゴリズムからUIまで)すべてのレベルで送信する方が良いでしょうか?または、他にもっと良い方法はありますか?

2番目の方法の方が良いと思いますが、実装は難しくなります。

1
Dušan Kalivoda

長期実行アルゴリズムは確かに、その進行状況を示す何らかの方法を使用できます。ただし、アルゴリズムが進行状況バーと通信していることをアルゴリズムが認識するのは不適切です。

アルゴリズムは、進行状況についてアルゴリズムが言うことを取り、進行状況バー、ログファイル、またはオーディオスピーカー上のノイズに変換できるアダプターと通信できる必要があります。

この方法で行われると、これらのメディアへの変更はアルゴリズムに影響を与えません。変更点はアダプターのみ。

そして、それが本当にあなたにとって重要であるならば、はい、私はソフトウェアアーキテクトと呼ばれています。私はコードモンキーという言葉を好む。

8
candied_orange

これが私がこの問題に取り組んだ方法です。重要だと思う点がいくつかあります。

  1. 進行状況をパーツに分割する可能性。作業に別々のフェーズがあるか、各作業項目にかなりの時間がかかるため、各作業項目も進捗状況を報告することはかなり一般的です。
  2. アルゴリズムのコードを書くとき、GUIの更新頻度を気にしたくありません。したがって、進捗状況の報告は、理想的には整数を増分するだけである必要があります。 GUIクラスは、毎秒ごとに進行状況をポーリングするタイマーを作成する必要があります。

したがって、私はこのようなものになってしまいます

public interface IProgress {
    double Completed { get; } // From 0 to 1
}
public class MyProgress : IProgress{
    private IProgress[] subprogresses;
    public double Completed
        =>// Sums all the subprogresses, divided by the count.

    public MyProgress[] Split(int count) {
        // creates new MyProgress objects, sets them as subprogresses, and returns the created objects
    }

    public MyCountingProgress Counting(int totalCount) {
        // Creates a counting progress and sets it as the only subprogress
    };
}

public class MyCountingProgress : IProgress
{
    public MyCountingProgress(int totalCount)
    {
        TotalCount = totalCount;
    }

    public int Count;
    public int TotalCount { get; }
    public double Completed => this.Count / (double)this.TotalCount;
}

呼び出しは通常、次のようになります

var progress = new MyProgress();
Task longRunningTask = DoWork(progress);
MyProgressDialog.ShowDialog("please wait", longRunningTask, progress);

進捗ダイアログは、進捗バーを更新するタイマーを開始し、完了したらダイアログを閉じるタスクの継続を登録し(これがGUIスレッドで実行されるようにTaskScheduler.FromCurrentSynchronizationContext()を使用)、モーダルダイアログを表示します。

さらにいくつかのポイント:

  • 現在のフェーズまたは作業項目の説明テキストを含めることができます。
  • 進行状況にcancelTokenを含めるか、それを個別に保持することができます。
  • 繰り返されるリスト内の各項目の進行状況を報告するlinqデコレーターを作成できます。
  • 作業の一部に他の作業よりも時間がかかることがわかっている場合に備えて、サブプログレスの重みを含めることができます。
  • 進行状況オブジェクトが完了したことを示す別のプロパティが必要になる場合があります。その理由は、フロートの合計が正確に1.0にならない可能性があるためです。
1
JonasH

最初に、バックグラウンド作業を行うためのeスレッド/タスクを含むクラスを作成します。スレッドを作成して開始するメソッドを追加します。

0と1の間の数値、または現在と合計の2つの数値など、進行状況インジケーターを持つイベント引数を使用して、イベントProgressを追加します。

スレッド実行メソッドで各反復でイベントを発生させ、進行状況を報告します。

UIコンポーネントでイベントをサブスクライブします。ハンドラーメソッドのUIスレッドにマーシャリングします(Invoke for Windows Formsを使用)。

UIスレッドで実行されるコードは、進行状況バーを更新できます。

0
Martin Maat

フロントエンドで進行状況を判断する場合は、 Microsoft API を使用して進行状況を報告できます。 サーバー側の進捗状況では、サービス層でサーバーからクライアントへのコールバックを実装する必要があります。私はあなたのアルゴリズムがサーバー側の進行状況の表示を必要としないことを想定しています。

0
user337189