1

私のアプリケーションには、進行状況バーとファイル名で構成されるキュー ダウンロード リストがあります。ユーザーがボタンをクリックすると、ファイル名と進行状況バーがインスタンス化され、キューに追加されます。ファイルは一度に 1 つずつ非同期でダウンロードされます。私がやりたいのは、ダウンロードを待機しているファイルのすべての進行状況バーを黄色で保持し、ダウンロード中は緑色に変わり、完了すると青色に変わることです。CheckForIllegalCrossThreadCalls = false;カスタムプログレスバーのコンストラクターにある場合、現在は機能します。プログレスバーにスレッドセーフな変更を加える方法があるかどうかを確認したいと思います。

各キュー アイテムをオブジェクトとして設定しました。ボタンが押されると、メイン フォーム コード (Form1.cs) からキュー アイテム オブジェクトが作成され、進行状況バーがキュー アイテム コンストラクターで作成されます。これがおそらく問題の始まりです。ダウンロードは、キュー アイテム オブジェクトの関数によって開始されます。

キュー アイテムのスニペット

 public class QueueItem
 {
    public bool inProgress;
    public QueueBar bar;

    public QueueItem(args)
    {
         bar = new QueueBar();
         inProgress = false;
         // handle arguments
    }

    public void Download()
    {
        // process info
        WebClient client = new WebClient();
        client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
        client.DownloadFileAsync(url, @savePath);
    }

    private long lastByte = 0;
    private long newByte = 0;
    private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        percentValue = e.ProgressPercentage;
        bar.Value = e.ProgressPercentage;
        newByte = e.BytesReceived;
    }

    private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        // change bar color
        bar.Value = 100;
    }
 }

キュー バーのスニペット

public class QueueBar : ProgressBar
{
    // variables

    public QueueBar()
    {
        this.SetStyle(ControlStyles.UserPaint, true);     
        // initialize variables
    }

    // function to change text properties
    // function to change color

    protected override void OnPaint(PaintEventArgs e)
    {
        // painting
    }
}

主な機能の抜粋

public partial class Form1 : Form
{
    private List<QueueItem> qItems;
    private BackgroundWorker queue;

    private void button_Click(object sender, EventArgs e)
    {
         // basic gist of it
        qItems.Add(new QueueItem(args));
        Label tmpLabel = new Label();
        tmpLabel.Text = filename;
        tmpLabel.Dock = DockStyle.Bottm;
        splitContainerQueue.Panel2.Controls.Add(tmpLabel);
        splitContainerQueue.Panel2.Controls.Add(qItems[qItems.Count - 1].bar);

        if (!queue.IsBusy) { queue.RunWorkerAsync(); }
    }

    private void queue_DoWork(object sender, DoWorkEventArgs e)
    {
        while (qItems.Count > 0)
        {
            if (!qItems[0].inProgress && qItems[0].percentValue == 0)
            {
                qItems[0].inProgress = true;
                qItems[0].Download();
            }
            // else if statements
        }
 }

また、バックグラウンド ワーカーを作成してキュー アイテムを作成し、コントロールを非同期に追加しようとしましたが、分割コンテナーが別のスレッドで作成されたため、機能しません。

4

1 に答える 1

0

UI コントロール (UI スレッドで作成されたもの) を別のスレッドから安全に呼び出すことはできません。そのような呼び出しにはInvokeRequired/を使用する必要があります。BeginInvoke()呼び出すBeginInvoke()ときは、デリゲートを渡します。このようなもの(サンプルコードの一部です。あなたのものは少し異なります):

private void SomeEventHandler ( object oSender, EventArgs oE )
{
    if ( InvokeRequired )
    {
        MethodInvoker oDelegate = (MethodInvoker) delegate
        {
            SomeEventHandler ( oSender, oE );
        };

        BeginInvoke ( oDelegate );
        return;
    }
    else
    {
        // already on the correct thread; access UI controls here
    }
}

また、UI スレッドからプログレス バーを作成することもできません。UI の一部としてすべてのコントロールを作成する必要があります。キュー アイテムからこれらのプログレス バーにアクセスする必要がある場合は、への参照を渡す必要があります。プログレスバー。プログレスバーにアクセスしようとすると、

if ( bar.InvokeRequired ) { ... }

正しいスレッドから呼び出しようとしているかどうかを判断します。

この混乱の理由は、コントロールがメッセージを介してプロパティの更新の多くを処理し、それらのメッセージを正しい順序で同期的に配信する必要があるためです。これを確実にする唯一の方法は (非常に複雑なコーディングを行わなくても)、スレッドがメッセージ ポンプを実行する同じスレッド上にすべてのコントロールを作成することです。

于 2012-09-30T21:30:46.313 に答える