我所咨询的公司有一个特定的业务要求,即某些Windows的每个实例都必须有自己的UI线程,而不是在首次加载应用程序时共享.NET框架创建的默认UI线程。
从编码的角度来看,在xaml中引入Telerik RadDocking控件之前,这很容易完成,并且工作良好。我直接从示例代码中复制和粘贴了xaml表单telerik的RadDocking示例,而没有修改它。当应用程序启动时,两个WindowWithTelerikDockingFromExample实例似乎一开始都没有问题地加载,实际上是窗口的第二个实例(标题为“单独UI线程上的窗口.”)运行并工作,"MainWindow“也是如此。直到激活第二个窗口,然后激活主窗口,然后切换回第二个窗口,才会引发以下异常:
“调用线程无法访问此对象,因为其他线程拥有该对象。”
定位源
'c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs'. Checksum: MD5 {3e 1e cd 2a 97 89 30 7e c9 1c 28 c2 28 13 aa e9}
The file 'c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs' does not exist.
Looking in script documents for 'c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs'...
Looking in the projects for 'c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs'.
The file was not found in a project.
Looking in directory 'C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\src\atl\'...
Looking in directory 'C:\Program Files\Microsoft Visual Studio 10.0\VC\atlmfc\include\'...
The debug source files settings for the active solution indicate that the debugger will not ask the user to find the file: c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs.
The debugger could not locate the source file 'c:\TB\117\WPF_Scrum\Release_WPF\Sources\Development\Controls\Docking\Docking\Parts\AutoHideArea.cs'.这是我的代码:
App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
this.ShutdownMode = System.Windows.ShutdownMode.OnLastWindowClose;
// Init the application's main window...
var mainWindow = new WindowWithTelerikDockingFromExample();
mainWindow.Title = "Main Window";
this.MainWindow = mainWindow;
mainWindow.Show();
// init another instance of the window with the telerik docking, on a seperate UI thread...
var thread = new Thread(() =>
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
var window2 = new WindowWithTelerikDockingFromExample();
window2.Title = "Window on seperate UI Thread...";
window2.Show();
System.Windows.Threading.Dispatcher.Run();
window2.Closed += (s2, e2) =>
{
window2.Dispatcher.InvokeShutdown();
};
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
base.OnStartup(e);
}
}WindowWithTelerikDockingFromExample.xaml:
<Window x:Class="TelerikDockingThreadIssueExample.WindowWithTelerikDockingFromExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="Window with xaml copy and pasted from Telerik example" Height="300" Width="300">
<Grid>
<telerik:RadDocking BorderThickness="0" Padding="0">
<telerik:RadDocking.DocumentHost>
<telerik:RadSplitContainer>
<telerik:RadPaneGroup>
<telerik:RadDocumentPane Header="Document 1" Title="Document 1" />
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking.DocumentHost>
<telerik:RadSplitContainer InitialPosition="DockedLeft">
<telerik:RadPaneGroup>
<telerik:RadPane Header="Pane Left 1" IsPinned="False">
<TextBlock Text="Pane Left 1" />
</telerik:RadPane>
<telerik:RadPane Header="Pane Left 2" IsPinned="False">
<TextBlock Text="Pane Left 2" />
</telerik:RadPane>
<telerik:RadPane Header="Pane Left 3" IsPinned="False">
<TextBlock Text="Pane Left 3" />
</telerik:RadPane>
<telerik:RadPane Header="Pane Left 4" IsPinned="False">
<TextBlock Text="Pane Left 4" />
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
<telerik:RadSplitContainer InitialPosition="DockedRight">
<telerik:RadPaneGroup>
<telerik:RadPane Header="Pane Right 1" IsPinned="False">
<TextBlock Text="Pane Right 1" />
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
<telerik:RadSplitContainer InitialPosition="DockedBottom">
<telerik:RadPaneGroup>
<telerik:RadPane Header="Pane Bottom 1" IsPinned="False">
<TextBlock Text="Pane Bottom 1" />
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking>
</Grid>
</Window>有什么想法吗?
发布于 2013-10-14 18:00:00
由于公司是Telerik的付费客户,我可以使用他们的帐户通过支持票将信息发送给Telerik。下面是我寄给他们的内容的摘录。这有点类似于我最初在StackOverflow上发布的问题,但是,我更进一步,对他们的RadDocking DLL进行了解压缩,并就他们需要做什么来解决这个问题提出了建议。我很高兴地报告,他们回答了这个问题:
我们有一个特定的业务要求,WPF Windows的特定实例必须每个都有自己的UI线程,而不是使用与应用程序一起创建的默认UI线程加载。从我们的经验来看,从编码的角度来看,这样做相对容易,直到我们将Telerik RadDocking控件引入混合。我已经附加了一个压缩文件,包含两个主文件的简单VS解决方案。其中一个是app.xaml.cs,另一个是名为WindowWithTelerikDockingFromExample.xaml的窗口。窗口中的xaml是从您的WPF对接示例中直接复制和粘贴的,没有任何更改。app.xaml.cs文件有一个方法: OnStartup。OnStartup方法做了两件事: 1.它首先创建一个窗口实例,并将应用程序MainWindow设置为该实例。2.然后,它创建窗口的另一个实例,但在另一个线程下。窗口启动的两个实例,没有问题,并且是可操作的。直到用户激活其中一个,然后激活另一个,然后在引发以下异常时再次激活另一个:“调用线程无法访问该对象,因为另一个线程拥有它。”
我们认为问题就在AutoHideArea类的Telerik.Windows.Controls.Docking.dll中。我们开始使用您的免费Telerik JustDecompile工具来分解theTelerik.Windows.Controls.Docking.dll,以帮助调查问题可能发生的地方。AutoHideArea中的"OnApplicationDeactivated“方法连接”OnApplicationDeactivated“事件,如下所示:
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.isLoaded = true;
base.SelectedIndex = -1;
if (!BrowserInteropHelper.IsBrowserHosted)
{
WeakReference weakReference = new WeakReference(this);
Window window = Window.GetWindow(this);
if (window != null)
{
window.SizeChanged += new SizeChangedEventHandler((objects, SizeChangedEventArgs a) => AutoHideArea.OnWindowEventOccured(weakReference));
window.LocationChanged += new EventHandler((object s, EventArgs a) => AutoHideArea.OnWindowEventOccured(weakReference));
}
if (Application.Current != null)
{
Application.Current.Deactivated += new EventHandler((object s, EventArgs a) => AutoHideArea.OnApplicationDeactivated(weakReference));
}
}
}如果遵循OnApplicationDeactivated方法,您会注意到它调用了"area.CloseImmediately()“方法,如下所示:
private static void OnApplicationDeactivated(WeakReference target)
{
AutoHideArea autoHideArea;
if (target.IsAlive)
{
autoHideArea = target.Target as AutoHideArea;
}
else
{
autoHideArea = null;
}
AutoHideArea area = autoHideArea;
if (area != null)
{
area.CloseImmediately();
}
}如果您一直遵循该方法,您会注意到它设置了base.SelectedIndex = -1,如下所示:
private void CloseImmediately()
{
this.OnLayoutChangeStarted();
base.SelectedIndex = -1;
this.OnLayoutChangeEnded();
}我们建议将方法更改为如下所示:
private void CloseImmediately()
{
if (!System.Windows.Threading.Dispatcher.CurrentDispatcher.CheckAccess())
{
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(CloseImmediately),null);
return;
}
this.OnLayoutChangeStarted();
base.SelectedIndex = -1;
this.OnLayoutChangeEnded();
}以下是Telerik的回复:
感谢您在RadDocking中报告此异常。该控件没有在多线程环境中进行测试,我们也没有意识到这样的问题。这个问题被记录在我们的坑里-- http://www.telerik.com/support/pits.aspx#/public/wpf/15920。我们将调查我们即将发布的版本的例外情况,我们将考虑您对修复的建议。 感谢您的反馈。我也很高兴更新您的Telerik要点。 致以敬意, 乔治·泰勒克 尝试TELERIK的最新产品- EQATEC应用分析为WPF。了解用户在应用程序中使用(或不使用)什么功能。了解你的观众。瞄准目标更好。明智地发展。
https://stackoverflow.com/questions/19237198
复制相似问题