2013-10-08 40 views
1

我正在咨询的公司有特定的业务要求,某些WPF Windows的每个实例必须拥有自己的UI线程,并且不能在应用程序是共享由.NET Framework创建的默认UI线程第一次加载。Telerik RadDocking的线程问题

从编码的角度来看,这很容易实现,并且运行良好,直到在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> 

任何想法?

回答

1

由于该公司是Telerik的付费客户,因此我可以通过他们的帐户通过支持凭单向Telerik发送信息。以下是我发给他们的摘录。这有点类似于我最初在StackOverflow上发布的问题,但是,我更进了一步,并对其RadDocking DLL进行了反编译,并提出了他们需要做什么来解决此问题的建议。我很高兴地报告说,他们回答了这个问题:

我们有一个具体的业务要求,即WPF的Windows的具体实例必须各自有自己的UI线程,而不是使用所创建的默认UI线程与应用程序加载。根据我们的经验,从编码角度来看,这样做相对容易,直到我们将Telerik RadDocking控件引入混合中。 我附上了一个包含简单VS解决方案的zip文件,其中包含两个主要文件。其中之一是app.xaml.cs,另一个是名为WindowWithTelerikDockingFromExample.xaml的Window。窗口中的xaml直接从您的WPF Docking示例中复制并粘贴,无需更改。 app.xaml.cs文件有一个方法:OnStartup。 OnStartup方法执行两件事: 1.它首先创建一个Window实例并将应用程序MainWindow设置为该实例。 2.然后它创建另一个窗口实例,但在不同的线程下。 窗口启动的两个实例没有问题且可操作。直到用户激活它们中的一个,然后激活另一个,然后在抛出以下异常时再次激活另一个异常: “调用线程无法访问此对象,因为不同的线程拥有它。”

我们认为问题出在Telerik.Windows.Controls.Docking.dll的AutoHideArea类中。我们自己决定使用免费的Telerik JustDecompile工具来反编译Telerik.Windows.Controls.Docking.dll,以帮助调查问题的发生位置。在AutoHideArea线的“装载的”方法一“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。 控件未在多线程环境中测试,我们不知道此类问题的 。这个问题记录在我们的PITS中 - http://www.telerik.com/support/pits.aspx#/public/wpf/15920。我们将 查看我们即将发布的版本的例外情况,我们将考虑您的修复建议 。

感谢您的反馈。我很高兴更新您的Telerik积分 。

问候,

乔治 Telerik的

尝试Telerik的最新产品 - EQATEC应用ANALYTICS为WPF。 了解您的用户在您的应用程序中使用(或不使用)的功能。 了解你的观众。更好地瞄准它。明智地发展。

+0

不幸的是,该错误仍然是开放的,“不计划”,所以我们不会在2013年Q3 SP1修复。 – Amer