开发者

updating gui from another class c#

hey i am new to c# plz help. i am writing a program that sorts data in a file and it is a time consuming process so i thought that i should run it in a separate thread and since it has alot of step so i made a new class for it. the problem is that i want to show the progress in the main GUI and i know for that i have to use Invoke function but the problem is that the form control variables are not accessible it this class. what should i do ??????

sample code:

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 */

    }

and it is used as

    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 = fo开发者_开发知识库lderBrowserDialog1.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..


Add an Event to your class that is doing the multi-threaded work, that triggers when the progress changes. Have your form subscribe to this event and update the progress bar.

Note ProgressEventArgs is a little class that inherits EventArgs and has an Integer for the progress.

// 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);
}


I would recommend you read up on the BackgroundWorker class. It is exactly for the problem you are trying to solve and makes things a lot easier than doing manual threading yourself.

Brief Example

    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();
    }


You could do something like this:

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);
        }
    }
}


There are a few option available to solve this sort of issue. In any case, you will have to fiddle with Invoke to get the UI to update.

You could...

  • ...add an event that fires on your new class which your UI can listen to, and Invoke as applicable - you'd still need to pass the data to your worker class (by constructor, properties, method call, etc)
  • ...keep the method as a method on your form, and pas that to start your new thread from (after all, a new thread doesn't have to be starting in a different class)
  • ...change the access modifiers on your controls to be (say) internal such that any class within the same assembly can Invoke changes to the controls, or read from them.
  • ...make your worker class a child of the form it needs to access - it can then see the privates of its parent, as long as it is passed a reference to the instance.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