How to make ChildWindow blocking
Asked Answered
A

6

14

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?

Audile answered 13/8, 2009 at 17:59 Comment(3)
Could you explain what are you trying to achieve?Manifesto
Just a simple modal blocking window similar to MessageBox.Show() or Form.ShowDialog()Audile
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.Upwards
A
7

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).

Accusation answered 13/8, 2009 at 19:30 Comment(0)
C
5

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.

Cholula answered 18/8, 2009 at 22:20 Comment(1)
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.Audile
K
3

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();
Karlee answered 27/9, 2010 at 9:40 Comment(0)
H
0

You could try to always cancel the close event and set the DialogResult later.

void ConfigTemplatePopup_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        if (tmpDialogResult != null || DialogResult == true) return;
        tmpDialogResult = DialogResult; //this is a field
        e.Cancel = true;
        var myMessageBox = new WspMessageBox("Are you sure you want to close?", true);
        myMessageBox.Closed += (s, e2) =>
                                    {
                                        if (wspMessageBox.DialogResult == true)
                                        {
                                            DialogResult = tmpDialogResult;
                                        }else
                                        {
                                            tmpDialogResult = null;
                                        }
                                    };
        myMessageBox.Show();
    }
Huppert answered 3/2, 2011 at 10:19 Comment(0)
C
0

Using async and await in Silverlight 5 you can easy create a static Show method that can be blocking, like

public partial class MyDialog {
  MyResultType result;
  readonly AutoResetEvent waitHandle;

  MyDialog() {
    InitializeComponent();
    waitHandle = new AutoResetEvent(false);
  }

  public static Task<MyResultType> Show() {
    var dialog = new MyDialog();
    dialog.Show();
    dialog.Closed += (s, e) => dialog.waitHandle.Set();
    return Task<MyResultType>.Factory.StartNew(() => { 
      dialog.waitHandle.WaitOne(); return dialog.result; });
  }

  void OkButton_Click(object sender, RoutedEventArgs e) {
      result = new MyResultType();
      // Initialize result...

      Close();
  }
}

and call the dialog like

public async void TestDialog() {
  var result = await ConfirmBox.Show();

  // This code will be performed after the Dialog is closed.
  // use result...   
}
Connel answered 28/11, 2012 at 15:37 Comment(2)
-1 I'm afraid, because this snippet doesn't compile. The output of Task<T>.Factory.StartNew() is not awaitable. Also, while this answer introduces the async/await pattern into the mix, it does not solve the fundamental problem that you can't block the UI thread without freezing the browser, so this approach offers no real value over and above Keith Mahoney's solution.Holley
This snippet is from my working project. I use it as a common solution for dialogs. To compile it you'll need the "async targeting pack", which you can add to your project using the nuget. Please read the article using the link in my answer.Connel
G
-1

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.

Garrison answered 20/8, 2009 at 11:1 Comment(2)
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.Audile
I made it blocking by linking an event into the message box. Thus, it blocks the rest of the program until the event firesGarrison

© 2022 - 2024 — McMap. All rights reserved.