2010-04-07 9 views
18

Moja aplikacja ma kilka niezależnych okien "najwyższego poziomu", z których wszystkie mają zupełnie inne funkcje/przepływy pracy.Okno modemu WPF przy użyciu bloków ShowDialog() Wszystkie inne systemy Windows

Obecnie używam ShowDialog() do wykonania modalnego okna WPF. Okno modalne jest dzieckiem jednego z głównych okien. Jednak po otwarciu wszystkie okna najwyższego poziomu są blokowane. Chciałbym, aby okno dialogowe blokowało TYLKO okno główne, z którego zostało uruchomione. czy to możliwe?

Nie jestem pewien, czy to ma znaczenie, ale okno, które otwiera okno dialogowe, jest początkowym oknem aplikacji - tak więc wszystkie inne okna najwyższego poziomu są od niego otwierane.

Odpowiedz

10

Jedną z opcji jest uruchomienie okien, których nie powinno dotyczyć okno dialogowe w innym wątku. Może to powodować inne problemy związane z aplikacją, ale jeśli te okna naprawdę zawierają różne przepływy pracy, może to nie być problem. Oto przykładowy kod pisałem aby sprawdzić, czy to działa:

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

I pozyskiwane this blog post na prowadzenie okno WPF na innym wątku.

5

miałem ten sam problem i wdrożył modalne zachowanie dialogowe jak opisane w tym poście: http://social.msdn.microsoft.com/Forums/vstudio/en-US/820bf10f-3eaf-43a8-b5ef-b83b2394342c/windowsshowmodal-to-parentowner-window-only-not-entire-application?forum=wpf

Próbowałem też podejście gwint wielokrotny UI, ale to spowodowało problemy z obiektywem innej biblioteki (Caliburn mikro & Telerik elementy sterujące wpf), ponieważ nie zostały zbudowane do użycia w wielu wątkach interfejsu użytkownika. Możliwe jest, aby działały z wieloma wątkami UI, ale ja wolę prostsze rozwiązanie ...

Jeśli zaimplementujesz okno dialogowe zgodnie z opisem, nie możesz już używać właściwości DialogResult, ponieważ spowoduje to "DialogResult może być ustawione tylko po utworzeniu okna i wyświetlane jako okno dialogowe "wyjątek. Wystarczy zaimplementować własną własność i użyć jej zamiast niej.

potrzebne następujące Windows API odniesienia:

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

następnie użyj tego:

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

To obsługi zdarzeń, które ponownie umożliwia okna nadrzędnego, gdy okno jest zamknięte:

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

A to jest ShowAndWaitHelper potrzebny do osiągnięcia modalnego zachowania w oknie dialogowym (blokuje to wykonanie wątku, ale nadal wykonuje pętlę komunikatów.

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

Twoja odpowiedź była niezwykle pomocna. Zrobiłem to z '' async Task ShowDialogAsync'. – jnm2

Powiązane problemy