首先,我不是Window.Forms开发经验丰富的人。但是,对于控件,我发现在线程应用程序中使用InvokeRequired检查有点繁琐。我已经创建了一个静态方法,我认为它解决了我繁琐的InvokeRequired检查。只是想把它公开,看看它是否是一种糟糕的“模式”:
public static void UIInvoke(Control uiControl, Action action)
{
if (!uiControl.IsDisposed)
{
if (uiControl.InvokeRequired)
{
uiControl.BeginInvoke(action);
}
else
{
action();
}
}
}好的,所以我有一个文本框(名为StatusTextBox),我想在后台线程中设置一些文本。这方面的守则是:
ThreadUtilities.UIInvoke(this.StatusTextBox, delegate()
{
string text = this.StatusTextBox.Text;
this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});这和这一样吗?
this.StatusTextBox.BeginInvoke(delegate()
{
string text = this.StatusTextBox.Text;
this.StatusTextBox.Text = (text.Length > 10) ? String.Empty : text.PadRight(1, '.');
});谷歌( Google )在其最佳状态下,发现了 这篇文章 ,有人想出了同样的方法。我会继续我的“骚扰”。谢谢!
发布于 2010-07-15 10:43:53
我坚信,任何依赖于InvokeRequired的代码都遵循着糟糕的模式。
每个方法要么知道它在UI线程的上下文中运行,要么知道它不是。可以从任何线程运行的唯一方法应该是在同步类(如Semaphore )上运行(如果您正在编写同步类,请问问自己是否真的应该这样做)。
这是多线程设计的一个原则,它减少了bug并澄清了代码。
对于从后台线程更新UI的问题,我建议使用调度到UI的Task SynchronizationContext对象。如果Task类不可用(例如,针对4.0前的框架),则使用BackgroundWorker。
下面是一个支持向UI报告进度以及支持取消和错误情况的后台任务的示例:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
class Program
{
[STAThread]
static void Main()
{
// Set up the UI and run it.
var program = new Program
{
startButton = new Button
{
Text = "Start",
Height = 23, Width = 75,
Left = 12, Top = 12,
},
cancelButton = new Button
{
Text = "Cancel",
Enabled = false,
Height = 23, Width = 75,
Left = 93, Top = 12,
},
progressBar = new ProgressBar
{
Width = 156, Height = 23,
Left = 12, Top = 41,
},
};
var form = new Form
{
Controls =
{
program.startButton,
program.cancelButton,
program.progressBar
},
};
program.startButton.Click += program.startButton_Click;
program.cancelButton.Click += program.cancelButton_Click;
Application.Run(form);
}
public Button startButton;
public Button cancelButton;
public ProgressBar progressBar;
private CancellationTokenSource cancellationTokenSource;
private void startButton_Click(object sender, EventArgs e)
{
this.startButton.Enabled = false;
this.cancelButton.Enabled = true;
this.cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = this.cancellationTokenSource.Token;
var progressReporter = new ProgressReporter();
var task = Task.Factory.StartNew(() =>
{
for (int i = 0; i != 100; ++i)
{
// Check for cancellation
cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(30); // Do some work.
// Report progress of the work.
progressReporter.ReportProgress(() =>
{
// Note: code passed to "ReportProgress" may access UI elements.
this.progressBar.Value = i;
});
}
// Uncomment the next line to play with error handling.
//throw new InvalidOperationException("Oops...");
// The answer, at last!
return 42;
}, cancellationToken);
// ProgressReporter can be used to report successful completion,
// cancelation, or failure to the UI thread.
progressReporter.RegisterContinuation(task, () =>
{
// Update UI to reflect completion.
this.progressBar.Value = 100;
// Display results.
if (task.Exception != null)
MessageBox.Show("Background task error: " + task.Exception.ToString());
else if (task.IsCanceled)
MessageBox.Show("Background task cancelled");
else
MessageBox.Show("Background task result: " + task.Result);
// Reset UI.
this.progressBar.Value = 0;
this.startButton.Enabled = true;
this.cancelButton.Enabled = false;
});
}
private void cancelButton_Click(object sender, EventArgs e)
{
this.cancellationTokenSource.Cancel();
}
}这个示例代码使用了我为方便而定义的ProgressReporter (它清理了代码,IMO)。此类型为在我的博客上定义,如下所示:
/// <summary>
/// A class used by Tasks to report progress or completion updates back to the UI.
/// </summary>
public sealed class ProgressReporter
{
/// <summary>
/// The underlying scheduler for the UI's synchronization context.
/// </summary>
private readonly TaskScheduler scheduler;
/// <summary>
/// Initializes a new instance of the <see cref="ProgressReporter"/> class. This should be run on a UI thread.
/// </summary>
public ProgressReporter()
{
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
/// <summary>
/// Gets the task scheduler which executes tasks on the UI thread.
/// </summary>
public TaskScheduler Scheduler
{
get { return this.scheduler; }
}
/// <summary>
/// Reports the progress to the UI thread. This method should be called from the task. Note that the progress update is asynchronous with respect to the reporting Task. For a synchronous progress update, wait on the returned <see cref="Task"/>.
/// </summary>
/// <param name="action">The action to perform in the context of the UI thread. Note that this action is run asynchronously on the UI thread.</param>
/// <returns>The task queued to the UI thread.</returns>
public Task ReportProgressAsync(Action action)
{
return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, this.scheduler);
}
/// <summary>
/// Reports the progress to the UI thread, and waits for the UI thread to process the update before returning. This method should be called from the task.
/// </summary>
/// <param name="action">The action to perform in the context of the UI thread.</param>
public void ReportProgress(Action action)
{
this.ReportProgressAsync(action).Wait();
}
/// <summary>
/// Registers a UI thread handler for when the specified task finishes execution, whether it finishes with success, failiure, or cancellation.
/// </summary>
/// <param name="task">The task to monitor for completion.</param>
/// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param>
/// <returns>The continuation created to handle completion. This is normally ignored.</returns>
public Task RegisterContinuation(Task task, Action action)
{
return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task finishes execution, whether it finishes with success, failiure, or cancellation.
/// </summary>
/// <typeparam name="TResult">The type of the task result.</typeparam>
/// <param name="task">The task to monitor for completion.</param>
/// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param>
/// <returns>The continuation created to handle completion. This is normally ignored.</returns>
public Task RegisterContinuation<TResult>(Task<TResult> task, Action action)
{
return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task successfully finishes execution.
/// </summary>
/// <param name="task">The task to monitor for successful completion.</param>
/// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread.</param>
/// <returns>The continuation created to handle successful completion. This is normally ignored.</returns>
public Task RegisterSucceededHandler(Task task, Action action)
{
return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task successfully finishes execution and returns a result.
/// </summary>
/// <typeparam name="TResult">The type of the task result.</typeparam>
/// <param name="task">The task to monitor for successful completion.</param>
/// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread. The argument to the action is the return value of the task.</param>
/// <returns>The continuation created to handle successful completion. This is normally ignored.</returns>
public Task RegisterSucceededHandler<TResult>(Task<TResult> task, Action<TResult> action)
{
return task.ContinueWith(t => action(t.Result), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.Scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task becomes faulted.
/// </summary>
/// <param name="task">The task to monitor for faulting.</param>
/// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param>
/// <returns>The continuation created to handle faulting. This is normally ignored.</returns>
public Task RegisterFaultedHandler(Task task, Action<Exception> action)
{
return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task becomes faulted.
/// </summary>
/// <typeparam name="TResult">The type of the task result.</typeparam>
/// <param name="task">The task to monitor for faulting.</param>
/// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param>
/// <returns>The continuation created to handle faulting. This is normally ignored.</returns>
public Task RegisterFaultedHandler<TResult>(Task<TResult> task, Action<Exception> action)
{
return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task is cancelled.
/// </summary>
/// <param name="task">The task to monitor for cancellation.</param>
/// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param>
/// <returns>The continuation created to handle cancellation. This is normally ignored.</returns>
public Task RegisterCancelledHandler(Task task, Action action)
{
return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
}
/// <summary>
/// Registers a UI thread handler for when the specified task is cancelled.
/// </summary>
/// <typeparam name="TResult">The type of the task result.</typeparam>
/// <param name="task">The task to monitor for cancellation.</param>
/// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param>
/// <returns>The continuation created to handle cancellation. This is normally ignored.</returns>
public Task RegisterCancelledHandler<TResult>(Task<TResult> task, Action action)
{
return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
}
}发布于 2010-07-15 06:58:37
您的静态UIInvoke方法看起来不错,我看不出它有什么问题。
https://stackoverflow.com/questions/3253133
复制相似问题