Потоковая проблема с 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. Узнайте, какие функции ваши пользователи используют (или не используют) в вашем приложении. Знай свою аудиторию. Нацельтесь лучше. Развивайся с умом.

Другие вопросы по тегам