В нашем приложении у нас есть возможность печатать отчеты. Для этих отчетов мы используем такие классы, как FlowDocument, Canvas и некоторые другие элементы управления, такие как ярлыки. Поскольку элементы управления должны создаваться только в основном потоке, приложения показывают окно уведомлений, и пользователь должен ждать. Отчет может содержать до 100 страниц, поэтому это может занять несколько минут. И пользователь не может ничего сделать с приложением.
Есть ли вообще выход из этой ситуации?
Как я выяснил, проблема заключается не в том, чтобы создать документ в новом потоке. Я могу сделать это. Но после этого я не могу показать документ, потому что он не был создан основным потоком. Если я создам его в основном потоке - это то, что я делаю - он отлично работает. Но тогда приложение блокируется при создании отчета.
Есть ли способ передать право собственности на объект на другой поток? Особенно с пользовательскими интерфейсами, такими как Canvas? Как я уже говорил, я могу создать отчет в дополнительном потоке, но приложение отказывается отображать его. Элементы управления, не созданные/принадлежащие основному потоку, не отображаются.
Вы не можете. Любой элемент управления пользовательского интерфейса должен управляться Dispatcher
Если вам абсолютно необходимо создавать элементы управления из другого потока, вам нужно вызвать их создание/добавление через главный диспетчер, синхронизацию или асинхронный режим в зависимости от ваших вариантов
Быстрый пример для асинхронного вызова (изменить BeginInvoke
к Invoke
для синхронизации, и играть с DispatcherPriority
соответственно)
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Your control creation here
}), DispatcherPriority.Background);
Раньше я работал над подобной проблемой: Global DockManager
, динамическая обработка DockManager
содержимого, мне пришлось создавать свои элементы управления из Диспетчера, чтобы иметь возможность фактически добавлять их в мой DockManager
. Удостоверьтесь, что вы избегаете каких-либо условий гонки с этими вызовами, и вы должны быть в порядке
Я бы посоветовал создать новый поток с диспетчером. В этом отдельном потоке вы можете создать дерево элементов и показать его в отдельном окне, которое не зависит от вашего потока пользовательского интерфейса главного окна. Тогда пользователь может выбрать печать или нет.
Чтобы создать новый поток с инициализированным диспетчером WPF, вы можете использовать следующий код:
public class DispatcherBuilder : IBuilder<Dispatcher>
{
public Dispatcher Build()
{
Dispatcher dispatcher = null;
var manualResetEvent = new ManualResetEvent(false);
var thread = new Thread(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
var synchronizationContext = new DispatcherSynchronizationContext(dispatcher);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
manualResetEvent.Set();
Dispatcher.Run();
});
thread.Start();
manualResetEvent.WaitOne();
manualResetEvent.Dispose();
return dispatcher;
}
}