使用ShowDialog()阻止所有其他窗口的WPF模式窗口

问题描述:

我的应用程序有几个独立的“顶级”窗口,它们都具有完全不同的功能/工作流程。使用ShowDialog()阻止所有其他窗口的WPF模式窗口

我目前使用ShowDialog()来创建一个WPF窗口模态。模态窗口是其中一个主窗口的子窗口。但是,一旦它打开,它将阻止所有顶级窗口。我希望对话框仅阻止从其启动的父窗口。这可能吗?

我不确定它是否重要,但打开对话框的窗口是应用程序的初始窗口 - 因此所有其他顶级窗口都是从它打开的。

一种方法是启动不希望受到不同线程对话框影响的窗口。这可能会导致您的应用程序出现其他问题,但如果这些窗口确实封装了不同的工作流程,那可能不是问题。下面是一些示例代码,我写来验证这个工程:

<Window x:Class="ModalSample.MyWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="{Binding Identifier}" Height="150" Width="150"> 
    <StackPanel> 
     <TextBox Text="{Binding Identifier}" /> 
     <Button Content="Open Normal Child" Click="OpenNormal_Click" /> 
     <Button Content="Open Independent Child" Click="OpenIndependent_Click" /> 
     <Button Content="Open Modal Child" Click="OpenModal_Click" /> 
    </StackPanel> 
</Window> 

using System.ComponentModel; 
using System.Threading; 
using System.Windows; 

namespace ModalSample 
{ 
    /// <summary> 
    /// Interaction logic for MyWindow.xaml 
    /// </summary> 
    public partial class MyWindow : INotifyPropertyChanged 
    { 
     public MyWindow() 
     { 
      InitializeComponent(); 
      DataContext = this; 
     } 

     private int child = 1; 

     private string mIdentifier = "Root"; 
     public string Identifier 
     { 
      get { return mIdentifier; } 
      set 
      { 
       if (mIdentifier == value) return; 
       mIdentifier = value; 
       if (PropertyChanged != null) 
        PropertyChanged(this, new PropertyChangedEventArgs("Identifier")); 
      } 
     } 

     private void OpenNormal_Click(object sender, RoutedEventArgs e) 
     { 
      var window = new MyWindow {Identifier = Identifier + "-N" + child++}; 
      window.Show(); 
     } 

     private void OpenIndependent_Click(object sender, RoutedEventArgs e) 
     { 
      var thread = new Thread(() => 
       { 
        var window = new MyWindow {Identifier = Identifier + "-I" + child++}; 
        window.Show(); 

        window.Closed += (sender2, e2) => window.Dispatcher.InvokeShutdown(); 

        System.Windows.Threading.Dispatcher.Run(); 
       }); 

      thread.SetApartmentState(ApartmentState.STA); 
      thread.Start(); 
     } 

     private void OpenModal_Click(object sender, RoutedEventArgs e) 
     { 
      var window = new MyWindow { Identifier = Identifier + "-M" + child++ }; 
      window.ShowDialog(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 
} 

我采购this blog post用于在不同的线程运行的WPF窗口。

我有同样的问题,因为在这篇文章中描述实现的模态对话框行为: http://social.msdn.microsoft.com/Forums/vstudio/en-US/820bf10f-3eaf-43a8-b5ef-b83b2394342c/windowsshowmodal-to-parentowner-window-only-not-entire-application?forum=wpf

我也尝试了多个UI线程的方法,但是这引起了与第三方库(卡利微& Telerik的问题wpf控件),因为它们不是构建在多个UI线程中使用的。可以使它们与多个UI线程一起工作,但我更喜欢更简单的解决方案...

如果按照所述实现对话框,则不能再使用DialogResult属性,因为它会导致“DialogResult can只有在Window创建后才能设置,并显示为对话框“异常”。只需实施自己的财产,并使用它。

您需要以下窗口API参考:

/// <summary> 
/// Enables or disables mouse and keyboard input to the specified window or control. 
/// When input is disabled, the window does not receive input such as mouse clicks and key presses. 
/// When input is enabled, the window receives all input. 
/// </summary> 
/// <param name="hWnd"></param> 
/// <param name="bEnable"></param> 
/// <returns></returns> 
[DllImport("user32.dll")] 
private static extern bool EnableWindow(IntPtr hWnd, bool bEnable); 

然后用这个:

// get parent window handle 
IntPtr parentHandle = (new WindowInteropHelper(window.Owner)).Handle; 
// disable parent window 
EnableWindow(parentHandle, false); 
// when the dialog is closing we want to re-enable the parent 
window.Closing += SpecialDialogWindow_Closing; 
// wait for the dialog window to be closed 
new ShowAndWaitHelper(window).ShowAndWait(); 
window.Owner.Activate(); 

这是事件处理程序,从而重新允许父窗口,当对话框关闭:

private void SpecialDialogWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
{ 
    var win = (Window)sender; 
    win.Closing -= SpecialDialogWindow_Closing; 
    IntPtr winHandle = (new WindowInteropHelper(win)).Handle; 
    EnableWindow(winHandle, false); 

    if (win.Owner != null) 
    { 
     IntPtr parentHandle = (new WindowInteropHelper(win.Owner)).Handle; 
     // reenable parent window 
     EnableWindow(parentHandle, true); 
    } 
} 

而这是实现模态对话行为所需的ShowAndWaitHelper (这会阻止线程的执行,但仍会执行消息循环。

private sealed class ShowAndWaitHelper 
{ 
    private readonly Window _window; 
    private DispatcherFrame _dispatcherFrame; 
    internal ShowAndWaitHelper(Window window) 
    { 
     if (window == null) 
     { 
      throw new ArgumentNullException("window"); 
     } 
     _window = window; 
    } 
    internal void ShowAndWait() 
    { 
     if (_dispatcherFrame != null) 
     { 
      throw new InvalidOperationException("Cannot call ShowAndWait while waiting for a previous call to ShowAndWait to return."); 
     } 
     _window.Closed += OnWindowClosed; 
     _window.Show(); 
     _dispatcherFrame = new DispatcherFrame(); 
     Dispatcher.PushFrame(_dispatcherFrame); 
    } 
    private void OnWindowClosed(object source, EventArgs eventArgs) 
    { 
     if (_dispatcherFrame == null) 
     { 
      return; 
     } 
     _window.Closed -= OnWindowClosed; 
     _dispatcherFrame.Continue = false; 
     _dispatcherFrame = null; 
    } 
} 
+0

你的回答是难以置信的帮助。我做了一个'异步任务 ShowDialogAsync'这个扩展方法的版本。 – jnm2 2016-06-14 21:13:43