在后台进程访问一个WPF的FlowDocument

在后台进程访问一个WPF的FlowDocument

本文介绍了在后台进程访问一个WPF的FlowDocument的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在后台访问一个WPF的FlowDocument

我的问题涉及到访问UI对象,在WPF的背景。我已经看到了几十个样本的应用程序,这一切都很简单,容易执行,而且其中95%的告诉你如何显示进度条。这并不完全是我想要......

My question relates to accessing a UI object, in the background in WPF. I have seen dozens of sample apps, that all are simple, easy to follow, and 95% of which tell you how display a progress bar. That’s not quite what I want…..

我的问题是这样的:我想在一个RichTextBox访问的FlowDocument执行一项艰巨的任务(或许多长期的任务)。确切的任务是不相关的,但一个实例可能是通过要扫描的文档,并且计数的次数会出现一个特定字,或多少红色字符有......。在很长的文件,这些可能是相当耗时的任务,如果做在前面,就占用了相当大的用户界面,并使其反应迟钝。我只是想解析的FlowDocument;我不想做任何更改。

My problem is this: I want to perform a long task (or many long tasks) by accessing a FlowDocument in a RichTextBox. The exact task isn’t relevant here, but an example might be to scan through the document, and count the number of times a particular word appears, or how many red characters there are……. In a long document, those could be fairly time consuming tasks, and if done in the foreground, would tie up the UI considerably and make it unresponsive. I only want to parse the FlowDocument; I do not want to make any changes to it.

这就是我想要做这样的事情。显而易见的解决方案就是这样做的背景,但问题是......怎么样?我已经实现的出现什么的是一个答案,但它只是不感觉不对对我来说,这就是为什么我在这里寻求帮助。

So that’s the kind of thing I’m trying to do. The obvious solution is to do it in the background, but the question is…how? I have achieved what appears to be an answer, but it just doesn’t "feel right" to me, which is why I am here asking for help.

我的解决方案

我的解决方案,它遵循使用一个BackgroundWorker这就要求用户界面对象的调度,以确保正确的线程访问...它似乎来完成这项工作。但是,不是吗?.............. 我已大大缩写我的解决方案,以方便(我希望)跟着我做什么...。

My "solution" which follows uses a BackgroundWorker which calls the UI object’s Dispatcher to ensure the right thread is accessed…and it "appears" to do the job. But does it?.............. I have considerably abbreviated my "solution" to make it easy (I hope) to follow what I am doing….

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的,pretty的多ALL长时间运行的任务由UI对象的调度处理。所以BackgroundWorker的实际上并没有做任何工作。这就是我所关心的部分;我看不出我得到什么,如果分派器捆绑起来做所有的工作

Although that seems to work, the problem as I see it is that apart from triggering the task with the BackgroundWorker, pretty much ALL the long running task is handled by the UI object’s Dispatcher. So the BackgroundWorker doesn’t actually do any work. That’s the part that concerns me; I can’t see how I am gaining anything, if the Dispatcher is tied up doing all the work

选项2

因此​​,它似乎更符合逻辑,我认为我将是更好的扭转这一局面一点点,并设置分派器的代表,以指向实例和启动BackgroundWorker的(我的想法是次的发送器线程然后将拥有的BackgroundWorker的线程),并完成所有的工作中的BackgroundWorker的DoWork的事件。这感觉的权利......。

So, it seemed more logical to me that I would be better to "twist this around a little" and set the Dispatcher’s delegate to point to the sub that instantiates and starts the BackGroundWorker (my thinking being that the Dispatcher thread would then own the BackgroundWorker’s thread), and to do all the work in the BackgroundWorker’s DoWork event. That "felt" right….

所以,我想这一点,而不是:

So I tried this, instead :

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

对我来说,这一切似乎更符合逻辑。我推测分派器将自己BackgroundWorker的,这反过来会做所有的长时间工作,一切都将在UI线程上。嗯...。所以很多逻辑思维......(通常是致命的与WPF!)......这不。它崩溃与通常的不同的线程错误。那么,什么似乎在第二个想法是一个更优雅的解决方案,竟然是个失败者!

To me, that all seemed far more logical. I surmised the Dispatcher would own the BackgroundWorker, which in turn would do all the long work, and everything would be on the UI thread. Well….so much for logical thinking…(usually fatal with WPF!)....It doesn’t. It crashes with the usual "Different Thread" error. So, what seemed on second thoughts to be a far more elegant solution, turned out to be a loser!

然后是我的问题如下:

  1. 是我的解决方案的解决方案,还是不?
  2. 我在哪里去了?
  3. 如何能在解决方案加以改进,使调度程序不依赖与长期任务......。其中也正是我试图避免这种情况?

