c# - 在对象中使用 BackgroundWorker 并更新 UI

标签 c# winforms multithreading user-interface backgroundworker

编辑:我希望到了某个地方

这是我得到的,但我不太确定如何将我的 bcLoad.ReportProgress(i) 委托(delegate)给创建的对象(即如何创建委托(delegate)以便它可以传递)。我已经创建了某种工作的对象事件(我可以调用我的对象方法,并且可以看到在读取行时触发的更改)。我知道 objectChanged 何时工作(写入控制台)。但是,bcLoad_RunWorkerCompleted 似乎不起作用,if 语句中的代码永远不会执行,所以我在某处出错了。虽然文件加载。

有人可以说明如何创建委托(delegate),然后在哪个部分使用传递委托(delegate)(我假设在对象中)以及为什么 bcLoad_RunWorkerComplete 为空。 这真的是我第一次在 c# 中使用事件、委托(delegate)和后台 worker

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

我已经掌握了使用 BackgroundWorker 来完成一些工作并更新 UI 以取得进展和完成的要点,目前我有一些简单的东西正如你所看到的,我传递了一个字符串(这是一个路径)然后我会读入一个文件并更新进度,目前我只是为了演示而休眠线程并设置进度。我还打算返回一个对象(字符串数组),但我还没有解决这个问题。

现在我的问题是,我怎样才能在由我的表单创建的对象中完成所有这些工作并仍然更新我的 UI?我有一个当前对文件进行操作的对象(即读取文件、写入、获取信息)。

目前我的理解是下面的演示 Form > BackgroundWorker > 为进度更新表单。

我愿意去

表单 > 创建对象 > BackgroundWorker > 为进度更新表单 > 返回字符串数组

我已经查看了所有示例,但没有搞清楚,所以我想我应该问问知道的人。这可能吗?我的目标是从我的表单中删除任何文件处理,以便更易于管理和维护。

有关如何执行此操作的完整代码示例非常棒!

这是我到目前为止的理解(记住只是为了举例,这不会编译)

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

最佳答案

您的对象(与文件一起使用)最好不依赖于 GUI 或 Bgw。

因此,让您的 Bgw 与您的表单保持一致,并调用对象表单 DoWork。

要报告反馈,您的对象需要一个事件(或采用委托(delegate)参数的主要方法)。

在该事件的处理程序中(在工作线程上调用),调用 bcLoad.ReportProgress(percent)。


对编辑的 react :

  • 您需要一个带有百分比空间的 EventArgs 类型,例如 EventHandler<ProgressEventArgs> ,您可能必须编写 ProgressEventArgs。

  • 我认为您不需要单独的 EventListener 类。这是表单作业。这让你:

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

}

因此,“更改”现在会通过 2 个事件处理程序进行过滤。 1 打破 FPS 的依赖性,1 同步到表单。

关于c# - 在对象中使用 BackgroundWorker 并更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3373564/

相关文章:

c# - 如何在 Azure 应用服务中将 X509Certificate2 与 WCF 结合使用

c# - 如何删除在 DataDirectory 中创建的 .sdf 文件

c# - 如何使用 C# 从网页创建 xps 文件?

c# - FileSystemWatcher 不响应虚拟机及其主机共享文件夹中的文件事件

c# - 关闭对话框表单时刷新 winform ComboBox 数据源

.net - 如何将可为空的 bool 值绑定(bind)到复选框?

multithreading - 使用 PyCharm 进行多线程调试

c# - System.Threading.Timer 的可扩展性如何?

c# - 使用 ComboBox 项目隐藏 ID?

ios - NSKeyedArchiver 调用encodeObject 时如何捕获 EXC_BAD_ACCESS