新线程打开新窗口,如何在新窗口中更新文本框?

时间:2011-11-08 07:05:43

标签: c# wpf multithreading

我最近发了一篇关于此事的帖子,但我认为我的最后一个问题写得不好而且我确实得到了答案,但我想知道是否有一个更简单的解决方案,因为我发现最后一个令人困惑。这次我试着尽可能清楚地写出来。

我有一个WPF应用程序的代码,这里是代码

MainWindow.xaml.cs的代码

public MainWindow()
{
  InitializeComponent();
}

private void button1_Click(object sender, RoutedEventArgs e)
{
  Thread[] threads = new Thread[3];

  for (int i = 0; i < 3; i++)
  {
    int index = i;
    threads[i] = new Thread(new ThreadStart(test));
    threads[i].SetApartmentState(ApartmentState.STA);
    threads[i].IsBackground = true;
    threads[i].Start();
  }
}

public void test()
{
  OutputWindow outputwindow = new OutputWindow();
  outputwindow.Show();
  System.Windows.Threading.Dispatcher.Run();

  outputwindow.textBox1.Text = "writing";
  //some more stuff done
  //some more stuff done
  //some more stuff done
  outputwindow.textBox1.Text = "\nmore writing";
  //some more stuff done
  //some more stuff done
  //some more stuff done
  outputwindow.textBox1.Text = "\nmore writing";
}

如何在执行test()时使textBox1.Text真正更新?

问候!

修改

感谢您的回答,但我还是无法使其成功。

这是一个网络应用程序,所以我认为文本框比数据绑定更合适,因为我想在整个程序中打印超时,ping和更多信息,以确保一切按计划进行。

我尝试了一些你的答案,但我无法让它发挥作用。这是我尝试的最后一个例子,它不起作用

public MainWindow()
{
  InitializeComponent();
}

private void button1_Click(object sender, RoutedEventArgs e)
{
  Thread[] threads = new Thread[3];

  for (int i = 0; i < 3; i++)
  {
    int index = i;
    threads[i] = new Thread(new ThreadStart(test));
    threads[i].SetApartmentState(ApartmentState.STA);
    threads[i].IsBackground = true;
    threads[i].Start();
  }
}

public void test()
{
  OutputWindow outputwindow = new OutputWindow();
  outputwindow.Show();
  System.Windows.Threading.Dispatcher.Run();

  outputwindow.textBox1.Text = "writing";
  //some more stuff done
  //some more stuff done
  //some more stuff done
  outputwindow.textBox1.Text = "\nmore writing";
  //some more stuff done
  //some more stuff done
  //some more stuff done
  Action action = () => outputwindow.textBox1.Text = "\nmore writing";
  Dispatcher.Invoke(action);
}

4 个答案:

答案 0 :(得分:1)

我已经回答了你的上一个问题然后我看到了这个问题。我已经得出结论你想要另一个意见,所以你再问一次,我不是故意回答这个问题,但我发现你没有走得太远,所以我会再试一次。

您的代码示例的问题是您调用Dispatcher.Run()。问题是你真的必须调用Dispatcher.Run来保持你的窗口文本框保持活动和响应,但在Dispatcher.Run方法内部是无限循环。你被困在Dispatcher.Run代码行上,直到Dispatcher被关闭并且窗口关闭。只有这样,你的代码才会继续并执行那些outputWindow.textBox1.Text set语句,但现在为时已晚。解决问题的方法是将outputWindow与“工作代码”分开。

对代码进行最简单的修改就是将“工作代码”放在另一个线程中,这样就不会被Dispatcher.Run()阻止。以下是您的示例代码应该如何显示(因为您更喜欢在同一方法中创建outputWindow)。

    // all good here
    public MainWindow()
    {
        InitializeComponent();
    }

    // also all good here
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Thread[] threads = new Thread[3];

        for (int i = 0; i < 3; i++)
        {
            int index = i;
            threads[i] = new Thread(new ThreadStart(test));
            threads[i].SetApartmentState(ApartmentState.STA);
            threads[i].IsBackground = true;
            threads[i].Start();
        }
    }

    // here's the change
    public void test()
    {
        OutputWindow outputwindow = new OutputWindow();
        outputwindow.Show();

        // here you create another thread which will execute your work code (the one you had after the Dispatcher.run statement
        Thread workThread = new Thread(new ParameterizedThreadStart(workMethod));
        workThread.IsBackground = true;
        // start the work thread BUT transfer the reference to outputwindow
        workThread.Start(outputwindow); 

        System.Windows.Threading.Dispatcher.Run();

        // no more code here; it has been transferd to workMethod which runs in another thread
    }

    public void workMethod(object threadParam)
    {
        OutputWindow outputwindow = (OutputWindow)threadParam;

        // those are little ugly but you must go through dispatcher because you are now in a different thread than your outputwindow
        outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "writing"; }), System.Windows.Threading.DispatcherPriority.Normal);
        //some more stuff done
        //some more stuff done
        //some more stuff done
        outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "\nmore writing"; }), System.Windows.Threading.DispatcherPriority.Normal);
        //some more stuff done
        //some more stuff done
        //some more stuff done
        outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "\nmore writing"; }), System.Windows.Threading.DispatcherPriority.Normal);

        // and finally close the outputWindow
        outputwindow.Dispatcher.InvokeShutdown();
    }

答案 1 :(得分:0)

答案 2 :(得分:0)

问题是在当前方法退出之前不会处理更新。您需要抽取消息泵,以便在当前方法退出之前应用它们。有关如何执行此操作的示例,请参阅here

public static void DoEvents(this Application application)
{
    Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new VoidHandler(() => { }));
}

private delegate void VoidHandler();

那就是说,我认为你的设计很缺乏,并且可以通过使用更加标准的机制来大大改善,例如BackgroundWorker

答案 3 :(得分:0)

正如其他人在这里写的那样,如果要更改UI对象,则需要调用。但是你有没有想过可能会使用数据绑定?这是一种使用WPF实际绑定数据的好方法(在您的情况下是一个字符串)。

这里有一些教程:http://www.wpftutorial.net/DataBindingOverview.html

从长远来看,你甚至想看看MVVM模式,但它至少是开始使用数据绑定的开始。