简体   繁体   English

在对象中使用BackgroundWorker并更新UI

[英]Using BackgroundWorker in an object and updating UI

Edit: Got somewhere, I hope 编辑:我希望能到达某个地方

Here is what I've got, but I'm not so sure about how I delegate my bcLoad.ReportProgress(i) to the object created (ie how to make the delegate so it can be passed). 这是我所拥有的,但我不太确定如何将我的bcLoad.ReportProgress(i)委托给创建的对象(即如何使委托以便可以传递)。 I've created the object events which work sort of(I can call my object method and I can see a change triggered when reading in lines). 我已经创建了有效的对象事件(我可以调用我的对象方法,我可以看到在读取行时触发的更改)。 I know when objectChanged is working(written to console). 我知道objectChanged何时工作(写入控制台)。 However, bcLoad_RunWorkerCompleted doesn't seem to work, the code in the if statement is never executed so I'm going wrong somewhere. 但是,bcLoad_RunWorkerCompleted似乎不起作用,if语句中的代码永远不会执行,所以我在某处出错了。 The file loads though. 但是文件加载了。

Could someone please possible set out how to create the delegate, then which section to use the pass delegate in(I assume in the object) and why bcLoad_RunWorkerComplete is null. 有人可以请说明如何创建委托,然后使用传递委托的哪个部分(我假设在对象中)以及为什么bcLoad_RunWorkerComplete为null。 This is really the first time I've used events, delegates and backgroundworkers in c# 这是我第一次在c#中使用事件,代表和后台工作者

/*
The object which does file operations
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
using System.Data;

namespace aodProductionViewer
{
    public class fileOperationsSpecial
    {
        public event EventHandler Changed;

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
            {
                Changed(this, e);
            }
        }

        public fileOperationsSpecial() 
        {        }

        /// <summary>
        /// Count the number of lines in the file specified.
        /// </summary>
        /// <param name="f">The filename to count lines in.</param>
        /// <returns>The number of lines in the file.</returns>
        static long CountLinesInFile(string f)
        {
            long count = 0;
            try
            {
                using (StreamReader r = new StreamReader(f))
                {
                    string line;
                    while ((line = r.ReadLine()) != null)
                    {
                        count++;
                    }
                }
            }
            catch (Exception err)
            {
                string strTemp = "Error get number of lines for save game file. \n" +
                                err.ToString();
                errorDialog errDiag = new errorDialog("save game line count",
                                        strTemp, true);
            }
            return count;
        }

        /// <summary>
        /// Use this to readin in a file
        /// </summary>
        /// <param name="strPath">Path of file to read in</param>
        /// <returns>a string array of the file</returns>
        public string[] readFile(string strPath)
        {
            long lng_LineCount = CountLinesInFile(strPath);
            string[] strReadIn = new string[lng_LineCount];
            try
            {
                long lngCount = 0;
                using (StreamReader reader = new StreamReader(strPath))
                {
                    String line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        strReadIn[lngCount] = line;
                        lngCount++;
                        OnChanged(EventArgs.Empty);
                    }
                }

            }
            catch (Exception err)
            { //            
            }

            return strReadIn;
        }
   }
}

/*
Event Listner
*/
using System;
using System.Collections.Generic;
using System.Text;

namespace aodProductionViewer
{
    class EventListener
    {
        private fileOperationsSpecial FPS;

        public EventListener(fileOperationsSpecial _fps)
        {
            FPS = _fps;
            FPS.Changed += new EventHandler(objectChanged);
        }

        private void objectChanged(object sender, EventArgs e)
        {            //changed has occured
        }

        public void Detach()
        {
            FPS.Changed -= new EventHandler(objectChanged);
            FPS = null;
        }
    }
}

/*
The backgroundWorker code (Part of)
*/

    BackgroundWorker bcLoad = new BackgroundWorker();

    private void btt_load_save_game_Click(object sender, EventArgs e)
    {
        //Do some file dialog stuff
        string strPath = null;
        bcLoad.RunWorkerAsync(strPath);
    }

    void bcLoad_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        tb_ProgressBar.Value = e.ProgressPercentage;
    }

    void bcLoad_DoWork(object sender, DoWorkEventArgs e)
    {
        string strPath = e.Argument as string;
        fileOperationsSpecial FPS = new fileOperationsSpecial();
        EventListener listener = new EventListener(FPS);
        string strArray = FPS.readFile(strPath);
        listener.Detach();
    }

    void bcLoad_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Result != null)
        {
            //Everything done
            tb_ProgressBar.Visible = false;
        }
    }

I've got the gist of using a BackgroundWorker to do some work and update the UI for progress and completion, I've got something simple at the moment As you can see, I pass a string (which is a path) I would then read a file in and update a progress, currently I'm just sleeping the thread and setting the progress for the sake of a demo. 我有使用BackgroundWorker做一些工作并更新UI进度和完成的要点,我现在有一些简单的东西你可以看到,我传递一个字符串(这是一条路径)然后我会读取文件并更新进度,目前我只是在休眠线程并为演示设置进度。 I also intend to return an object (string array) but I've yet to get around to that. 我还打算返回一个对象(字符串数组),但我还没有解决这个问题。

