2

我有一个带有文本框的 windows 窗体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在某个地方吗?我在哪里犯错?

4

2 回答 2

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));
于 2012-05-27T02:56:58.060 回答
0

您可以将 Tasks 与TaskScheduler 表单 UI 线程一起使用。当您将调度程序表单 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();
    }
}
}
于 2012-05-27T09:14:04.057 回答