Cách sử dụng thanh tiến trình WinForms?


101

Tôi muốn hiển thị tiến trình tính toán đang thực hiện trong thư viện bên ngoài.

Ví dụ: nếu tôi có một số phương pháp tính toán và tôi muốn sử dụng nó cho 100000 giá trị trong lớp Biểu mẫu của mình, tôi có thể viết:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }            

    private void Caluculate(int i)
    {
        double pow = Math.Pow(i, i);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        progressBar1.Maximum = 100000;
        progressBar1.Step = 1;

        for(int j = 0; j < 100000; j++)
        {
            Caluculate(j);
            progressBar1.PerformStep();
        }
    }
}

Tôi nên thực hiện từng bước sau mỗi phép tính. Nhưng điều gì sẽ xảy ra nếu tôi thực hiện tất cả 100000 phép tính trong phương pháp bên ngoài. Khi nào tôi nên "thực hiện bước" nếu tôi không muốn làm cho phương pháp này phụ thuộc vào thanh tiến trình? Ví dụ, tôi có thể viết

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void CaluculateAll(System.Windows.Forms.ProgressBar progressBar)
    {
        progressBar.Maximum = 100000;
        progressBar.Step = 1;

        for(int j = 0; j < 100000; j++)
        {
            double pow = Math.Pow(j, j); //Calculation
            progressBar.PerformStep();
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        CaluculateAll(progressBar1);
    }
}

nhưng tôi không muốn làm như vậy.


4
Truyền một đối tượng ủy nhiệm cho phương thức.
Hans Passant

Câu trả lời:


112

Tôi đề nghị bạn xem qua BackgroundWorker . Nếu bạn có một vòng lặp lớn trong WinForm, nó sẽ chặn và ứng dụng của bạn sẽ giống như bị treo.

Nhìn vào BackgroundWorker.ReportProgress()để biết cách báo cáo tiến trình trở lại chuỗi giao diện người dùng.

Ví dụ:

private void Calculate(int i)
{
    double pow = Math.Pow(i, i);
}

private void button1_Click(object sender, EventArgs e)
{
    progressBar1.Maximum = 100;
    progressBar1.Step = 1;
    progressBar1.Value = 0;
    backgroundWorker.RunWorkerAsync();
}

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    var backgroundWorker = sender as BackgroundWorker;
    for (int j = 0; j < 100000; j++)
    {
        Calculate(j);
        backgroundWorker.ReportProgress((j * 100) / 100000);
    }
}

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // TODO: do something with final calculation.
}

5
Ví dụ đẹp, nhưng có một lỗi nhỏ trong mã của bạn. Bạn cần đặt backgroundWorker.WorkerReportsProgress thành true. Kiểm tra chỉnh sửa của tôi
Mana

1
@mana Giả định là cái BackgroundWorkerđược thêm vào thông qua trình thiết kế và được định cấu hình ở đó. Nhưng có, nó sẽ cần được định cấu hình để WorkerReportsProgressđặt thành true.
Peter Ritchie

à, thật tệ, không biết rằng bạn có thể đặt nó trong nhà thiết kế
Mana

Nếu bạn tự hỏi điều gì đó khác, ví dụ của bạn chỉ tính đến 99 backgroundWorker.ReportProgress ((j * 100) / 100000); làm thế nào để nhận được 100% đếm
Mana

1
@mana Nếu bạn muốn hiển thị 100% tiến độ, làm điều đó trong RunWorkerCompletedxử lý sự kiện, nếu bạn DoWorkxử lý không làm điều đó ..
Peter Ritchie

77

Kể từ .NET 4.5, bạn có thể sử dụng kết hợp asyncchờ đợi với Tiến trình để gửi các bản cập nhật cho chuỗi giao diện người dùng:

private void Calculate(int i)
{
    double pow = Math.Pow(i, i);
}

public void DoWork(IProgress<int> progress)
{
    // This method is executed in the context of
    // another thread (different than the main UI thread),
    // so use only thread-safe code
    for (int j = 0; j < 100000; j++)
    {
        Calculate(j);

        // Use progress to notify UI thread that progress has
        // changed
        if (progress != null)
            progress.Report((j + 1) * 100 / 100000);
    }
}

