我是WPF的新手,也是WPF中的线程,并且在我的程序中成功实现了bing maps控件。问题是,控件在打开时需要很长时间才能加载并减慢程序本身的速度。加载时间从大约2秒增加到大约20秒,这是不可接受的。
我的想法是,将bing maps控件加载到一个单独的线程中,从而不会降低性能。我一直试图这样做,但我的UI一直被地图控件的加载过程阻止。
以下是使用Dispatcher的示例:
private init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background thread: I would like to load the map here
Map map = new Map();
map.CredentialsProvider = providerKey;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI thread
// How do I assign the loaded Map to this thread?
mapElementOnUI = map;
mapPanel.Children.Add(mapElementOnUI);
}));
}
));
thread.Start();
//Load the rest of the GUI here
}
如果我像处理线程那样得到InvalidOperationException(没有STA-Thread)。如果我将代码更改为以下内容,我的UI会在加载地图控件时阻止:
private init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background thread
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI thread
mapElementOnUI = new Map();
mapElementOnUI.CredentialsProvider = providerKey;
mapPanel.Children.Add(mapElementOnUI);
}));
}
));
thread.Start();
//Load the rest of the GUI here
}
我也一直试图通过await和async实现解决方案但没有成功。是否可以在单独的线程中加载maps控件?有人可以用一段代码帮我解决这个问题吗?非常感谢!
答案 0 :(得分:1)
好吧,在你的第二个例子中,你正在做的就是启动另一个线程,立即切换回UI线程来创建一个控件,这就是你的UI阻塞的原因。
至于你的第一个例子,你不能在一个线程上创建一个控件并在另一个线程上使用它(这不是你得到的例外。你需要在调用Start之前调用SetApartmentState)。因此,您无法在后台线程上创建地图控件,然后将其加载到主线程上创建的窗口中。这可以通过框架来防止。
您可以做的是在自己的UI线程上创建一个单独的窗口,并将地图控件加载到该窗口中。这会阻止您的主应用程序窗口阻塞,但它需要您管理第二个应用程序窗口。此外,如果您希望主应用程序线程中的对象和地图控制线程中的对象相互交互,那么您必须在两端执行一堆额外的工作来处理跨线程调用。您可以阅读此方法here.
至于地图控件本身,我不熟悉它,所以我不知道是否有其他方法来处理或推迟加载而不冻结你的UI。
答案 1 :(得分:1)
好的,我现在解决了这个问题。 @ user1100269给出了一个很好的提示,解决了我遇到的异常:thread.SetApartmentState.STA。此外,我不得不在后台线程中创建一个新的Map实例,而不是在任何地方使用它 - 我没有真正得到它,但我的问题现在已经解决了。地图在后台加载,不再阻止UI。以下是感兴趣的人的工作代码:
private void init()
{
Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate()
{
// Background Thread
Map map = new Map();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
// Back to the UI Thread
var provider = new ApplicationIdCredentialsProvider(MyCredentials);
mapElementOnUI = new Map();
mapElementOnUI.CredentialsProvider = provider;
mapPanel.Children.Add(mapElementOnUI);
updateMapLocations();
}));
}
));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}