运行在另一个线程WPF控件 [英] Running a WPF control in another thread
问题描述
我用我的项目有三个很好的UI响应可视控件是从图书馆,我没有来源。结果
的时间太长更新(200毫秒,大约)在一次屏幕上的这些控件。 (我可能需要一次更新所有三个,这让我的用户界面卡住〜600毫秒,而他们都是思维)。
I am using a visual control in my project that is from a library that I do not have the source to.
It takes too long to update (200ms, roughly) for good UI responsiveness with three of these controls on-screen at once. (I might need to update all three at once, which leaves my UI stuck for ~600ms while they are all thinking).
我看了一下TaskSchedulers一些帖子,和我开始研究并行任务的功能作为自己的线程运行的每个这些控件的方式。该平台将是多核心,所以我想利用simultaineous处理。
I have read some posts about TaskSchedulers, and am beginning to investigate the Parallel task features as a way of running each of these controls in their own thread. The platform will be multi-core, so I want to take advantage of simultaineous processing.
的问题是,我甚至不知道什么是不知道我如何去这一点,虽然..
The problem is that I don't even know what I don't know about how to go about this, though..
是否有从在WPF?
特别:它是一个第三方的地图控制,即赋予了新的位置,或当缩放级别花费的时间太长了重绘(200毫秒〜)。随着或许这三个更新以最大4赫兹 - 显然他们会跟不上..结果
我都封装在一个UserControl WPF控件,并且需要在它自己的线程上运行的每个实例,而。还是捕捉用户输入(鼠标点击,例如)
Specifically: it is a third party map control, that when given a new location or zoom level takes far too long to redraw (~200ms). With perhaps three of these updating at a maximum of 4Hz - obviously they won't keep up..
I have encapsulated the WPF control in a usercontrol, and need to run each instance in it's own thread, while still capturing user input (mouse clicks, for example).
更新:当我感觉周围的解决方案,我已经实现了以下到目前为止,结果
我的主(UI)线程派生创建一个包含有问题的控制一个新的窗口一个线程,并定位它在正确的位置(这样它看起来只是一个普通的控制。)
UPDATE: while I am feeling around for a solution, I have implemented the following so far.
My main (UI) thread spawns a thread that creates a new window that contains the control in question, and locates it in the correct position (so that it looks like it is just a normal control).
_leftTopThread = new Thread(() =>
{
_topLeftMap = new MapWindow()
{
WindowStartupLocation = WindowStartupLocation.Manual,
Width = leftLocation.Width,
Height = leftLocation.Height,
Left = leftLocation.X,
Top = leftLocation.Y,
CommandQueue = _leftMapCommandQueue,
};
_topLeftMap.Show();
System.Windows.Threading.Dispatcher.Run();
});
_leftTopThread.SetApartmentState(ApartmentState.STA);
_leftTopThread.IsBackground = true;
_leftTopThread.Name = "LeftTop";
_leftTopThread.Start();
其中, CommandQueue
是的Thread-safe BlockingCollection 的队列发送命令到图(移动位置等)。结果,
现在的问题是,现在我可以的或者的
Where CommandQueue
is a Thread-safe BlockingCollection Queue for sending commands to the map (moving the location, etc).
The problem is now that I can either
- 有用户输入因
System.Windows.Threading.Dispatcher.Run()
呼叫 - 或在CommandQueue块,听由主线程发送的命令
我不能旋转等待命令,因为这会泡了我所有的线程CPU!结果
是否有可能阻止和的有事件消息泵工作
I can't spin waiting for commands, because it would soak up all my thread CPU!
Is it possible to block and have the event message-pump working?
推荐答案
嗯,我有一个可行的方法? - 但它可能不是最优雅的。
Well, I have a method that works - but it may well not be the most elegant..
我有一个包含我的第三方(慢渲染)在XAML控件的窗口。
I have a window that contains my third party (slow-rendering) control in the XAML.
public partial class MapWindow : Window
{
private ConcurrentQueue<MapCommand> _mapCommandQueue;
private HwndSource _source;
// ...
}
我的主(UI)线程contructs并启动该窗口的线程:
My main (UI) thread contructs and starts this window on a thread:
_leftTopThread = new Thread(() =>
{
_topLeftMap = new MapWindow()
{
WindowStartupLocation = WindowStartupLocation.Manual,
CommandQueue = _leftMapCommendQueue,
};
_topLeftMap.Show();
System.Windows.Threading.Dispatcher.Run();
});
_leftTopThread.SetApartmentState(ApartmentState.STA);
_leftTopThread.IsBackground = true;
_leftTopThread.Name = "LeftTop";
_leftTopThread.Start();
然后我得到一个处理窗口在线程(已初始化后):
I then get a handle to the window in the thread (after it has initialised):
private IntPtr LeftHandMapWindowHandle
{
get
{
if (_leftHandMapWindowHandle == IntPtr.Zero)
{
if (!_topLeftMap.Dispatcher.CheckAccess())
{
_leftHandMapWindowHandle = (IntPtr)_topLeftMap.Dispatcher.Invoke(
new Func<IntPtr>(() => new WindowInteropHelper(_topLeftMap).Handle)
);
}
else
{
_leftHandMapWindowHandle = new WindowInteropHelper(_topLeftMap).Handle;
}
}
return _leftHandMapWindowHandle;
}
}
..并把命令到线程后与该线程的窗口共享安全的队列:
.. and after putting a command onto the thread-safe queue that is shared with the threaded window:
var command = new MapCommand(MapCommand.CommandType.AircraftLocation, new object[] {RandomLatLon});
_leftMapCommendQueue.Enqueue(command);
..我让他知道它可以检查队列:
.. I let it know it can check the queue:
PostMessage(LeftHandMapWindowHandle, MapWindow.WmCustomCheckForCommandsInQueue, IntPtr.Zero, IntPtr.Zero);
该窗口可以收到我的消息,因为它挂钩到窗口消息:
The window can receive my message because it has hooked into the window messages:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_source = PresentationSource.FromVisual(this) as HwndSource;
if (_source != null) _source.AddHook(WndProc);
}
..它则可以检查:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) //
{
// Handle messages...
var result = IntPtr.Zero;
switch (msg)
{
case WmCustomCheckForCommandsInQueue:
CheckForNewTasks();
break;
}
return result;
}
..然后线程上执行!
..and then execute on the thread!
private void CheckForNewTasks()
{
MapCommand newCommand;
while (_mapCommandQueue.TryDequeue(out newCommand))
{
switch (newCommand.Type)
{
case MapCommand.CommandType.AircraftLocation:
SetAircraftLocation((LatLon)newCommand.Arguments[0]);
break;
default:
Console.WriteLine(String.Format("Unknown command '0x{0}'for window", newCommand.Type));
break;
}
}
}
易作为..: )
Easy as that.. :)
这篇关于运行在另一个线程WPF控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!