2010-04-07 28 views
18

Mi aplicación tiene varias ventanas independientes de "nivel superior", que tienen funciones/flujos de trabajo completamente diferentes.WPF Ventana modal usando ShowDialog() Bloquea todos los demás Windows

Actualmente estoy usando ShowDialog() para hacer una ventana WPF modal. La ventana modal es hija de una de las ventanas principales. Sin embargo, está bloqueando todas las ventanas de nivel superior una vez que está abierto. Me gustaría que el cuadro de diálogo bloquee SÓLO la ventana principal desde la que se inició. es posible?

No estoy seguro si es importante, pero la ventana que abre el cuadro de diálogo es la ventana inicial de la aplicación, por lo que se abren todas las demás ventanas de nivel superior.

Respuesta

10

Una opción es iniciar las ventanas que no desea que se vean afectadas por el diálogo en un subproceso diferente. Esto puede ocasionar otros problemas para su aplicación, pero si esas ventanas realmente encapsulan diferentes flujos de trabajo, eso puede no ser un problema. Aquí algunos ejemplos de código que escribí para verificar que esto funciona:

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

que proceden this blog post para el funcionamiento de una ventana de WPF en un subproceso diferente.

5

que tenían el mismo problema y aplicado del comportamiento de diálogo modal como se describe en este post: http://social.msdn.microsoft.com/Forums/vstudio/en-US/820bf10f-3eaf-43a8-b5ef-b83b2394342c/windowsshowmodal-to-parentowner-window-only-not-entire-application?forum=wpf

También probé un enfoque de múltiples hilos de interfaz de usuario, pero esto causó problemas con terceros bibliotecas (Caliburn micro & telerik controles wpf), ya que no están diseñados para ser utilizados en múltiples subprocesos de UI. Es posible hacer que funcionen con múltiples subprocesos de UI, pero prefiero una solución más simple ...

Si implementa el diálogo como se describe, ya no puede usar la propiedad DialogResult, ya que causaría un "DialogResult can establecerse solo después de que se crea Window y se muestra como diálogo "excepción". Simplemente implemente su propiedad y úsela en su lugar.

son necesarios los siguientes ventanas de referencia de la 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); 

A continuación, utilice la siguiente:

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

Este es el controlador de eventos, que vuelve a habilitar la ventana padre, cuando el diálogo se cierra:

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

Y este es el ShowAndWaitHelper necesario para lograr el comportamiento de diálogo modal (Esto bloquea la ejecución del hilo, pero aún ejecuta el ciclo del mensaje.

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

Tu respuesta ha sido increíblemente útil. Hice una versión del método de extensión 'async Task ShowDialogAsync' de esto. – jnm2

Cuestiones relacionadas