我有一个带有文本框txtOutput
的窗体。我有一些内容。我编写了一个属性来获取和设置txtOutput.Text
来自同一个线程和这样的线程:
public string OutputString
{
get
{
string text = string.Empty;
if (txtOutput.InvokeRequired)
{
txtOutput.BeginInvoke(new MethodInvoker(delegate
{
text = txtOutput.Text;
}));
}
else
{
text = txtOutput.Text;
}
return text;
}
set
{
if (txtOutput.InvokeRequired)
{
txtOutput.BeginInvoke(new MethodInvoker(delegate
{
txtOutput.Text = value;
}));
}
else
{
txtOutput.Text = value;
}
}
}
如果我从同一个线程设置/获取属性,则在调用PrintMessage()
之类的下面的函数时,行为就像预期的一样。
public void PrintMessage()
{
MessageBox.Show(OutputString);
}
但是当我这样打电话时new Thread(PrintMessage).Start()
。 get
不检索文本框中的值(即MessageBox
显示空字符串)。当我通过在线上保留断点来做同样的事情时:
txtOutput.BeginInvoke(new MethodInvoker(delegate
{
text = txtOutput.Text;
}));
在调试时,会检索该值(即MessageBox
显示txtOutput
内容)
我应该在某个地方sleep
吗?我在哪里弄错了?
答案 0 :(得分:2)
问题是,在UI线程可以处理您使用调度程序发出的请求之前,您正在使用对文本变量的引用调用MessageBox.Show()。我会避免使用Thread.Sleep(),因为你可能会得到一些讨厌的副作用。理想情况下,您应该重新考虑代码以摆脱属性,这是同步的,有利于更异步的解决方案。类似于下面代码的东西应该会给你你想要的结果:
public void PrintMessage(Action<string> displayAction)
{
string text = string.Empty;
if (txtOutput.InvokeRequired)
{
txtOutput.BeginInvoke(new MethodInvoker(delegate
{
displayAction.Invoke(txtOutput.Text);
}));
}
else
{
displayAction.Invoke(txtOutput.Text);
}
}
并调用它:
// Get the text asynchronously
PrintMessage(s => MessageBox.Show(s));
答案 1 :(得分:0)
您可以将任务与TaskScheduler form UI thread一起使用。将调度程序表单UI线程传递给任务工厂时,此任务在UI线程中执行,并将结果返回到创建此任务的线程。
namespace WpfTest {
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
public partial class MainWindow : Window {
private readonly TaskScheduler _taskScheduler;
public MainWindow() {
InitializeComponent();
_taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
button.Click += ButtonOnClick;
}
public string Text {
get {
if (text.CheckAccess()) {
return text.Text;
}
return Task.Factory.StartNew(
() => text.Text, CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
}
set {
if (text.CheckAccess()) {
text.Text = value;
} else {
Task.Factory.StartNew(
() => { text.Text = value; }, CancellationToken.None, TaskCreationOptions.None, _taskScheduler);
}
}
}
private void ButtonOnClick(object sender, RoutedEventArgs routedEventArgs) {
Text += "Test1";
new Thread(() => { Text += "Test2"; }).Start();
}
}
}