繁体   English   中英

c# - 在执行期间将信息传递给BackgroundWorker

[英]c# - Pass information to BackgroundWorker From UI during execution

我有一个使用后台工作线程的ac#应用程序,并且非常成功地从正在运行的线程更新UI。 该应用程序涉及网络上的最短路径路由,并在后台工作进行时在UI上显示网络和最短路径。 我希望允许用户在应用程序运行时通过使用滑块来降低显示速度。

我发现这是一个建议,但它在vb.net中,我不清楚如何让它在c#中工作。

BackgroundWorker如何在UI线程运行时从UI线程获取值?

我可以将滑块的值传递给backgroundworker,如下所示:

//启动异步操作。 delay = this.trackBar1.Value; backgroundWorker1.RunWorkerAsync(延迟);

并在backgroundworker线程中使用它,但它只使用初始发送的值。 当我在UI上移动滑块时,我不清楚如何从backgroundworker中获取值。

我以前使用过多个线程和委托,但如果可以使用后台工作程序,我更喜欢它的简单性。

2012/5/10

感谢大家的回复。 我仍然遇到问题,很可能是因为我的结构是如何形成的。 网络路由的重载计算在TransportationDelayModel类中完成。 BackgroundWorker_DoWork创建此类的实例,然后将其踢出。 延迟在TransportationDelayModel中处理。

代码框架如下:

在UI中:

 private void runToolStripMenuItem1_Click(object sender, EventArgs e)
    {
        if (sqliteFileName.Equals("Not Set"))
        {
            MessageBox.Show("Database Name Not Set");
            this.chooseDatabaseToolStripMenuItem_Click(sender, e);

        }

        if (backgroundWorker1.IsBusy != true)
        {
            // Start the asynchronous operation.
            delay = this.trackBar1.Value;
            // pass the initial value of delay
            backgroundWorker1.RunWorkerAsync(delay);
            // preclude multiple runs
            runToolStripMenuItem1.Enabled = false;
            toolStripButton2.Enabled = false;
        }

    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        if (!backgroundWorkerLaunched)
        {
            // instantiate the object that does all the heavy work
            TransportationDelayModel TDM = new TransportationDelayModel(worker, e);               
            // kick it off
            TDM.Run(sqliteFileName, worker, e);
            backgroundWorkerLaunched = true;
        }


    }

TransportationDelayModel构造函数是:

 public TransportationDelayModel(BackgroundWorker worker, DoWorkEventArgs e)
    {
        listCentroids = new List<RoadNode>();
        listCentroidIDs = new List<int>();
        listNodes = new List<RoadNode>();
        listNodeIDs = new List<int>();
        listRoadLink = new List<RoadLink>();
        roadGraph = new AdjacencyGraph<int, RoadLink>(true); // note parallel edges allowed
        tdmWorker = worker;
        tdmEvent = e;
        networkForm = new NetworkForm();
    }

所以我有tdmWorker,它允许我将信息传递回UI。

在TransportationDelayModel的内部计算中,我睡了一段延迟时间

  if (delay2 > 0)
                                    {
                                        tdmWorker.ReportProgress(-12, zzz);
                                        System.Threading.Thread.Sleep(delay2);
                                    }

所以问题似乎是如何将更新的滑块值从UI传递回后台工作程序中执行的对象。 我已经尝试了许多组合,有点颠簸,无济于事,或者没有任何反应或者我得到一条消息,不允许访问其他线程上发生的事情。 我意识到,如果我在DoWork事件处理程序中完成所有工作,那么我应该能够像你建议的那样做事,但是要做到这一点太复杂了。

再次感谢您的建议和帮助。

2012年6月2日

我已经通过两种方法解决了这个问题,但我有一些问题。 根据我对R. Harvey的评论,我构建了一个简单的应用程序。 它由带有运行按钮,滑块和富文本框的表单组成。 运行按钮启动后台工作线程,该线程实例化“Model”类的对象,该对象执行所有工作(我的TransportationModel的简化代理)。 Model类简单地在文本框中写入100行,将每行中的点数增加1,每行之间的延迟基于滑块的设置,以及行尾的滑块值,类似于这个:

.................... 58

..................... 58

...................... 58

....................... 51

