点击主窗口上的按钮时,我打开了一个侧窗。这个侧窗口是在与主窗口不同的线程上创建的。
当我第一次点击按钮时,一切正常。然后我关闭了侧窗。现在当我点击按钮第二次时,它会抛出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元素的所有调用来说,这是非常繁琐的,并且实际上并没有解决问题的原因。
非常感谢!
答案 0 :(得分:2)
我猜测罪魁祸首是GraphUtilities.BackgroundGradient
,但你没有列出GraphUtilities
类。画笔是freezable个对象。
来自MSDN上的Freezable Objects Overview:
冻结的Freezable也可以在线程之间共享,而解冻的Freezable则不能。[/ p>
因此,第一次运行时,该画笔与监视窗口线程相关联。下次打开该窗口时,它是一个新线程。如果要从其他线程中使用它,则必须在画笔上调用Freeze方法。
答案 1 :(得分:1)
WPF的前提条件是所有UI资源都由单个线程(UI线程)创建并拥有。许多事情取决于假设的有效性。如果你不遵守这一点,那么所有的赌注都会被取消。
您不需要在后台线程上创建等待指示符UI,以便在后台线程上使用它。大型应用程序在主线程上创建所有UI。后台线程上发生长时间运行的活动。到时候,控制marshalls进入UI线程的时间足够长,以便更新UI。
我建议您read this carefully继续操作。
所以你得到了你想要的答案。 可以完成的事实并不是一个好主意。在软件设计中,聪明很少与智能相同。