如何使ChildWindow阻塞

14

ChildWindow是一个模态窗口,但它不会阻塞。有没有办法使它阻塞?我基本上想要一个ShowDialog()方法,该方法将调用ChildWindow.Show(),但在用户关闭ChildWindow之前不返回。我尝试使用Monitor.Enter()在ChildWindow.Show()后进行阻塞,但ChildWindow从未呈现,浏览器只是挂起了。有人有什么想法吗?


你能解释一下你想要实现什么吗? - Ray
只是一个简单的模态阻塞窗口,类似于MessageBox.Show()或Form.ShowDialog()。 - Mike Hall
我已经尝试了所有的方法,但始终没有找到解决方案。我一直在尝试制作自己的MessageBox。我甚至尝试了异步,但它总是会锁死。在读到Tim Heuer说无法实现真正的Modal后,我放弃了。 - Paully
6个回答

7

5
您无法在Silverlight中实现您正在尝试的操作。您对UI所做的任何更改都将在UI线程上运行。如果阻止UI线程,则用户无法与浏览器交互,因此他们无法采取任何行动来解除阻塞线程。
如果确实想要创建一个阻止对话框窗口,唯一的方法是使用非UI线程。例如,您可以创建一个类似以下代码的方法:
    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();
    }

这种方法会显示一个对话框窗口,并且在对话框窗口关闭之前不会返回。但是,这种方法只能从非UI线程中调用。如果从UI线程中调用它将导致死锁(因为UI线程正在等待只能在UI线程上触发的事件)。

或者,您应该考虑使您的逻辑异步而不是强制它同步。


我喜欢这个解决方案,看起来应该可以工作,但由于它不能在UI线程上调用,我不确定我能否使用它(即使我在问题中实际上没有指定(因为当时我甚至没有考虑过))。无论如何,这是一个很好的解决方法。 - Mike Hall

3
如果您想在子窗口关闭时执行某些操作,请使用以下代码。在调用之前隐藏不需要的控件,并在子窗口关闭时显示,简单明了 :)
ChildWindow cw = new ChildWindow(); 
cw.Closed += new EventHandler(cw_Closed);
cw.Show();

0
您可以尝试始终取消关闭事件并稍后设置DialogResult。
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();
    }

0

使用Silverlight 5中的async和await,您可以轻松创建一个静态Show方法,该方法可以是阻塞的,例如:

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();
  }
}

然后像这样调用对话框

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

  // This code will be performed after the Dialog is closed.
  // use result...   
}

很抱歉,因为这段代码片段无法编译。Task<T>.Factory.StartNew()的输出是不可等待的。此外,虽然这个答案引入了async/await模式,但它并没有解决不能阻塞UI线程而不冻结浏览器的根本问题,因此这种方法在Keith Mahoney的解决方案之上并没有提供真正的价值。 - Rob Lyndon
这段代码摘自我的工作项目,我将其用作对话框的通用解决方案。要编译它,您需要使用 "async targeting pack",您可以通过NuGet将其添加到您的项目中。请阅读我回答中提供的链接文章。 - Alexander Zwitbaum

-1
我努力创建了一个“阻塞”模态对话框,通过使用自定义控件,将其垂直和水平对齐方式设置为拉伸,并将其作为当前控件布局的子项添加进去。
例如:
   <Grid x:Name="LayoutRoot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <Rectangle Fill="LightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0.3" />
    </Grid>

然后在自定义控件中

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

根据模态框需要显示的内容,我会动态地向模态框布局中添加控件。显然,这不是一种优雅的解决方案,但至少它能够工作。

这将使它以某种方式成为模态,但似乎不会阻塞,因为没有函数调用会一直阻塞直到用户关闭消息框。 - Mike Hall
我通过将事件链接到消息框来使其阻塞。因此,在事件触发之前,它会阻止程序的其他部分运行。 - Johannes

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