........................ 44

......................... 44

本练习的目的是能够在“模型”运行时移动窗体上的滑块,并获得更改的延迟(如上所述)。

我的第一个解决方案是创建一个Globals类,以保存滑块的值:

class Globals
{
    public static int globalDelay;
}

然后,在表单中,每当滚动轨迹栏时我都会更新此值:

private void trackBar1_Scroll(object sender, EventArgs e)
    {
        Globals.globalDelay = this.trackBar1.Value;
    } 

在模型中,我只是拿起全局的价值:

 public void Run(BackgroundWorker worker, DoWorkEventArgs e)
 {

     for (int i = 1; i < 100; i++)
     {
         delay = Globals.globalDelay; // revise delay based on static global set on UI
         System.Threading.Thread.Sleep(delay);
         worker.ReportProgress(i);
         string reportString = ".";
         for (int k = 0; k < i; k++)
         {
             reportString += ".";
         }
         reportString += delay.ToString();
         worker.ReportProgress(-1, reportString);

     }
 }
}

这很好用。 我的问题是:这种方法有什么缺点,实现起来非常简单,非常普遍。

第二种方法基于R. Harvey的建议,利用代表并调用。

我为代表创建了一个类:

 public class MyDelegates
{
    public delegate int DelegateCheckTrackBarValue(); // create the delegate here
}

在表单中,我创建:

  public int CheckTrackBarValue()
    {
        return this.trackBar1.Value;

    } 

并且Model类现在有一个成员m_CheckTrackBarValue

 public class Model 
{

#region Members

Form1 passedForm;
public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue=null;      
#endregion Members

#region Constructor
public Model(BackgroundWorker worker, DoWorkEventArgs e, Form1 form)
{
    passedForm = form;
}

当运行按钮启动后台线程时,将传递调用表单

private void button1_Click(object sender,EventArgs e){if(backgroundWorker1.IsBusy!= true){

            backgroundWorker1.RunWorkerAsync();

        }
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {

        BackgroundWorker worker = sender as BackgroundWorker;

        if (!backgroundWorkerLaunched)
        {
            // instantiate the object that does all the heavy work
            Model myModel= new Model(worker, e, this);

            Model.m_CheckTrackBarValue = new MyDelegates.DelegateCheckTrackBarValue(this.CheckTrackBarValue);

            // kick it off
            myModel.Run(worker, e);
            backgroundWorkerLaunched = true;
        }
    }

最后,在Model中,在传递的表单上调用Invoke方法以获取轨迹栏的值。 public void Run(BackgroundWorker worker,DoWorkEventArgs e){

     for (int i = 1; i < 100; i++)
     {
         int delay = (int)passedForm.Invoke(m_CheckTrackBarValue,null); // invoke the method, note need the cast here
         System.Threading.Thread.Sleep(delay);
         worker.ReportProgress(i);
         string reportString = ".";
         for (int k = 0; k < i; k++)
         {
             reportString += ".";
         }
         reportString += delay.ToString();
         worker.ReportProgress(-1, reportString);

     }
 }

这也有效。 我一直收到错误,直到我将成员变量设为静态,例如public static MyDelegates.DelegateCheckTrackBarValue m_CheckTrackBarValue = null;

我对这个解决方案的疑问:对于以前的版本,此解决方案是否有优势? 我的实施方式是否使事情过于复杂? 为什么m_CheckTrackBarValue需要是静态的。

我为这个编辑的长度道歉,但我认为问题和解决方案可能是其他人感兴趣的。

您必须将TrackBar对象传递给BackgroundWorker ,而不是delay delay不会改变。

为了简化需要Invoke()您可以使用一个辅助方法,比如这一个

Async.UI(delegate { textBox1.Text = "This is way easier!"; }, textBox1, true);

我将假设您已熟悉跨线程调用以更新UI。 因此,解决方案非常简单:在您的工作线程中,在每次迭代之后,调用UI以获取滑块的拇指位置。

要使用后台工作程序,请向DoWork属性添加方法,如下所示:

this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

DoWork方法中,您需要检查设置更新延迟的变量。

这可以是包含的Form或UI控件上可用的整数字段,也可以是TrackBar本身。

暂无
暂无

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

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