winform-从showdialog继续而无需隐藏窗口



在多线程环境中。

我正在使用进度表格中的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

因此,您可以枚举所有应用程序的窗口,并在需要时启用/禁用它们,并在模拟模式时设置对话框窗口始终处于顶部。我认为始终顶部意味着它位于操作系统中的所有窗口上方,因此您可能需要一个计时器,而不是将您的窗口放到前面的窗口(在您的应用程序窗口中(,如果设置为模拟模态

最新更新