另一个问题。请注意,我用的FlowDocument的调度,使这项工作。如果我用了System.Windows.Threading.Dispatcher.CurrentDispatcher代替,则代表子(DoTheTimeConsumingTask)不被调用等等 - 所有意图和目的 - 什么也没有发生。有人可以解释为什么不请?

A further question. Please note that I had to use the FlowDocument’s Dispatcher to make this work. If I used the System.Windows.Threading.Dispatcher.CurrentDispatcher instead, then the Delegate sub (DoTheTimeConsumingTask ) does not get invoked so – to all intents and purposes – nothing happens. Can someone explain why not, please?

我不是来给您呼叫的第一个端口。我试过几十种选择,并没有发现任何东西,感觉完全正确的(除了我的第二个选项不起作用大声笑),所以我问了一些指导,请。

I have not come to you as a first port of call. I’ve tried dozens of options, and haven’t found anything yet that feels totally right (apart from my second option that doesn’t work LOL) so I’m asking for some guidance, please.

推荐答案

你面临的主要问题是,的FlowDocument 派生自 DispatcherObject的,等你搞它的调度来访问它。你试图做这件事情一切都会采取将项目的形式在调度的工作队列,等待它避开执行它们。其中,如果调度是一个在处理用户界面,会导致你想要什么,以避免:当调度正在执行的工作项目,所有剩余的鼠标点击和击键堆放在调度的工作队列,并且UI将unresponsible。

The primary issue you're facing is that FlowDocument derives from DispatcherObject, and so you have to engage its Dispatcher to access it. Everything you try to do with this thing is going to take the form of putting items in the Dispatcher's work queue and waiting for it to get around to executing them. Which, if the Dispatcher is the one that's handling the UI, is going to result in exactly what you're trying to avoid: while the Dispatcher is executing your work item, all the remaining mouse clicks and keystrokes are piling up in the Dispatcher's work queue, and the UI will be unresponsible.

你能得到什么出了的FlowDocument 是一个 DispatcherObject的的是,它的内容不能同时你的长期变化-running任务处理它。这些鼠标点击和击键队列中可能会改变它,一旦你的任务已经完成,但同时它的运行,他们只是积累。这实际上是比较重要;如果你的的可以使用调度,你会遇到在那里的东西在UI改方案,以绕过的FlowDocument ,而你的任务正在运行。然后,你将有什么俗称问题。

What you get out of the FlowDocument being a DispatcherObject is that its content can't change while your long-running task is processing it. Those mouse clicks and keystrokes in the queue may change it once your task is done, but while it's running, they're just accumulating. This is actually kind of important; if you were able to get around using the Dispatcher, you'd face the scenario where something in the UI changed the FlowDocument while your task was running. Then you would have what are commonly known as "problems."

即使你能克隆的FlowDocument ,然后从UI的调度克隆,它仍然是一个 DispatcherObject的,你还是会遇到同样的问题,试图同时对其执行多个任务;你有你的序列访问它,或者看你的后台线程崩溃的选项。

Even if you could clone the FlowDocument and disconnect the clone from the UI's dispatcher, it would still be a DispatcherObject, and you'd still run into the same problem trying to execute multiple tasks on it simultaneously; you'd have the options of serializing your access to it or watching your background thread crash.

要解决这个问题,你需要做的是做出某种非 - DispatcherObject的冻结快照的FlowDocument 。然后运行的快照你的任务。这样一来,如果UI,是活的,改变了的FlowDocument ,而你的任务时,它不会弄乱你的游戏。

To get around this, what you need to do is make some kind of non-DispatcherObject frozen snapshot of the FlowDocument. Then run your task on the snapshot. That way, if the UI, being live, changes the FlowDocument while your task is running, it won't mess up your game.

我会怎么做:用的XamlWriter 和序列化的FlowDocument 的XDocument 。序列化任务涉及调度,但一旦它的完成,你可以根据你想运行数据,因为许多古怪的并行分析,并没有在UI将会影响着它。 (也曾经是一个的XDocument 您使用XPath,这是pretty的好锤子查询它,只要你的问题实际上是钉子。)

What I'd do: use a XamlWriter and serialize the FlowDocument into an XDocument. The serialization task involves the Dispatcher, but once it's done, you can run as many wacky parallel analyses of the data as you want and nothing in the UI will affect it. (Also once it's an XDocument you query it with XPath, which is a pretty good hammer, so long as your problems are actually nails.)

这篇关于在后台进程访问一个WPF的FlowDocument的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 22:56