Доступ к WPF FlowDocument в BackGround
Мой вопрос касается доступа к объекту пользовательского интерфейса в фоновом режиме в WPF.Я видел десятки примеров приложений, все они просты, за ними легко следить, и 95% из них рассказывают, как отображать индикатор выполнения.Это не совсем то, что я хочу… ..
Моя проблема заключается в следующем: я хочу выполнить длинную задачу (или много длинных задач), используя FlowDocument в RichTextBox.Точная задача здесь не имеет значения, но примером может быть сканирование документа и подсчет количества появлений конкретного слова или количества красных символов …….В длинном документе это может быть довольно трудоемким заданием, и, если оно будет выполнено на переднем плане, значительно сократит интерфейс и сделает его не отвечающим.Я только хочу разобрать FlowDocument;Я не хочу вносить в него какие-либо изменения.
Так вот, что я пытаюсь сделать.Очевидное решение - сделать это в фоновом режиме, но вопрос в том ... как?Я достиг того, что кажется ответом, но мне это просто «не кажется правильным», поэтому я здесь и прошу помощи.
Мой «Решение »
Мое следующее« решение »использует BackgroundWorker, который вызывает Диспетчер объекта пользовательского интерфейса, чтобы обеспечить доступ к нужному потоку… и он« появляется »для выполнения работы.Но так ли это? .............. Я значительно сократил свое «решение», чтобы облегчить (надеюсь) следить за тем, что я делаю….
WithEvents worker As BackgroundWorker
Private Delegate Sub DelegateSub()
Private theDocument As FlowDocument
''' <summary>
''' Triggers the background task. Can call from anywhere in main code blocks
''' </summary>
Private Sub StartTheBackgroundTask()
worker = New BackgroundWorker
worker.RunWorkerAsync()
End Sub
''' <summary>
''' In the background, hands the job over to the UI object's Dispatcher
''' </summary>
Private Sub HandleWorkerDoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles worker.DoWork
Dim priority As System.Windows.Threading.DispatcherPriority
Dim theLongRunningTask As DelegateSub
'(1) Define a delegate for the Dispatcher to work with
theLongRunningTask = New DelegateSub(AddressOf DoTheTimeConsumingTask)
'(2) Set Dispatcher priority as required
priority = System.Windows.Threading.DispatcherPriority.Background
'(3) Add the job to the FlowDocument's Dispatcher's tasks
theDocument.Dispatcher.BeginInvoke(theLongRunningTask, priority)
End Sub
''' <summary>
''' Sub whose logic accesses, but does not change, the UI object
''' </summary>
Private Sub DoTheTimeConsumingTask()
'For example......
For Each bl As Block In theDocument.Blocks
'......do something
Next
End Sub
Хотя это, кажется, работает, проблема, как я вижу, состоит в том, что помимо запуска задачи с помощью BackgroundWorker, почти ВСЕ долго выполняемая задача обрабатывается диспетчером объекта пользовательского интерфейса.Так что BackgroundWorker фактически не выполняет никакой работы.Это та часть, которая касается меня;Я не вижу, как я что-то получаю, если Диспетчер связан, выполняя всю работу
Вариант 2
Итак, мне показалось более логичным, чтоЯ бы лучше «немного перевернул это» и установил, что делегат Dispatcher указывает на подпрограмму, которая создает и запускает BackGroundWorker (я думаю, что поток Dispatcher будет тогда принадлежать потоку BackgroundWorker), и выполнять всю работув событии BackgroundWorker's DoWork.Это «казалось» правильным….
Поэтому я попробовал это вместо этого:
WithEvents worker As BackgroundWorker
Private Delegate Sub DelegateSub()
Private theDocument As FlowDocument
''' <summary>
''' Triggers the background task. Can call from anywhere in main code blocks
''' </summary>
Private Sub StartTheBackgroundTask()
Dim priority As System.Windows.Threading.DispatcherPriority
Dim theTask As DelegateSub
'(1) Define a delegate for the Dispatcher to work with
theTask = New DelegateSub(AddressOf RunWorker)
'(2) Set Dispatcher priority as required
priority = System.Windows.Threading.DispatcherPriority.Normal
'(3) Add the job to the Dispatcher's tasks
theDocument.Dispatcher.BeginInvoke(theTask, priority)
End Sub
''' <summary>
''' Creates and starts a new BackGroundWorker object
''' </summary>
Private Sub RunWorker()
Worker = New BackgroundWorker
Worker.RunWorkerAsync()
End Sub
''' <summary>
''' Does the long task in the DoWork event
''' </summary>
Private Sub HandleWorkerDoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles worker.DoWork
DoTheTimeConsumingTask()
End Sub
''' <summary>
''' Sub whose logic accesses, but does not change, the UI object
''' </summary>
Private Sub DoTheTimeConsumingTask()
'For example......
For Each bl As Block In theDocument.Blocks
'......do something
Next
End Sub
Мне все это казалось гораздо более логичным.Я предположил, что Dispatcher будет владельцем BackgroundWorker, который, в свою очередь, будет выполнять всю долгую работу, и все будет в потоке пользовательского интерфейса.Ну ... так много для логического мышления ... (обычно фатально для WPF!) .... Это не так.Вылетает с обычной ошибкой «Другой поток».Итак, то, что казалось на первый взгляд гораздо более элегантным решением, оказалось проигравшим!
Мои вопросы следующие:
- Является ли мое «решение» aрешение или нет?
- Куда я иду не так?
- Как можно улучшить «решение», чтобы Диспетчер не связывался с долгой задачей…. Какова именно ситуацияЯ пытался избежать?
Еще один вопрос.Обратите внимание, что мне пришлось использовать диспетчер FlowDocument, чтобы сделать эту работу.Если бы вместо этого я использовал System.Windows.Threading.Dispatcher.CurrentDispatcher, то подпункт Делегат (DoTheTimeConsumingTask) не вызывался так - во всех смыслах и целях - ничего не происходит.Может кто-нибудь объяснить, почему нет, пожалуйста?
Я не пришел к вам в качестве первого порта захода.Я перепробовал десятки вариантов и пока не нашел ничего подходящего (кроме моего второго варианта, который не работает с LOL), поэтому прошу совета, пожалуйста.