我的应用程序有几个独立的“顶级”窗口,它们具有完全不同的功能/工作流程。
我目前正在使用ShowDialog()使一个WPF窗口变为模态。该模态窗口是主窗口中的子窗口。但是,一旦打开,它会阻止所有顶级窗口。我希望对话框仅阻止从它启动的父窗口。这可行吗?
我不确定它是否重要,但打开对话框的窗口是应用程序的初始窗口--因此,所有其他顶级窗口都是从它打开的。
我的应用程序有几个独立的“顶级”窗口,它们具有完全不同的功能/工作流程。
我目前正在使用ShowDialog()使一个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的方法,但这会导致与第三方库(caliburn micro和telerik wpf controls)的问题,因为它们没有构建成用于多个UI线程。虽然可以使它们在多个UI线程中工作,但我更喜欢更简单的解决方案...
如果您按照所述方式实现对话框,则无法再使用DialogResult属性,因为它会引发“DialogResult can be set only after Window is created and shown as dialog”异常。只需实现自己的属性并使用即可。
您需要以下Windows 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;
}
}
一个选项是启动那些你不想被对话框影响的窗口在不同的线程上。这可能会导致应用程序出现其他问题,但如果这些窗口确实封装了不同的工作流程,那可能不是问题。以下是我编写的一些示例代码,以验证此方法是否有效:
<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;
}
}
我获取了这篇博客文章,用于在不同线程上运行WPF窗口。
async Task<DialogResult> ShowDialogAsync
扩展方法版本。 - jnm2