Потоковая проблема с Telerik RadDocking
Компания, для которой я консультируюсь, предъявляет особые требования бизнеса, чтобы каждый экземпляр определенной Windows WPF ДОЛЖЕН иметь собственный поток пользовательского интерфейса и НЕ использовать общий поток пользовательского интерфейса, созданный.NET Framework при первой загрузке приложения.
С точки зрения кодирования, это легко сделать и хорошо работать, пока не введен элемент управления Telerik RadDocking в xaml. Я скопировал и вставил пример RadDocking Telerik из xaml непосредственно из примера кода, не изменяя его. Когда приложение запускается, оба экземпляра WindowWithTelerikDockingFromExample [похоже] сначала загружаются без проблем, фактически второй экземпляр окна (под названием "Окно в отдельном потоке пользовательского интерфейса...") работает и работает так же, как и "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 через службу поддержки. Следующая выдержка из того, что я им отправил. Это несколько похоже на вопрос, который я первоначально разместил в Stackru, однако я пошел дальше и декомпилировал их библиотеку RadDocking DLL и предложил, что им нужно сделать, чтобы решить эту проблему. Я рад сообщить, что они ответили на вопрос:
У нас есть конкретное бизнес-требование, согласно которому каждый конкретный экземпляр Windows WPF должен иметь свой собственный поток пользовательского интерфейса, в отличие от использования потока пользовательского интерфейса по умолчанию, который создается вместе с приложением. Исходя из нашего опыта, сделать это было относительно легко с точки зрения кодирования, пока мы не внедрили в комбинацию элемент управления Telerik RadDocking. Я приложил zip-файл, содержащий простое решение VS с двумя основными файлами. Одним из них является app.xaml.cs, а другим - окно WindowWithTelerikDockingFromExample.xaml. Xaml в окне был непосредственно скопирован и вставлен из вашего примера стыковки WPF без изменений. Файл app.xaml.cs имеет один метод: OnStartup. Метод OnStartup делает две вещи: 1. Сначала он создает экземпляр Window и устанавливает для этого приложения MainWindow. 2. Затем он создает другой экземпляр окна, но в другом потоке. Два экземпляра окна запускаются без проблем и работают. Это происходит до тех пор, пока пользователь не активирует один из них, а затем активирует другой, а затем снова активирует другой, когда выбрасывается следующее исключение: "Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им".
Мы считаем, что проблема лежит где-то в классе AutoHideArea в Telerik.Windows.Controls.Docking.dll. Мы взяли на себя обязательство использовать ваш бесплатный инструмент Telerik JustDecompile для декомпиляции the Telerik.Windows.Controls.Docking.dll, чтобы попытаться выяснить, где может возникнуть проблема. Метод OnLoaded в AutoHideArea связывает событие "OnApplicationDeactivation" следующим образом:
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));
}
}
}
Если вы будете следовать методу OnApplicationDeactivation, вы заметите, что он вызывает метод "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. Мы рассмотрим исключение для наших будущих выпусков и рассмотрим ваше предложение по исправлению.
Спасибо за ваш отзыв. Я также рад обновить ваши очки Telerik.
С Уважением,
Джордж Телерик
ПОПРОБУЙТЕ НОВЕЙШИЙ ПРОДУКТ TELERIK - АНАЛИТИКА ПРИЛОЖЕНИЙ EQATEC для WPF. Узнайте, какие функции ваши пользователи используют (или не используют) в вашем приложении. Знай свою аудиторию. Нацельтесь лучше. Развивайся с умом.