如何同时操作UI线程上的两个不同控件



如何在保持应用程序的UI线程响应的同时连续操作标签的文本?我正在Visual Studio 2010中创建一个windows窗体应用程序。此外,如果你能在vb.net或c#中发布你的例子,我将非常感谢。

您必须使用BeginInvoke从另一个线程执行此操作,这将更改UI线程上UI控件的状态。

如果您使用的是.NET Framework 4,则可以使用异步任务。

这段代码将在一个新线程上启动一些东西:

var myBackgroundTask = Task.Factory.StartNew(() => MyBackGroundMethod());

你说你想更新用户界面(UI),因为UI上的控件是在主线程上创建的,所以从另一个线程直接访问它们会引发跨线程异常。然而,您可以从后台线程启动一个新的Task,并告诉它在主线程上运行,如下所示:

private void MyMethod()
{
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var myBackgroundTask = Task.Factory.StartNew(() => MyBackGroundMethod(uiTaskScheduler));
}
private void MyBackGroundMethod(TaskScheduler uiTaskScheduler)
{
Task.Factory.StartNew(
() => { TextBox1.Text = "Hello from separate thread"; },
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
}

如果你想在任务完成时做点什么,你可以使用这样的延续:

var finishedTask = myBackgroundTask.ContinueWith(
x => Label1.Text = "Task 1 completed sucessfully",
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
uiTaskScheduler);

请注意TaskContinuationOptions-您可以指定仅在没有错误的情况下继续,或者仅在取消、未取消等情况下继续(更多)。

如果您想取消长时间运行的进程,可以使用CancellationToken。

下面是一个完整的示例,它使用后台任务,更新进度,允许取消,并在完成、出错、取消或完成时报告。

它是一个包含2个按钮、1个标签和1个进度条的表单。分别命名为StartButton、StopButton、ProgressBar、InformationLabel。

public partial class MainForm : Form
{
CancellationTokenSource cancelTokenSource;
public MainForm()
{
InitializeComponent();
}
private void StartButton_Click(object sender, EventArgs e)
{
InformationLabel.Text = "Task 1 running...";
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
cancelTokenSource = new CancellationTokenSource();
this.StopButton.Enabled = true;
this.StartButton.Enabled = false;
var someWork = Task.Factory.StartNew(
() => { DoWork(uiTaskScheduler); },
cancelTokenSource.Token,
TaskCreationOptions.None,
TaskScheduler.Default);
var someWorkFinished = someWork.ContinueWith(
x => InformationLabel.Text = "Task 1 completed sucessfully",
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
uiTaskScheduler);
var someWorkErrored = someWork.ContinueWith(
x => InformationLabel.Text = "Task 1 Errored",
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
uiTaskScheduler);
var someWorkCancelled = someWork.ContinueWith(
x => InformationLabel.Text = "Task 1 Cancelled",
CancellationToken.None,
TaskContinuationOptions.OnlyOnCanceled,
uiTaskScheduler);
var resultTasks = new List<Task>();
resultTasks.Add(someWorkFinished);
resultTasks.Add(someWorkErrored);
resultTasks.Add(someWorkCancelled);
Task.Factory.ContinueWhenAny(
resultTasks.ToArray(),
x => this.Reset(),
CancellationToken.None,
TaskContinuationOptions.None,
uiTaskScheduler);
}
private void Reset()
{
this.StopButton.Enabled = false;
this.StartButton.Enabled = true;
}
private void DoWork(TaskScheduler uiTaskScheduler)
{
for (int i = 0; i < 10; i++)
{
cancelTokenSource.Token.ThrowIfCancellationRequested();
Thread.Sleep(500);
i++;
// Update progress
Task.Factory.StartNew(
() => { UpdateProgress(ProgressBar, i * 10); }, 
CancellationToken.None, 
TaskCreationOptions.None, 
uiTaskScheduler);
}
}
private void UpdateProgress(ProgressBar progressBar, int i)
{
progressBar.Value = i;
}
private void StopButton_Click(object sender, EventArgs e)
{
cancelTokenSource.Cancel();
}
}

最新更新