Now my question is, how can I do all this in an object created by my form and still update my UI? 现在我的问题是,如何在我的表单创建的对象中完成所有这些操作并仍然更新我的UI? I have an object which currently does operations on files (ie read file, write, get info). 我有一个目前对文件进行操作的对象(即读取文件,写入,获取信息)。

Currently as my understanding stands the demo below goes Form > BackgroundWorker > Update form for progress. 目前,根据我的理解,下面的演示为Form> BackgroundWorker> Update表单以获取进度。

I would like it to go 我想要它去

Form > Create Object > BackgroundWorker > Update form for progress > return string array 表单>创建对象> BackgroundWorker>进度更新表单>返回字符串数组

I've looked about and not made heads or tails of any of the examples, so I thought I'd ask people who would know. 我看过并且没有做过任何一个例子的正面或反面,所以我想我会问那些会知道的人。 Is this even possible? 这甚至可能吗? What I'm trying to aim for is to remove any file processing out of my form so it becomes easier to manage and maintain. 我想要的目标是从我的表单中删除任何文件处理,以便更容易管理和维护。

Full code example on how to do this would be fantastic! 关于如何做到这一点的完整代码示例将是太棒了!

Here is what I understand so far (remember just for an examples sake, this won't compile) 这是我到目前为止所理解的(记住只是为了一个例子,这不会编译)

BackgroundWorker bcLoad = new BackgroundWorker();

    public frm_ProductionViewer()
    {
        InitializeComponent();
        load_settings();
        bcLoad.DoWork += new DoWorkEventHandler(bcLoad_DoWork);
        bcLoad.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bcLoad_RunWorkerCompleted);

        bcLoad.WorkerReportsProgress = true;
        bcLoad.ProgressChanged += new ProgressChangedEventHandler(bcLoad_ProgressChanged);

        bcLoad.WorkerSupportsCancellation = true;
    }

private void btt_load_save_game_Click(object sender, EventArgs e)
    {

        ts_label_GameLoaded.Text = "Loading";
        bcLoad.RunWorkerAsync(strPath);
    }

void bcLoad_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        tb_ProgressBar.Value = e.ProgressPercentage;
    }

    void bcLoad_DoWork(object sender, DoWorkEventArgs e)
    {
            string strPath = e.Argument as string;
            //load file
            //Update progress
            bcLoad.ReportProgress(80);
            Thread.Sleep(300 * 5);
            bcLoad.ReportProgress(100);
    }

    void bcLoad_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Result != null)
        {
            textBox1.Text = "done";
        }
        tb_ProgressBar.Visible = false; ;
        ts_label_GameLoaded.Text = "Loaded";
    }

Your object (that works with files) should preferably not depend on either the GUI or the Bgw. 您的对象(使用文件)最好不要依赖于GUI或Bgw。

So keep your Bgw with (on) your Form and call into the object form DoWork. 因此,请将您的Bgw与您的表格保持在一起,并调用DoWork的对象形式。

To report feedback, your Object needs an event (or the main methods taking a delegate parameter). 要报告反馈,您的对象需要一个事件(或者采用委托参数的主要方法)。

In the handler of that event (called on the worker thread), call the bcLoad.ReportProgress(percent). 在该事件的处理程序中(在工作线程上调用),调用bcLoad.ReportProgress(百分比)。


Reaction to the Edit: 对编辑的反应:

  • you need an EventArgs type with room for the Percentage, like EventHandler<ProgressEventArgs> , you probably have to write ProgressEventArgs. 你需要一个具有Percentage空间的EventArgs类型,比如EventHandler<ProgressEventArgs> ,你可能需要编写ProgressEventArgs。

  • I don't think you want a separate EventListener class. 我不认为你想要一个单独的EventListener类。 This is the Forms job. 这是Forms工作。 That gets you: 这会让你:

class Form ...
{
    private void objectChanged(object sender, ProgressEventArgs e)
    {   //changed has occured
        // trigger the Bgw event
        // or use Form.Invoke here to set the progress directly
        bcLoad.ReportProgress(e.Percentage);          
    }

}

So a 'change' now filters through 2 eventhandlers. 因此,“更改”现在可以过滤2个事件处理程序。 1 to break dependency from the FPS, 1 to sync to the Form. 1打破FPS的依赖,1同步到Form。

If you want a class (other than the form) to encapsulate the background worker, one approach is to add an event to this class and subscribe to this event within the form: 如果您希望类(表单除外)封装后台工作程序,则一种方法是向此类添加事件并在表单中订阅此事件:

  • Class X encapsulates background worker and publishes a "Done" event X类封装后台工作程序并发布“完成”事件
  • Form creates instance of Class X, and event handler for "Done" event 表单创建类X的实例,以及“完成”事件的事件处理程序
  • Form calls Class X's instance "Do async work" method and moves on 表单调用Class X的实例“Do async work”方法并继续
  • Class X's instance notifies form when async work completes via Done event, passing the string or whatever state the form needs 当异步工作通过Done事件完成,传递字符串或表单所需的任何状态时,类X的实例通知表单

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM