ispiro
ispiro

Reputation: 27723

Block code until event fires (Like modal dialog)

A modal dialog blocks code until it returns. I want the same thing without the new window.

A simple example with a separate dialog (not what I'm looking for)

MessageBox.Show("Continue");
MessageBox.Show("This waits until the first returns.");

A simple non-working example of what I'm looking for:

mres1.Wait();//Where: ManualResetEventSlim mres1 = new ManualResetEventSlim(false); 
MessageBox.Show("This should wait until button2 is clicked.");

+

private void button2_Click(object sender, EventArgs e)
{
    mres1.Set();
}

Yes, I know this fails because it blocks the whole thread. But that's the kind of system I'm looking for, only without blocking the thread, only the code. Is that possible?

(I'm not looking for a workaround (-"Call the rest of the code in the event's event handler."). I'm asking if it can be done as described.)

EDIT: The goal is to replace modal dialogs that I currently have, with a Panel (with Dock = DockStyle.Fill).

Upvotes: 1

Views: 337

Answers (4)

Scott Chamberlain
Scott Chamberlain

Reputation: 127603

This is a slightly modified version of SiLo's old solution that does not use any extra threads and still works with .NET 4.0

EDIT: Updated to use a panel, might need a little more tweaking but I think this will get you 99% of the way there.

private TaskCompletionSource<object> waiter = new TaskCompletionSource<object>(); 

private void button1_Click(object sender, EventArgs e)
{

    Panel coverScreen = new Panel();
    coverScreen.BackColor = Color.Black;
    coverScreen.Dock = DockStyle.Fill;
    this.Controls.Add(coverScreen);

    waiter.Task.ContinueWith(_ =>
    {
        this.Controls.Remove(coverScreen);
        coverScreen.Dispose();
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

private void button2_Click(object sender, EventArgs e)
{
    waiter.SetResult(null);
    waiter = new TaskCompletionSource<object>(); //Reset the TaskCompletionSource
}

Upvotes: 2

Erik
Erik

Reputation: 12868

EDIT: I think I understand your intentions better. Basically, you want to create your own modal behavior instead of using the system default.

In this case you want two separate blocks of code to run, one after the other, and waiting for some user interaction to trigger the transition (such as an OK button or Hide() method).

Perhaps it would be best to make your own ModalPanel UI element that would drive from the base Panel control. In your subclass, you could expose a ShowModal method that let you put in a task and it would execute once the ModalPanel was hidden.

For example:

class ModalPanel : Panel
{
    protected readonly Button okButton;
    Task task;

    public ModalPanel()
    {
        okButton = new Button();

        okButton.Width = 100;
        okButton.Height = 32;

        okButton.Left = (this.Width - okButton.Width) / 2;
        okButton.Top = (this.Height - okButton.Height) / 2;

        okButton.Text = "OK";
        okButton.Click += delegate { this.Hide(); };
    }

    public virtual void ShowModal(Task completion)
    {
        this.task = completion;

        this.Dock = DockStyle.Fill;
        this.Show();
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);

        var isHidden = !this.Visible;

        if (isHidden && task != null)
            task.Start();
    }
}

Upvotes: 1

T McKeown
T McKeown

Reputation: 12857

If you want to continue updating the UI then you should look into the await/async approach however you can wait/signal on non UI threads this way:

If you are partial to the TPL you can use Task<>

ManualResetEventSlim mres1 = null;

private void button1_Click(object sender, EventArgs e)
{
   System.Threading.ThreadPool.QueueUserWorkItem( Wait , null );
}

private void Wait(object state)
{
    mres1 = new ManualResetEventSlim(false);
    mres1.Wait();
    //rest of the code to execute....
}
private void button2_Click(object sender, EventArgs e)
{
    mres1.Set();

}

Upvotes: 0

Servy
Servy

Reputation: 203814

await, in combination with a means of converting whatever you want into a task, makes this both easy, clear, and effective.

private async void button1_Click(object sender, EventArgs e)
{
    var tcs = new TaskCompletionSource<bool>();
    button2.Click += (s, args) => tcs.TrySetResult(true);
    await tcs.Task;
    DoSomething();
}

Upvotes: -1

Related Questions