ShowDialog 方法挂起而不显示 window ¿死锁?
ShowDialog method hangs without showing the window ¿Deadlock?
我们有一个 WPF 忙碌 window 指示器。它使用 window.ShowDialog()
在主线程上显示。在响应 Loaded 事件时,将执行一个操作并关闭 window,以便应用程序继续其工作。
window.ShowDialog()
似乎时常挂起(很少)不显示对话框并且未触发 Loaded 事件,因此应用程序挂起。相关代码如下:
private void BusyIndicatorAsyncCall(string text, Action<DoWorkEventArgs> doWorkDinamicView = null, Action doWork = null, Action workCompleted = null, Action<Exception> exceptionReturn = null)
{
Window window = this.CreateWindowOfBusyIndicator(text);
Dispatcher dispatcher = window.Dispatcher;
BackgroundWorker backgoundworker = new BackgroundWorker();
IViewModel viewModel = (window.Content as UserControl).DataContext as IViewModel;
this.Modals.Add(viewModel, window);
if (doWorkDinamicView != null)
{
DoWorkEventArgs eventArgs = new DoWorkEventArgs(window.Content);
backgoundworker.DoWork += (s, e) => doWorkDinamicView.Invoke(eventArgs);
}
else if (doWork != null)
{
backgoundworker.DoWork += (s, e) => { doWork.Invoke(); };
}
backgoundworker.RunWorkerCompleted += (s, e) =>
{
Exception exception = e.Error;
if (exception == null)
{
if (workCompleted != null)
{
try
{
this.StyleName = null;
workCompleted.Invoke();
}
catch (Exception ex)
{
exception = ex;
}
}
}
this.Modals.Remove(viewModel);
dispatcher.Invoke(new Action(window.Close));
if (exception != null)
{
if (exceptionReturn == null)
throw new Exception("Error en RunWorkerCompleted.", exception);
else
exceptionReturn(exception);
}
};
RoutedEventHandler onLoaded = new RoutedEventHandler((sender, e) =>
{
try
{
backgoundworker.RunWorkerAsync();
}
catch
{
}
});
this.BusyIndicatorImpl(window, onLoaded);
}
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
window.Loaded += onLoaded;
window.ShowDialog();
window.Loaded -= onLoaded;
}
当应用程序挂起时,我可以在 window.ShowDialog()
方法上看到指令指针,但是 window 在应用程序上不可见,并且 backgroundWorker 尚未启动,所以我猜测是尚未引发 OnLoaded 事件。
应用程序并没有真正挂起,因为它可以正确地重新绘制,但您无法单击屏幕上的任何位置。作为应用程序挂起时的副作用,它会在 Windows 7.
上从任务栏中消失
我中断执行时看到的调用栈如下:
user32.dll!_NtUserGetMessage@16() + 0x15 bytes
user32.dll!_NtUserGetMessage@16() + 0x15 bytes
[Managed to Native Transition]
WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x14 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x80 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0x75 bytes WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox) + 0x17d bytes
PresentationFramework.dll!System.Windows.Window.Show() + 0x5c bytes
PresentationFramework.dll!System.Windows.Window.ShowDialog() + 0x27d bytes
> xxx.dll!xxx.BusyIndicatorImpl(System.Windows.Window window, System.Windows.RoutedEventHandler onLoaded) Line 743 + 0xd bytes C#
正如其他人之前所说,它看起来像是一个死锁,所以我得到了一个转储,其中包含 Visual Studio 和 运行 一些工具,用于查找死锁。这是应用程序拥有的线程的信息 运行:
Debugger Thread ID Managed Thread ID OS Thread ID Thread Object GC Mode Domain Lock Count Apt Exception
0 1 5684 2b2390 Preemptive 9176600 0 STA
6 2 5572 2c7a80 Preemptive 2a82f8 0 MTA (Finalizer)
7 3 3676 2cb828 Preemptive 2a82f8 0 Unknown
11 4 864 7f7d5c0 Preemptive 2a82f8 0 MTA (Threadpool Worker)
15 10 4340 921cdc8 Preemptive 9176600 1 MTA
16 12 1648 9438560 Preemptive 2a82f8 0 MTA (Threadpool Completion Port)
17 14 3380 9001038 Preemptive 2a82f8 0 Unknown (Threadpool Worker)
21 7 5336 9002fe8 Preemptive 2a82f8 0 MTA (Threadpool Worker)
20 5 4120 9003fc0 Preemptive 2a82f8 0 MTA (Threadpool Worker)
25 18 5172 9004508 Preemptive 2a82f8 0 MTA (Threadpool Worker)
27 11 5772 9003a78 Preemptive 2a82f8 0 MTA (Threadpool Worker)
只有一个线程带有应用程序代码(0,通过 ShowDialog 调用管理 1)。其他线程没有应用程序代码,线程 15(托管 10)是唯一具有一些 .Net 代码的线程。
查看线程 15(托管 10),因为它是带锁的线程,我看到以下调用堆栈:
[[HelperMethodFrame_1OBJ] (System.Threading.WaitHandle.WaitMultiple)] System.Threading.WaitHandle.WaitMultiple(System.Threading.WaitHandle[], Int32, Boolean, Boolean)
mscorlib_ni!System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)+92
System_ni!System.Net.TimerThread.ThreadProc()+28f
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+6f
mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+a7
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+16
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+41
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart()+44
[[GCFrame]]
[[DebuggerU2MCatchHandlerFrame]]
[[ContextTransitionFrame]]
[[DebuggerU2MCatchHandlerFrame]]
这个调用堆栈看起来像是一个等待被解雇的计时器,但也许我对这种解释有误,所以我不知道如何继续。
我可以根据您的要求提供您需要的任何信息。我不是 WinDbg 方面的专家,但我开始能够处理它,因此您也可以要求我获取有关它的信息。
更新:
向应用程序添加一些日志后,我们有以下额外信息:
问题是方法 dispatcher.Invoke(new Action(window.Close));
被调用并且执行时没有抛出异常,但是方法 window.ShowDialog();
没有 return。
我们已尝试使用 Spy++ 和类似工具找到 window,据我所知 window 不存在,但 window.ShowDialog();
一直在执行。
我希望这能让您对正在发生的事情有所了解。
可能是死锁也可能不是,但我们没有足够的信息来开始(例如 Loaded
事件处理程序内部发生了什么,或者在什么上下文中调用了东西)。
您的代码中有一些值得仔细研究的地方:
您似乎没有设置模态对话框的所有者。在最坏的情况下,这可能会导致您的模态对话框显示在背景中,很难或无法关闭。
不清楚您在哪里创建 window。 Loaded
事件只会在您的 window 第一次显示时触发(与 Activated
事件相反)。如果您回收 window,则不会触发加载事件,您的后台工作程序也不会启动。
这是一个非常基本的工作 WPF 示例,由一个主 window 和一个后台工作人员和一个模式进度对话框组成。我会尝试从这里开始尝试找出问题所在。
MainWindow.xaml
<Window x:Class="ProgressBarSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="200"
Height="100"
mc:Ignorable="d">
<Grid>
<Button Click="OnButtonClick">Start</Button>
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace ProgressBarSample
{
public partial class MainWindow
{
private BackgroundWorker _backgroundWorker;
private ProgressWindow _progressWindow;
public MainWindow()
{
InitializeComponent();
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
_progressWindow = new ProgressWindow { Owner = this };
_backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
_backgroundWorker.DoWork += OnWorkerDoWork;
_backgroundWorker.RunWorkerCompleted += OnWorkerRunWorkerCompleted;
_backgroundWorker.ProgressChanged += OnWorkerProgressChanged;
_backgroundWorker.RunWorkerAsync();
_progressWindow.ShowDialog();
}
private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
_progressWindow.ProgressValue = e.ProgressPercentage;
}
private void OnWorkerRunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
_progressWindow.Close();
MessageBox.Show("Done");
}
private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
{
int initialValue = 100;
for (int i = 0; i < initialValue; i++)
{
Thread.Sleep(50);
_backgroundWorker.ReportProgress(i);
}
}
}
}
ProgressWindow.xaml
<Window x:Class="ProgressBarSample.ProgressWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Progress"
Width="300"
Height="100"
mc:Ignorable="d">
<Grid>
<ProgressBar Maximum="100" Minimum="0"
Value="{Binding ProgressValue}" />
</Grid>
</Window>
ProgressWindow.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ProgressBarSample
{
public partial class ProgressWindow : INotifyPropertyChanged
{
private double _progressValue;
public ProgressWindow()
{
InitializeComponent();
DataContext = this;
}
public double ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
调用堆栈显示您的对话框处于等待消息的模态消息循环中。它没有被锁定或阻止。
正如 Dirk 指出的那样,Loaded 事件并不总是引发。其来源可能是他建议的两件事之一。
解决方法是在 BusyIndicatorImpl 中显式调用 onLoaded。
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
onLoaded();
window.ShowDialog();
}
或者,您可以 post 它到您的模态对话框。
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
window.Dispatcher.InvokeAsync(() => onLoaded(window, null));
window.ShowDialog();
}
我在一年后找到了问题的原因,以下代码显示了正在发生的事情的概念证明:
总结(由于实际代码中的竞争条件)有 3 个 windows 具有父子关系 (A-->B-->C) 并且代码正在关闭 B。这在 WPF 应用程序上工作(并且 C 也被关闭)但在 VSTO 加载项中不起作用并挂起,B 永远不会离开 ShowDialog 方法:
为 Office Word 2010 创建一个新的 VSTO 项目并粘贴以下代码(不确定如果您针对不同的 Office 版本会发生什么):
using System.Diagnostics;
using System.Windows;
using System.Windows.Interop;
using Action = System.Action;
namespace WordAddIn1HangTest
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Window window1 = new Window();
window1.Content = "1";
Window window2 = new Window();
window2.Content = "2";
WindowInteropHelper windowInteropHelper1 = new WindowInteropHelper(window1);
WindowInteropHelper windowInteropHelper2 = new WindowInteropHelper(window2);
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
windowInteropHelper1.Owner = Process.GetCurrentProcess().MainWindowHandle;
window1.ShowDialog();
MessageBox.Show("Hello");
}));
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
windowInteropHelper2.Owner = windowInteropHelper1.Handle;
window2.ShowDialog();
}));
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
window1.Close();
}));
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
随着 window1 关闭,您应该会看到消息 "Hello"。相反,您没有看到任何 window 打开并且 ShowDialog 调用挂起。
我们有一个 WPF 忙碌 window 指示器。它使用 window.ShowDialog()
在主线程上显示。在响应 Loaded 事件时,将执行一个操作并关闭 window,以便应用程序继续其工作。
window.ShowDialog()
似乎时常挂起(很少)不显示对话框并且未触发 Loaded 事件,因此应用程序挂起。相关代码如下:
private void BusyIndicatorAsyncCall(string text, Action<DoWorkEventArgs> doWorkDinamicView = null, Action doWork = null, Action workCompleted = null, Action<Exception> exceptionReturn = null)
{
Window window = this.CreateWindowOfBusyIndicator(text);
Dispatcher dispatcher = window.Dispatcher;
BackgroundWorker backgoundworker = new BackgroundWorker();
IViewModel viewModel = (window.Content as UserControl).DataContext as IViewModel;
this.Modals.Add(viewModel, window);
if (doWorkDinamicView != null)
{
DoWorkEventArgs eventArgs = new DoWorkEventArgs(window.Content);
backgoundworker.DoWork += (s, e) => doWorkDinamicView.Invoke(eventArgs);
}
else if (doWork != null)
{
backgoundworker.DoWork += (s, e) => { doWork.Invoke(); };
}
backgoundworker.RunWorkerCompleted += (s, e) =>
{
Exception exception = e.Error;
if (exception == null)
{
if (workCompleted != null)
{
try
{
this.StyleName = null;
workCompleted.Invoke();
}
catch (Exception ex)
{
exception = ex;
}
}
}
this.Modals.Remove(viewModel);
dispatcher.Invoke(new Action(window.Close));
if (exception != null)
{
if (exceptionReturn == null)
throw new Exception("Error en RunWorkerCompleted.", exception);
else
exceptionReturn(exception);
}
};
RoutedEventHandler onLoaded = new RoutedEventHandler((sender, e) =>
{
try
{
backgoundworker.RunWorkerAsync();
}
catch
{
}
});
this.BusyIndicatorImpl(window, onLoaded);
}
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
window.Loaded += onLoaded;
window.ShowDialog();
window.Loaded -= onLoaded;
}
当应用程序挂起时,我可以在 window.ShowDialog()
方法上看到指令指针,但是 window 在应用程序上不可见,并且 backgroundWorker 尚未启动,所以我猜测是尚未引发 OnLoaded 事件。
应用程序并没有真正挂起,因为它可以正确地重新绘制,但您无法单击屏幕上的任何位置。作为应用程序挂起时的副作用,它会在 Windows 7.
上从任务栏中消失我中断执行时看到的调用栈如下:
user32.dll!_NtUserGetMessage@16() + 0x15 bytes
user32.dll!_NtUserGetMessage@16() + 0x15 bytes
[Managed to Native Transition]
WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x14 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x80 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0x75 bytes WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox) + 0x17d bytes
PresentationFramework.dll!System.Windows.Window.Show() + 0x5c bytes
PresentationFramework.dll!System.Windows.Window.ShowDialog() + 0x27d bytes
> xxx.dll!xxx.BusyIndicatorImpl(System.Windows.Window window, System.Windows.RoutedEventHandler onLoaded) Line 743 + 0xd bytes C#
正如其他人之前所说,它看起来像是一个死锁,所以我得到了一个转储,其中包含 Visual Studio 和 运行 一些工具,用于查找死锁。这是应用程序拥有的线程的信息 运行:
Debugger Thread ID Managed Thread ID OS Thread ID Thread Object GC Mode Domain Lock Count Apt Exception
0 1 5684 2b2390 Preemptive 9176600 0 STA
6 2 5572 2c7a80 Preemptive 2a82f8 0 MTA (Finalizer)
7 3 3676 2cb828 Preemptive 2a82f8 0 Unknown
11 4 864 7f7d5c0 Preemptive 2a82f8 0 MTA (Threadpool Worker)
15 10 4340 921cdc8 Preemptive 9176600 1 MTA
16 12 1648 9438560 Preemptive 2a82f8 0 MTA (Threadpool Completion Port)
17 14 3380 9001038 Preemptive 2a82f8 0 Unknown (Threadpool Worker)
21 7 5336 9002fe8 Preemptive 2a82f8 0 MTA (Threadpool Worker)
20 5 4120 9003fc0 Preemptive 2a82f8 0 MTA (Threadpool Worker)
25 18 5172 9004508 Preemptive 2a82f8 0 MTA (Threadpool Worker)
27 11 5772 9003a78 Preemptive 2a82f8 0 MTA (Threadpool Worker)
只有一个线程带有应用程序代码(0,通过 ShowDialog 调用管理 1)。其他线程没有应用程序代码,线程 15(托管 10)是唯一具有一些 .Net 代码的线程。
查看线程 15(托管 10),因为它是带锁的线程,我看到以下调用堆栈:
[[HelperMethodFrame_1OBJ] (System.Threading.WaitHandle.WaitMultiple)] System.Threading.WaitHandle.WaitMultiple(System.Threading.WaitHandle[], Int32, Boolean, Boolean)
mscorlib_ni!System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)+92
System_ni!System.Net.TimerThread.ThreadProc()+28f
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+6f
mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+a7
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+16
mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+41
mscorlib_ni!System.Threading.ThreadHelper.ThreadStart()+44
[[GCFrame]]
[[DebuggerU2MCatchHandlerFrame]]
[[ContextTransitionFrame]]
[[DebuggerU2MCatchHandlerFrame]]
这个调用堆栈看起来像是一个等待被解雇的计时器,但也许我对这种解释有误,所以我不知道如何继续。
我可以根据您的要求提供您需要的任何信息。我不是 WinDbg 方面的专家,但我开始能够处理它,因此您也可以要求我获取有关它的信息。
更新:
向应用程序添加一些日志后,我们有以下额外信息:
问题是方法 dispatcher.Invoke(new Action(window.Close));
被调用并且执行时没有抛出异常,但是方法 window.ShowDialog();
没有 return。
我们已尝试使用 Spy++ 和类似工具找到 window,据我所知 window 不存在,但 window.ShowDialog();
一直在执行。
我希望这能让您对正在发生的事情有所了解。
可能是死锁也可能不是,但我们没有足够的信息来开始(例如 Loaded
事件处理程序内部发生了什么,或者在什么上下文中调用了东西)。
您的代码中有一些值得仔细研究的地方:
您似乎没有设置模态对话框的所有者。在最坏的情况下,这可能会导致您的模态对话框显示在背景中,很难或无法关闭。
不清楚您在哪里创建 window。
Loaded
事件只会在您的 window 第一次显示时触发(与Activated
事件相反)。如果您回收 window,则不会触发加载事件,您的后台工作程序也不会启动。
这是一个非常基本的工作 WPF 示例,由一个主 window 和一个后台工作人员和一个模式进度对话框组成。我会尝试从这里开始尝试找出问题所在。
MainWindow.xaml
<Window x:Class="ProgressBarSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="200"
Height="100"
mc:Ignorable="d">
<Grid>
<Button Click="OnButtonClick">Start</Button>
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace ProgressBarSample
{
public partial class MainWindow
{
private BackgroundWorker _backgroundWorker;
private ProgressWindow _progressWindow;
public MainWindow()
{
InitializeComponent();
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
_progressWindow = new ProgressWindow { Owner = this };
_backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
_backgroundWorker.DoWork += OnWorkerDoWork;
_backgroundWorker.RunWorkerCompleted += OnWorkerRunWorkerCompleted;
_backgroundWorker.ProgressChanged += OnWorkerProgressChanged;
_backgroundWorker.RunWorkerAsync();
_progressWindow.ShowDialog();
}
private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
_progressWindow.ProgressValue = e.ProgressPercentage;
}
private void OnWorkerRunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
_progressWindow.Close();
MessageBox.Show("Done");
}
private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
{
int initialValue = 100;
for (int i = 0; i < initialValue; i++)
{
Thread.Sleep(50);
_backgroundWorker.ReportProgress(i);
}
}
}
}
ProgressWindow.xaml
<Window x:Class="ProgressBarSample.ProgressWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Progress"
Width="300"
Height="100"
mc:Ignorable="d">
<Grid>
<ProgressBar Maximum="100" Minimum="0"
Value="{Binding ProgressValue}" />
</Grid>
</Window>
ProgressWindow.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ProgressBarSample
{
public partial class ProgressWindow : INotifyPropertyChanged
{
private double _progressValue;
public ProgressWindow()
{
InitializeComponent();
DataContext = this;
}
public double ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
调用堆栈显示您的对话框处于等待消息的模态消息循环中。它没有被锁定或阻止。
正如 Dirk 指出的那样,Loaded 事件并不总是引发。其来源可能是他建议的两件事之一。
解决方法是在 BusyIndicatorImpl 中显式调用 onLoaded。
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
onLoaded();
window.ShowDialog();
}
或者,您可以 post 它到您的模态对话框。
private void BusyIndicatorImpl(Window window, RoutedEventHandler onLoaded)
{
window.Dispatcher.InvokeAsync(() => onLoaded(window, null));
window.ShowDialog();
}
我在一年后找到了问题的原因,以下代码显示了正在发生的事情的概念证明:
总结(由于实际代码中的竞争条件)有 3 个 windows 具有父子关系 (A-->B-->C) 并且代码正在关闭 B。这在 WPF 应用程序上工作(并且 C 也被关闭)但在 VSTO 加载项中不起作用并挂起,B 永远不会离开 ShowDialog 方法:
为 Office Word 2010 创建一个新的 VSTO 项目并粘贴以下代码(不确定如果您针对不同的 Office 版本会发生什么):
using System.Diagnostics;
using System.Windows;
using System.Windows.Interop;
using Action = System.Action;
namespace WordAddIn1HangTest
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Window window1 = new Window();
window1.Content = "1";
Window window2 = new Window();
window2.Content = "2";
WindowInteropHelper windowInteropHelper1 = new WindowInteropHelper(window1);
WindowInteropHelper windowInteropHelper2 = new WindowInteropHelper(window2);
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
windowInteropHelper1.Owner = Process.GetCurrentProcess().MainWindowHandle;
window1.ShowDialog();
MessageBox.Show("Hello");
}));
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
windowInteropHelper2.Owner = windowInteropHelper1.Handle;
window2.ShowDialog();
}));
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
window1.Close();
}));
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
随着 window1 关闭,您应该会看到消息 "Hello"。相反,您没有看到任何 window 打开并且 ShowDialog 调用挂起。