views:

1782

answers:

6

The ChildWindow is a modal window, but it does not block. Is there any way to make it blocking? I basically want a ShowDialog() method that will call ChildWindow.Show() but then not return until the user has closed the ChildWindow. I tried using Monitor.Enter() to block after ChildWindow.Show(), but the ChildWindow never rendered and the browser just hung. Anyone have any ideas?

+3  A: 

I don't believe it supports that behavior. You can vote for it on CodePlex. Depending on what you want, you might either look at some of Tim Heuer's workarounds here, or use a different custom control, like the Silverlight Modal Control (on CodePlex).

Jon Galloway
+3  A: 

You can't do what you are trying to do in Silverlight. Any changes you make to the UI will run on the UI thread. If you block the UI thread, the user can't interact with the browser, so there is no action that they could take to unblock the thread.

If you really want to create a blocking dialog window, the only way to do that is from a non-UI-thread. E.g. you could create a method that looks something like:

    private void ShowModalDialog()
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        Dispatcher.BeginInvoke(() =>
        {
            ChildWindow cw = new ChildWindow();
            cw.Content = "Modal Dialog";
            cw.Closed += (s, e) => waitHandle.Set();
            cw.Show();
        });
        waitHandle.WaitOne();
    }

This method will show a dialog window and won't return until the dialog window is closed. But this method can only be called from a non-UI-thread. Calling it from the UI-thread will cause a deadlock (since the UI-thread is waiting for an event that can only fire on the UI-thread).

Alternatively, you should consider making your logic async rather than forcing it to be synchronous.

KeithMahoney
I like this solution and it looks like it should work, but since it can't be called on the UI thread, I'm not sure I can use this (even though I didn't actually specify that in my question (since I didn't even think about it then)). Either way, great workaround.
Mike Hall
+1  A: 

I've tried everything and I never found a solution. I was trying to make my own MessageBox. I even tried async but it always locked up. I gave up after reading that Tim Heuer saying it wasn't possible to have true Modal.

Paully
A: 

I hacked away at creating a "blocking" modal dialog, by having a custom control that has a vertical- and horizontal alignment as stretch, and then add that as a child of the layout of the current control.

e.g.

   <Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <Rectangle Fill="LightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0.3" />
    </Grid>

and then in the custom control

Utilities.UITools.MessageBox x = new Utilities.UITools.MessageBox();
x.SetError(e.Result);
this.LayoutRoot.Children.Add(x);

Depending on what has to be displayed in the modal, I dynamically add controls to the modal layout. Clearly, not an elegant solution, but at least it works.

Johannes
That would make it modal in a way, but it doesn't appear that it would be blocking in that there is no function call that would block until the user closed the message box.
Mike Hall
I made it blocking by linking an event into the message box. Thus, it blocks the rest of the program until the event fires
Johannes
A: 

Hi, Jeff,

In solution,

private void ShowModalDialog()
{
    AutoResetEvent waitHandle = new AutoResetEvent(false);
    Dispatcher.BeginInvoke(() =>
    {
        ChildWindow cw = new ChildWindow();
        cw.Content = "Modal Dialog";
        cw.Closed += (s, e) => waitHandle.Set();
        cw.Show();
    });
    waitHandle.WaitOne();
}

How can we retrive cw inside Dispatcher.BeginInvoke()?

Because after "waitHandle.WaitOne();", we might need to check the cw.DialogResult for the next operation.

A: 

If you want to perform some action on your child window close, use the following code. Hide unwanted controls before invoke and show on close of the child window, simple :)

ChildWindow cw = new ChildWindow(); 
cw.Closed += new EventHandler(cw_Closed);
cw.Show();
Ashin Antony