private async void button1_Click(object sender, EventArgs e)
{
    progressBar1.Maximum = 100;
    progressBar1.Step = 1;

    var progress = new Progress<int>(v =>
    {
        // This lambda is executed in context of UI thread,
        // so it can safely update form controls
        progressBar1.Value = v;
    });

    // Run operation in another thread
    await Task.Run(() => DoWork(progress));

    // TODO: Do something after all calculations
}

Nhiệm vụ hiện là cách ưa thích để thực hiện những gì BackgroundWorkerhiện.

Các nhiệm vụ và Progressđược giải thích chi tiết hơn tại đây:


2
Đây phải là câu trả lời được chọn, IMO. Câu trả lời chính xác!
JohnOpincar

Hầu như hầu hết các câu trả lời đẹp nhất, ngoại trừ việc nó được sử dụng Task.Run thay vì một hàm async bình thường
KansaiRobot

@KansaiRobot Ý bạn là trái ngược với await DoWorkAsync(progress);? Điều này rất có chủ đích, vì điều đó sẽ không dẫn đến một luồng phụ chạy. Chỉ khi DoWorkAsyncnào gọi nó là của riêng nó, awaitví dụ như đợi thao tác I / O, button1_Clickchức năng này mới tiếp tục. Chuỗi giao diện người dùng chính bị chặn trong thời gian này. Nếu DoWorkAsynckhông thực sự không đồng bộ mà chỉ là nhiều câu lệnh đồng bộ, bạn không thu được gì.
Wolfzoon

System.Windows.Controls.ProgressBar không chứa trường "Bước". Nó nên được xóa khỏi ví dụ; đặc biệt là vì nó không được sử dụng.
Robert Tausig

2
@RobertTausig Đúng Steplà nó chỉ có sẵn trong thanh tiến trình của WinForms và không cần thiết ở đây, nhưng Nó đã có trong mã ví dụ của câu hỏi (các dạng win được gắn thẻ), vì vậy nó có thể ở lại.
quasoft

4

Xin chào, có một hướng dẫn hữu ích về ngọc trai Dot Net: http://www.dotnetperls.com/progressbar

Đồng ý với Peter, bạn cần sử dụng một số luồng hoặc chương trình sẽ bị treo, phần nào đánh bại mục đích.

Ví dụ sử dụng ProgressBar và BackgroundWorker: C #

using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            // Start the BackgroundWorker.
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100; i++)
            {
                // Wait 100 milliseconds.
                Thread.Sleep(100);
                // Report progress.
                backgroundWorker1.ReportProgress(i);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }
    }
} //closing here

1

Tasktồn tại, Đó là sử dụng phép thuật BackgroundWorker, Taskđơn giản hơn. ví dụ:

ProgressDialog.cs:

   public partial class ProgressDialog : Form
    {
        public System.Windows.Forms.ProgressBar Progressbar { get { return this.progressBar1; } }

        public ProgressDialog()
        {
            InitializeComponent();
        }

        public void RunAsync(Action action)
        {
            Task.Run(action);
        }
    }

Làm xong! Sau đó, bạn có thể sử dụng lại ProgressDialog ở bất cứ đâu:

var progressDialog = new ProgressDialog();
progressDialog.Progressbar.Value = 0;
progressDialog.Progressbar.Maximum = 100;

progressDialog.RunAsync(() =>
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(1000)
        this.progressDialog.Progressbar.BeginInvoke((MethodInvoker)(() => {
            this.progressDialog.Progressbar.Value += 1;
        }));
    }
});

progressDialog.ShowDialog();

Vui lòng không đăng các câu trả lời giống hệt nhau cho nhiều câu hỏi . Đăng một câu trả lời hay, sau đó bình chọn / gắn cờ để đóng các câu hỏi khác là trùng lặp. Nếu câu hỏi không trùng lặp, hãy điều chỉnh câu trả lời của bạn cho phù hợp với câu hỏi .
Martijn Pieters
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.