开始使用我没有经验的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, '.');
});
谷歌处于最佳状态,发现this article有人想出了同样的方法。我会在那里继续我的“骚扰”。谢谢!
答案 0 :(得分:3)
我坚信任何依赖于InvokeRequired
的代码都会遵循一个糟糕的模式。
每个方法应知道它在UI线程的上下文中运行,或者知道它不是。可以从任何线程运行的唯一方法应该是在同步类上,例如Semaphore
(如果你正在编写同步类,请问自己是否真的应该这样做)
这是多线程设计的原则,可以减少错误并澄清代码。
对于从后台线程更新UI的问题,我建议使用安排在用户界面Task
的{{1}}对象。如果SynchronizationContext
类不可用(例如,定位4.0之前的框架),请使用Task
。
这是一个后台任务的示例,它支持向UI报告进度以及支持取消和错误情况:
BackgroundWorker
此示例代码使用我为方便起见而定义的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();
}
}
(它清理代码,IMO)。此类型为defined on my blog:
ProgressReporter
答案 1 :(得分:0)
你的静态UIInvoke
方法看起来不错,我看不出它有什么问题。