从另一个类c#更新gui

时间:2011-03-04 11:51:12

标签: c# multithreading class

嘿,我是c#plz帮助的新手。 我正在编写一个程序来对文件中的数据进行排序,这是一个耗时的过程,所以我认为我应该在一个单独的线程中运行它,因为它有很多步骤,所以我为它创建了一个新的类。问题是我想在主GUI中显示进度,我知道我必须使用Invoke函数,但问题是表单控件变量无法访问此类。我该怎么办??????

示例代码:

public class Sorter
{
    private string _path;
    public Sorter(string path)
    {
        _path = path;
    }

    public void StartSort()
    {
        try
        {
                 processFiles(_path, "h4x0r"); // Just kidding
        }
        catch (Exception e)
        {
            MessageBox.Show("Error: " + e.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void processFiles(string Dir, string[] key)
    {
        /* sorting program */

    }

,用作

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

        private void browseBtn_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                textBox1.Text = folderBrowserDialog1.SelectedPath;
        }

        private void startBtn_Click(object sender, EventArgs e)
        {
            if (startBtn.Text == "Start Sorting")
            {
   Thread worker = new Thread(new ThreadStart(delegate() {
                sort = new Sorter(textBox1.Text);
                sort.StartSort(); })); 
                worker.start();
            }
            else
                MessageBox.Show("Cancel");//TODO: add cancelling code here
        }
    }

plz help ..

4 个答案:

答案 0 :(得分:4)

向正在执行多线程工作的类添加一个事件,该事件会在进度发生变化时触发。让您的表单订阅此活动并更新进度条。

注意ProgressEventArgs是一个继承EventArgs的小类,并且有一个Integer用于进度。

// delegate to update progress
public delegate void ProgressChangedEventHandler(Object sender, ProgressEventArgs e);

// Event added to your worker class.
public event ProgressChangedEventHandler ProgressUpdateEvent

// Method to raise the event
public void UpdateProgress(object sender, ProgressEventArgs e)
{
   ProgressChangedEventHandler handler;
   lock (progressUpdateEventLock)
   {
      handler = progressUpdateEvent;
   }

   if (handler != null)
      handler(sender, e);
}

答案 1 :(得分:2)

我建议您阅读BackgroundWorker课程。这正是您尝试解决的问题,并且比自己手动线程化更容易。

简要示例

    public Form1()
    {
        InitializeComponent();

        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.WorkerSupportsCancellation = true;
        backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
    }

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

    private void btnStart_Click(object sender, EventArgs e)
    {
        if (!backgroundWorker.IsBusy)
            backgroundWorker.RunWorkerAsync();
    }

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 1; i < 101; ++i)
        {
            if (backgroundWorker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                //Sort Logic is in here.
                Thread.Sleep(250);
                backgroundWorker.ReportProgress(i);
            }
        }
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        if (backgroundWorker.IsBusy && backgroundWorker.WorkerSupportsCancellation)
            backgroundWorker.CancelAsync();
    }

答案 2 :(得分:1)

你可以这样做:

public delegate void StatusReporter(double progressPercentage);

public class MainClass
{

    public void MainMethod()
    {
        Worker worker = new Worker(ReportProgress);

        ThreadStart start = worker.DoWork;
        Thread workThread = new Thread(start);

        workThread.Start();

    }

    private void ReportProgress(double progressPercentage)
    {
        //Report here!!!
    }
}


public class Worker
{
    private readonly StatusReporter _reportProgress;

    public Worker(StatusReporter reportProgress)
    {
        _reportProgress = reportProgress;
    }

    public void DoWork()
    {
        for (int i = 0; i < 100; i++ )
        {
            // WORK, WORK, WORK
            _reportProgress(i);
        }
    }
}

答案 3 :(得分:1)

有一些选项可用于解决此类问题。在任何情况下,您都必须摆弄Invoke才能更新UI。

可以 ......

  • ...添加一个事件,触发你的UI可以监听的新类,并调用适用的 - 你仍然需要将数据传递给你的工人类(通过构造函数,属性,方法调用等) )
  • ...将方法保留为表单上的方法,并将其用于启动新线程(毕竟,新线程不必在另一个类中启动)
  • ...将控件上的访问修饰符更改为(例如)internal,以便同一程序集中的任何类都可以Invoke更改控件,或从中读取。
  • ...使您的worker类成为其需要访问的表单的子级 - 然后它可以看到其父级的private,只要它传递了对该实例的引用。