单击主窗口上的按钮时,我会打开一个侧窗口。此侧窗口是在与主窗口不同的线程上创建的。
当我第一次单击按钮时,一切正常。然后我关闭侧窗。现在,当我第二次单击该按钮时,它会抛出InvalidOperationException
.
当侧窗关闭时,它和它运行的线程都被丢弃,每次打开时,都会创建一个全新的线程,从而创建一个全新的窗口。在窗口的最低子元素的构造函数中引发异常(即在构造窗口时第一次访问 UI 对象)。
这就是发生的事情。按钮单击的处理程序:
public partial class MainWindow : Window
{
private void OnVariableMonitoringButtonClick(object sender, RoutedEventArgs e)
{
if (monitoringWindow == null)
{
Cursor = Cursors.Wait;
monitoringWindow = new MonitoringWindowWrapper();
monitoringWindow.Loaded += OnMonitoringWindowLoaded;
monitoringWindow.Closed += OnMonitoringWindowClosed;
monitoringWindow.Start(); // <---
}
else
{
monitoringWindow.Activate();
}
e.Handled = true;
}
void OnMonitoringWindowLoaded(object sender, EventArgs e)
{
Dispatcher.Invoke(new Action(() => Cursor = Cursors.Arrow));
}
void OnMonitoringWindowClosed(object sender, EventArgs e)
{
monitoringWindow = null;
Dispatcher.Invoke(new Action(() => Cursor = Cursors.Arrow));
}
}
该类MonitoringWindowWrapper
仅将类的相关方法包装MonitoringWindow
在Dispatcher
调用中以处理跨线程调用。
代码进入MonitoringWindowWrapper.Start()
方法:
partial class MonitoringWindowWrapper
{
public void Start()
{
// Construct the window in a parallel thread
Thread windowThread = new Thread(LoadMonitoringWindow);
windowThread.Name = "Monitoring Window Thread";
windowThread.IsBackground = true;
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.Start(); // <---
}
private void LoadMonitoringWindow()
{
try
{
// Construct and set up the window
monitoringWindow = new MonitoringWindow(); // <---
monitoringWindow.Loaded += OnMonitoringWindowLoaded;
monitoringWindow.Closed += OnMonitoringWindowClosed;
monitoringWindow.Show();
// Start window message pump on this thread
System.Windows.Threading.Dispatcher.Run();
}
catch (Exception e)
{
// Catch any exceptions in this window to save the application from crashing
ErrorMessasgeBox.Show(e.Message);
if (Closed != null) Closed(this, new EventArgs());
}
}
}
然后代码进入MonitoringWindow.MonitoringWindow()
构造函数并过滤到窗口中最低的子元素:
public partial class MonitoringWindow : Window
{
public MonitoringWindow()
{
InitializeComponent(); // <---
// ...
}
}
一直到:
public partial class LineGraph : UserControl, IGraph, ISubGraph
{
public LineGraph()
{
InitializeComponent();
Brush b = GraphUtilities.BackgroundGradient;
Background = b; // EXCEPTION THROWN AT THIS LINE
BorderBrush = GraphUtilities.Border;
}
}
异常调用堆栈可以让我们深入了解抛出异常的位置:
System.InvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The calling thread cannot access this object because a different thread owns it.
Source=WindowsBase
StackTrace:
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Freezable.ReadPreamble()
at System.Windows.Media.GradientStopCollection.OnInheritanceContextChangedCore(EventArgs args)
at System.Windows.DependencyObject.OnInheritanceContextChanged(EventArgs args)
at System.Windows.DependencyObject.OnInheritanceContextChanged(EventArgs args)
at System.Windows.Freezable.AddInheritanceContext(DependencyObject context, DependencyProperty property)
at System.Windows.DependencyObject.ProvideSelfAsInheritanceContext(DependencyObject doValue, DependencyProperty dp)
at System.Windows.DependencyObject.ProvideSelfAsInheritanceContext(Object value, DependencyProperty dp)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at System.Windows.Controls.Control.set_Background(Brush value)
at Graphing.LineGraph..ctor() in z:\Documents\Projects\Software\Serial\SerialWindows\Graphing\LineGraph\LineGraph.xaml.cs:line 28
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
我能想到的唯一原因如下:
1. 在另一个线程上创建的对象的Background
属性绑定到该LineGraph
对象的属性?但是,我的应用程序中没有任何这样的绑定(至少是明确的),如果是这种情况,那么为什么它第一次工作?
2. 该Background
属性不知何故不归LineGraph
对象所有?但这没有任何意义。
3. 构造函数没有在创建它正在构造的对象的线程上执行?这又没有任何意义,Visual Studio 调试器说它正在“监视窗口线程”线程上运行。
我该如何解决这个问题?
我可以使用Dispatcher
来设置背景,但这对于该类和所有其他类中的所有 UI 元素的调用来说都是非常乏味的,而且它实际上并没有解决问题的原因。
非常感谢你!