我最近编写了我的第一个VB WPF应用程序,用Quickbooks执行一些自动化任务。这是一个挑战,因为我以前没有VB或Quickbooks的经验。除此之外,我还不了解线程模型以及我在此过程中可能遇到的问题。我基本上在1个线程中编写了整个应用程序... UI线程。我已经明白,虽然这“有效”,但它会导致长时间运行的任务停止UI线程的问题。我开始研究多线程,现在遇到了问题,因为我使用的是UI绑定 ObservableCollections ,它本身不支持在辅助线程上更新OC。我发现了一些关于这个主题的帖子,比如Mirality的this one,我会研究下一个。
在我对整个应用程序进行大量重写之前,我希望有一种更简单的方法来完成我正在尝试做的事情。我想查看多线程的唯一原因是因为我想显示一个“正在加载...”窗口,有或没有特定于任务的消息(即“从Quickbooks获取所有供应商......”等),具体取决于什么更容易。如果有一种简单的方法可以快速“任务切换”来更新UI线程,然后继续执行可能解决我的问题的其他任务,但我不知道该怎么做。
这是一个简单的例子来说明问题。我有一个自定义应用程序类,它在 AppStartup 方法中执行启动任务。
Partial Public Class MyApp
Inherits System.Windows.Application
Private _qbclient As QBClient
Private _vendorListWindow As VendorListWindow
Private _splashScreen As StartupSplash
'======================================================
' AppStartup Method
' Initialize the application
'======================================================
Public Sub AppStartup()
_vendorListWindow = New VendorListWindow()
_vendorListWindow.IsEnabled = False
_splashScreen = New StartupSplash()
_splashScreen.Owner = _vendorListWindow
_splashScreen.Show()
_splashScreen.progressMessage.Content = "Downloading all items from Quickbooks..."
Me.GetQBClient().GetAllItems()
_splashScreen.progressMessage.Content = "Downloading all vendors from Quickbooks..."
Me.GetQBClient().GetAllVendors()
_splashScreen.progressMessage.Content = "Locating vendors with items..."
Me.GetQBClient().FlagVendorsWithItems()
_splashScreen.progressMessage.Content = "Adding list filters..."
_vendorListWindow.AddListFilters()
_splashScreen.Close()
_vendorListWindow.Show()
_vendorListWindow.IsEnabled = True
End Sub
End Class
在上面的示例中, StartupSplash 只是一个基本窗口,没有用于启动启动画面的框架。在应用程序的其他时间,我有另一个基本的加载窗口,基本上做同样的事情,但看起来略有不同。
在任何一种情况下,我都会看到启动画面,但 progressMessage 文本块的内容永远不会更新。显然,这是因为UI线程忙于我要求它做的任务。在所有情况下,我都会在调用下一个任务之前从长时间运行的任务返回,因此有足够的时间将更新推送到UI然后继续。这甚至是可能的,还是我必须承担多线程应用程序的艰巨任务?
谢谢!
答案 0 :(得分:1)
在C#中,但不应该太难翻译...
请注意,Thread.Sleep
函数可模拟您长时间运行的任务。
此外,使用TPL,您不必将它们链接在一起。您可以一次性单独开始每个长时间运行(如果它们不相互依赖)。
using System.Threading.Tasks;
txtProgress.Text = "Long-run 1";
Task.Factory
.StartNew(() => Thread.Sleep(2000))
.ContinueWith(
task => txtProgress.Text = "Long-run 2", TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(task => Thread.Sleep(2000))
.ContinueWith(
task => txtProgress.Text = "Long-run 3", TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(task => Thread.Sleep(2000))
.ContinueWith(task => txtProgress.Text = "Done.", TaskScheduler.FromCurrentSynchronizationContext())
;
答案 1 :(得分:0)
如果您了解Windows窗体,您可能知道DoEvents()方法,它通常处理消息,以便您的UI可以不时更新。 WPF没有这个,但有一个解决方法(从C#转换,希望这有效):
Private Sub DoEvents()
Dim f As New DispatcherFrame()
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, DirectCast(Function(arg As Object) Do
Dim fr As DispatcherFrame = TryCast(arg, DispatcherFrame)
fr.[Continue] = [True]
End Function, SendOrPostCallback), f)
Dispatcher.PushFrame(frame)
End Sub
您可以在操作之间调用此方法,UI应处理事件并使用挂起的更改进行更新。但是,在获取数据时,这仍然会使您的UI无响应。但是因为它是一个闪屏,这对你来说可能是可以接受的。
答案 2 :(得分:0)
经过多次在线搜索和头部刮擦,我终于解决了它。最后这很简单。您需要做的就是将 System.Windows.Forms DLL包含到应用程序引用中,然后调用内置的 System.Windows.Forms.Application.DoEvents()方法。关于这一切的令人沮丧的部分是关于如何做事的好的文档很少。 MSDN库充满了关于可用的技术信息,但没有那么多如何使用它。希望这可以帮助其他需要类似功能的人。
以下是我上面的代码段,修改为与 System.Windows.Forms
一起使用Imports System.Windows.Forms 'This must be included in the assembly references...doesn't work otherwise
Partial Public Class MyApp
Inherits System.Windows.Application
Private _qbclient As QBClient
Private _vendorListWindow As VendorListWindow
Private _splashScreen As StartupSplash
'======================================================
' AppStartup Method
' Initialize the application
'======================================================
Public Sub AppStartup()
_vendorListWindow = New VendorListWindow()
_vendorListWindow.IsEnabled = False
_splashScreen = New StartupSplash()
'_splashScreen.Owner = _vendorListWindow
_splashScreen.Show()
_splashScreen.progressMessage.Content = "Downloading all items from Quickbooks..."
Me.GetQBClient().GetAllItems()
Forms.Application.DoEvents()
System.Threading.Thread.Sleep(100) 'This is here so that the text shows up even if the step is really quick
_splashScreen.progressMessage.Content = "Downloading all vendors from Quickbooks..."
Me.GetQBClient().GetAllVendors()
Forms.Application.DoEvents()
System.Threading.Thread.Sleep(100) 'This is here so that the text shows up even if the step is really quick
_splashScreen.progressMessage.Content = "Locating vendors with items..."
Me.GetQBClient().FlagVendorsWithItems()
Forms.Application.DoEvents()
System.Threading.Thread.Sleep(100) 'This is here so that the text shows up even if the step is really quick
_splashScreen.progressMessage.Content = "Adding list filters..."
_vendorListWindow.AddListFilters()
_splashScreen.Close()
_vendorListWindow.Show()
_vendorListWindow.IsEnabled = True
End Sub
End Class