2012-04-05 26 views
18

我在我的项目中使用的可视控件来自我没有源代码的库。
需要花费很长时间才能更新(粗略为200毫秒),以便在屏幕上同时显示其中三个控件时具有良好的UI响应能力。 (我可能需要立即更新所有三个,这让我的用户界面卡住〜600毫秒,而他们都在想)。在另一个线程中运行WPF控件

我已阅读了一些关于TaskSchedulers的文章,并开始研究并行任务功能,以此作为在他们自己的线程中运行这些控件中的每一个的一种方式。该平台将是多核,所以我想利用相同的处理。

的问题是,我甚至不知道我不知道如何去了解这一点,虽然不..

是否有从一个单独的线程运行的控制合适的设计模式WPF中的主要UI线程?

具体的:这是一个第三方地图控制,当给定一个新的位置或缩放级别需要太长时间才能重绘(〜200ms)。有可能三个这些更新在最大4Hz - 显然他们不会跟上..
我已经封装WPF控件在一个用户控件,并需要在它自己的线程中运行每个实例,同时仍然捕获用户输入(例如鼠标点击)。

UPDATE:虽然我感觉周围的解决方案,我到目前为止实施以下。
我的主(UI)线程产生一个线程,该线程创建一个包含正在讨论的控件的新窗口,并将其定位在正确的位置(以便它看起来像只是一个正常控件)。

_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(); 

CommandQueueThread-safe BlockingCollection队列用于将命令发送到在地图(移动位置等)。
现在的问题是,我可以

  • 具有用户输入由于System.Windows.Threading.Dispatcher.Run()呼叫
  • 或块上的CommandQueue,监听由主线程

我发送的命令无法旋转等待命令,因为它会吸收我所有线程的CPU!
是否有可能阻止使事件消息泵工作?

+0

所有WPF控件必须在UI线程上进行更新。不过,如果您提供您正在使用的控件的某些详细信息以及您编写的用于更新/填充它的任何代码,我们可能会提供帮助。 – 2012-04-10 04:10:27

+0

@CameronPeters,你确定不能有多个“UI线程”? – svick 2012-04-10 20:20:05

+0

我目前有多个'UI'线程正在运行(感谢Threading.Dispatcher.Run()),但无法阻止它们等待信号。 – DefenestrationDay 2012-04-10 22:18:09

回答

11

嗯,我有一个可行的方法 - 但它可能不是最优雅..

我有一个包含我的第三方(慢渲染)在XAML控件的窗口。

public partial class MapWindow : Window 
{ 
    private ConcurrentQueue<MapCommand> _mapCommandQueue; 
    private HwndSource _source; 

    // ... 

} 

我的主(UI)线程contructs并开始在此窗口线程:

_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(); 

然后我得到的窗口句柄的线程(已初始化后):

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; 
    } 
} 

..并将命令放到与线程窗口共享的线程安全队列后:

var command = new MapCommand(MapCommand.CommandType.AircraftLocation, new object[] {RandomLatLon}); 
_leftMapCommendQueue.Enqueue(command); 

..我让他知道它可以检查队列:

PostMessage(LeftHandMapWindowHandle, MapWindow.WmCustomCheckForCommandsInQueue, IntPtr.Zero, IntPtr.Zero); 

窗口可以收到我的消息,因为它已经挂接到窗口消息:

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; 
} 

..然后在线程上执行!

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; 
     } 
    } 
} 

易为.. :)

+1

这实际上是一项伟大的工作,您所做的这些工作很少在这里赞赏 – 2012-12-26 16:03:33

5

我一直在寻找到这个问题,以及,我能找到最相关的信息,在这个博客帖子(但我没有测试它尚未):

http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx

它创建在UI线程上添加一个HostVisual,然后创建一个后台线程,创建一个MediaElement,并将其放入一个VisualTarget(指向HostVisual)中,并将其全部放入我们的哈希VisualTargetPresentationSource中。

该方法的问题在于,显然用户将无法与在新线程中运行的控件进行交互。

相关问题