2012-04-05 25 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中。

該方法的問題在於,顯然用戶將無法與在新線程中運行的控件進行交互。

相關問題