在多线程环境中。
我正在使用进度表格中的Showdialog来阻止其他形式的所有用户活动。
完成该过程的一部分后,我隐藏了进度表格,我执行一些任务,然后再次显示(Showdialog(。
一切都很好,但是进度闪烁。
有没有一种方法可以从showdialog继续不隐藏窗口,或者更好,或者更好的是,有没有办法将表演转换为showdialog并再次返回?
编辑
我的代码类似于此
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
ShowDialog();
}
}
void RunWork1()
{
// Do a lot of things including update UI
Hide();
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
编辑2
感谢大家的答案和建议。
最后,我采用了解决方案,将一些小型作品分组为在单元测试中我在UI中使用的一项较大作品,我仍在测试小型作品。
实际上,这不是我要寻找的解决方案,我希望找到基于处理消息泵或执行类似操作的解决方案(请参阅System.Windows.Forms.Application源代码从Rundialog(。(开始在发布此问题之前,我迷路了(。
您不能做到这一点。至少我没有一种简单的方法。解决您的代码的一种方法:
//C#-ish pseudocode based on OPs example; doesn't compile
class frmProgress : Form
{
// Standard stuff
public void DoSomeWorks()
{
async Task.Run(() => RunWork1());
ShowDialog();
}
void RunWork1()
{
// Do a lot of things including update UI
if (_iLikeTheResultOfWork1)
{
async Task.Run(() => RunWork2());
}
else
{
Hide();
}
}
void RunWork2()
{
// Do a lot of things including update UI
Hide();
}
}
编辑对于那些抱怨代码的人没有编译,它们是正确的。但这是OP可以通过获得代码示例而获得的最好的,这就是我猜他被无情地投票的原因。
但是,为了使答案与他人更加相关,我的建议是不要隐藏两个任务之间的进度表格,请在确定任务结束时将其隐藏。所有这些都通过尊重您正在处理的上下文的线程模型,这是OP代码无法做到的。通过最终将在其他线程中运行的方法中的winforms中的UI无法正常工作。
我想您有一些方法,例如:
void Task1()
{
Debug.WriteLine("Task1 Started");
System.Threading.Thread.Sleep(5000);
Debug.WriteLine("Task1 Finished");
}
void Task2()
{
Debug.WriteLine("Task2 Started");
System.Threading.Thread.Sleep(3000);
Debug.WriteLine("Task2 Finished");
}
如果要并行运行它们:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.WhenAll(new Task[] {
Task.Run(()=>Task1()),
Task.Run(()=>Task2()),
});
//Hide the loading image
this.Enabled = true;
}
如果您要一个一个人运行它们:
private async void button1_Click(object sender, EventArgs e)
{
this.Enabled = false;
//Show a loading image
await Task.Run(()=>Task1());
await Task.Run(()=>Task2());
//Hide the loading image
this.Enabled = true;
}
引用异步/等待 - 在异步编程中的最佳实践
我建议您利用异步事件处理程序和任务,以允许在表单通过show对话框中以模式为单位
时进行非阻止呼叫class frmProgress : Windows.Form {
// Standard stuff
public void DoSomeWorks() {
Work -= OnWork;
Work += OnWork;
Work(this, EventArgs.Empty);//raise the event and do work on other thread
ShowDialog();
}
private CancellationTokenSource cancelSource;
private event EventHandler Work = delegate { };
private async void OnWork(object sender, EventArgs e) {
Work -= OnWork; //unsubscribe
cancelSource = new CancellationTokenSource(); //to allow cancellation.
var _iLikeTheResultOfWork1 = await RunWork1Async(cancelSource.Token);
if (_iLikeTheResultOfWork1) {
await RunWork2Async(cancelSource.Token);
}
DialogResult = DialogResult.OK; //just an example
}
Task<bool> RunWork1Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
Task<bool> RunWork2Async(CancellationToken cancelToken) {
// Do a lot of things including update UI
//if while working cancel called
if (cancelToken.IsCancellationRequested) {
return Task.FromResult(false);
}
//Do more things
//return true if successful
return Task.FromResult(true);
}
}
请注意使用取消令牌以根据需要取消任务。
UI现在没有阻止,异步功能可以继续工作。没有闪烁,因为该表格在没有任何中断的情况下显示出来。
我可能会被误解,在这里我所做的,用 wndProc
处理。我创建了一个额外的表格和使用的Show();
方法。显示出表单后,就开始了异步任务。显示表格后,我通过wndProc
处理了该表格。
protected override void WndProc(ref Message m) {
if((f2.IsDisposed || !f2.Visible)) {
foreach(var control in controlList) {
control.Enabled = true; // enable all controls or other logic
}
}
if(m.Msg == 0x18 && !f2.IsDisposed) { // notify dialog opens and double checks dialog's situation
foreach(var control in controlList.Where(ctrl => ctrl.Name != "button1")) {
control.Enabled = false; // disable except cancel button
}
}
base.WndProc(ref m);
}
希望有帮助,
从您发布的代码中,您在runwork((方法末尾调用hide((,然后立即showdialog((。如果我正确理解,您要在您的runwork((方法中调用hide(( first ,这使得在UI更新时可访问主UI窗口。一切完成后,然后出现Showdialog((方法并再次阻止主UI线程。
class frmProgress : Form
{
public bool _iLikeTheResultOfWork1 = true;
// Note: changed to async method and now awaiting the task
public async void DoSomeWorks()
{
await Task.Run(() => RunWork1());
ShowDialog();
if (_iLikeTheResultOfWork1)
{
await Task.Run(() => RunWork2());
ShowDialog();
}
}
// Hide window and wait 5 seconds. Note that the main window is active during this 5 seconds.
// ShowDialog() is called again right after this, so the dialog becomes modal again.
void RunWork1()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
void RunWork2()
{
Hide();
Task.Delay(5000).Wait();
// Do a lot of things including update UI
}
}
我上面的代码将调用runwork1((,该runwork1((将对话框隐藏5秒。在此期间,主UI窗口将处于活动状态。之后,方法返回,showdialog((被调用。关闭该窗口以调用RunWork2((并重新开始过程。
这是您试图实现的目标吗?我刚刚测试了此代码,并且没有视觉"闪烁"。如果这是您想要的,此方法是否会发生"闪烁"?
"当它离开模态消息循环(由showdialog((启动的表单时,该表单始终将关闭((并禁用所有其他窗口。"
https://social.msdn.microsoft.com/forums/sharepoint/sharepoint/en-us/3f6c57a1-92fd-49c5-49c5-9f46-94545454df8078888c/-runtime?forum = winforms
因此,您可以枚举所有应用程序的窗口,并在需要时启用/禁用它们,并在模拟模式时设置对话框窗口始终处于顶部。我认为始终顶部意味着它位于操作系统中的所有窗口上方,因此您可能需要一个计时器,而不是将您的窗口放到前面的窗口(在您的应用程序窗口中(,如果设置为模拟模态