在调用ShowDialog()后如何继续执行代码

24

Form.ShowDialog()方法会导致代码停止执行,直到新调用的窗体关闭为止。我需要在调用ShowDialog()方法后让代码继续运行。我搜索并阅读了使用BackgroundWorker的相关信息,但这是我第一次听说它,以前从未使用过。

Form2 form2this = new Form2();
form2this.ShowDialog();
MessageBox.Show("Something");

点击按钮后会执行这段代码,我要如何调用 ShowDialog 方法以防止用户与主窗体交互,但仍允许主窗体继续工作?

如果已经有讨论此问题的内容,那么请见谅,但我发现所有的方法都过于复杂,而这个任务本身却很简单。实际上,我很惊讶 ShowDialog 方法中没有包含这种功能,例如 ShowDialog().Continue 就很好。


1
当子窗体显示时,您想要做什么样的事情?您是更改父窗体、与子窗体交互还是执行非 UI 任务? - Servy
Parentform.Enabled = false - Brad
6个回答

22
  • 如果您只是想让代码继续执行而不会阻塞直到弹出窗口关闭,请考虑使用Show而不是ShowDialog

  • 如果您希望在子窗体弹出时父窗体执行一些操作,那么使用BackgroundWorker(或启动一个新的Thread/Task)可能是合适的。不过更多了解这些任务的信息会有所帮助。如果需要与主窗体或子窗体交互,那么这似乎会有麻烦; 只需要进行一些后台任务而没有UI交互,那么这就是正确的思路。

  • 另一个可能性是,您真正想做的事情实际上应该在子窗体中完成,而不是在父窗体中。


3
谢谢。我不能使用Show而必须使用ShowDialog的原因是,我不希望用户能够与父窗体进行交互。但我可以在使用Show方法时可能加入parentForm.Enabled = false选项。一旦调用show方法并显示子窗体后,在关闭子窗体时,它将重新启用父窗体。我正在调用第二个窗体,该窗体内嵌有定时器以在计时完成后自动关闭。第二个窗体基本上只是一个带有标签的表单,指示用户耐心等待,因为父窗体正在进行一些耗时操作。 - Seth
任务。与父窗体交互会导致正在执行的任务出现错误。 - Seth
1
@Seth 只需小心实现适合实际任务的解决方案,而不是拼凑一个旨在支持不同场景的解决方案。根据您的编辑,这似乎是本例的情况。由于您正在执行后台任务,并且在此期间不需要与 UI 实际交互,因此应启动 BackgroundWorkerTask 来执行长时间运行的任务,然后在启动后使用 ShowDialog 显示对话框窗体。 - Servy
1
非常感谢您的回答和您的专业知识,以及希望我做正确的事情。我真的很感激您想让我学习正确的方法。使用BackgroundWorker或Task确实很有道理。但是我以前从未以那种方式使用过它们。实际上,我确实使用了Brad的方法,在调用Show之后使用ParentForm.Enabled = false。然后,我使用事件处理程序等待子窗体关闭,一旦它关闭,我就重新启用父窗体。通过使父窗体变灰,它实际上可以很好地混合在一起。非常感谢大家。 - Seth
我很抱歉我的经验不足,我只是自学的,并且通过YouTube /Web学习,平时并不从事编程。所以我非常感激你们所有人的合作精神,再次感谢@Brad提供的想法!祝一切顺利,Seth - Seth

7
只要在模态对话框打开期间进行异步操作,您可以像下面示例一样简单地完成操作,假设button1_Click()是按钮的事件处理程序。
private async void button1_Click(object sender, EventArgs e)
{
    // create and display modal form
    Form2 modalForm = new Form2();
    BeginInvoke((Action)(() => modalForm.ShowDialog()));

    // do your async background operation
    await DoSomethingAsync();

    // close the modal form
    modalForm.Close();
}


private async Task DoSomethingAsync()
{
    // example of some async operation....could be anything
    await Task.Delay(10000);
}

我发现当我使用推荐使用Show()的方法时,可能会出现对话框在应用程序之间来回切换后却出现在主窗体之后的情况。而当我使用上述解决方案时,这种情况永远不会发生。

0

这是我的方式,很丑陋但我没有更好的想法。

private void AppUiMain_Shown(object sender, EventArgs e)
{
    var loading = new AppUiLoading();
    loading.Shown += (o, args) =>
    {
        bool isLoading = true;
        loading.Top = (int)(loading.Top * 1.16);

        Application.DoEvents();//refresh ui

        EventHandler ehr = null;
        EventHandler ehe = null;
        ehr = (ss, ee) =>
        {
            App.Instance.Ready -= ehr;
            App.Instance.Error -= ehe;
            isLoading = false;
        };
        ehe = (ss, ee) =>
        {
            loading.Text = "Error";
            loading.ShowAbortButton("Error occur");
        };
        App.Instance.Error += ehe;
        App.Instance.Ready += ehr;
        InitApp();

        //HACK: find a better way to `refresh' main form
        Application.DoEvents();
        this.Height++;
        this.Height--;

        //HACK: find a better way to keep message looping on ShowDialog
        while (isLoading)
            Application.DoEvents();

        loading.Close();
    };
    loading.ShowDialog(this);
}

0

-1

我认为下一个异步ShowDialog的解决方案是:

public bool DialogResultAsync
{
    get;
    private set;
}

public async Task<bool> ShowDialogAsync()
{
    var cts = new CancellationTokenSource();
    // Attach token cancellation on form closing.
    Closed += (object sender, EventArgs e) =>
    {
        cts.Cancel();
    };
    Show(); // Show message without GUI freezing.
    try
    {
        // await for user button click.
        await Task.Delay(Timeout.Infinite, cts.Token);
    }
    catch (TaskCanceledException)
    { } 
}

public void ButtonOkClick()
{
    DialogResultAsync = true;
    Close();
}

public void ButtonCancelClick()
{
    DialogResultAsync = false;
    Close();
}

在主窗体中,您必须使用此代码:

public async void ShowDialogAsyncSample()
{
    var msg = new Message();
    if (await msg.ShowDialogAsync())
    {
        // Now you can use DialogResultAsync as you need.
        System.Diagnostics.Debug.Write(msg.DialogResultAsync);
    }
}

-1

运行异步调用以显示模态框。以下是wpf中的示例:

private Window waitView;

/// <summary>
/// Closes a displayed WaitView from code.
/// </summary>
public void CloseWaitView()
{
  if(waitView != null)
  {
     // Work on the gui Thread of waitView.
     waitView.Dispatcher.Invoke(new Action(() => close()));
  }
}

/// <summary>
/// Closes a displayed WaitView and releases waitView-Instance.
/// </summary>    
private void close()
{
   waitView.Close();
   waitView = null;
}   

/// <summary>
/// Showes a modal WaitView (Window).
/// </summary>
public void ShowWaitView()
{
  // instance a new WaitViewWindow --> your Window extends Window-Class
  waitView = new WaitViewWindow();

  // prepare a operation to call it async --> your ShowDialog-call
  var asyncCall = new Action(() => waitView.Dispatcher.Invoke(
                                   new Action(() => waitView.ShowDialog())
                             ));

  // call the operation async

  // Argument 1 ar:
  // ar means IAsyncResult (what should be done, when come back from ShowDialog -->     
  // remove view memory with set waitView to null or ... dispose

  // the second argument is an custom parameter you can set to use in ar.AsyncState
  asyncCall.BeginInvoke(ar => waitView = null, null);

  // all from here is done during ShowDialog ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接