2010-03-03 60 views
2

有這種情況 - 我在C#(WPF)上做了一些數學建模應用程序,顯示它是實時矢量圖形的結果。數學是做得很好(迭代過程,每幀繪製),但有問題 - 當我使用額外的線程做計算,繪製結果應該使用Dispatcher.BeginInvoke(每幀!)這是一個相當昂貴的操作(分析表明它花費了將近30%的時間)。 但如果我嘗試在UI線程中做所有事情,我只會看到最後一幀(如預期的那樣)。線程實踐。建模應用程序

計算和顯示結果是應用程序的唯一任務,所以我甚至不需要GUI到建模過程中的反應 - 但我需要顯示實時結果...

所以我想避免BeginInvoke並同時顯示結果,而我看不到方法。任何想法如何以這種方式安排計算? 圖形顯示在一些空的FrameworkElement的DrawingVisual中。

THX

回答

1

選項1:渲染事件

做你的計算上獨立的線程,但隊列更改UI和更新他們的渲染事件。它看起來像這樣:

PresentationSource.FromVisual(window).CompositionTarget.Rendering += (obj, e) => 
{ 
    foreach(var update in _queue) 
    UpdateUI(update); 
} 

此代碼假定_queue是一個線程安全(同步)隊列。您可以創建這樣一個隊列類或下載一個隊列類。或者,您可以用「鎖定(_queue)」來包圍「foreach」。

這比Dispatcher.BeginInvoke()更好的原因是:1.它在每個幀之前被調用,所以如果幀速率下降,它會被調用得不那麼頻繁,2.它會批量處理更改。

選項2:多線程UI

您可以通過使用一個單獨的hWnd運行UI的一個單獨的線程的部分。您可以使用WindowsFormsIntegration或使用一些Win32魔術。這裏有一種方法:

  1. 在新線程中,構建窗口並顯示它(不要忘記Appliation。運行())
  2. 一旦顯示的新窗口,使用((HwndSource)PresentationSource.FromVisual(window)).Handle
  3. 用監視器或ManualResetEvent的
  4. 使用WindowsFormsHost傳遞的hWnd回主UI線程構建HWND的容器中,添加得到的HWND它的UI
  5. 處理調整事件在主界面,通過他們下到包含的窗口

方案3:動畫

您可以創建自己的自定義動畫派生類來爲UI項目設置動畫。這些可以使用從模型中精確預測的數據。這樣做的好處是精確的時間同步:每次你的代碼被調用時,它都會確切地知道「何時」(在動畫時間內),它正在計算位置。因此,如果其他程序花費CPU或GPU一秒鐘,並減慢速度和幀速率,則動畫仍以較低的幀速率順利進行。

要做到這一點,子類DoubleAnimationBase並重寫GetCurrentValueCore()來執行您的自定義計算,並結合所需的Clone()和CreateInstanceCore()覆蓋。然後用它來動畫你的屬性。

如果僅僅動畫現有對象的Double屬性是不夠的,則可以創建一個動畫來生成整個對象,如幾何,幾何三維,繪圖,Drawing3D甚至UIElement。爲此,子類AnimationTimeline和覆蓋GetCurrentValue()以及其他。

使用動畫的優點與渲染事件相同,除非您可以讓WPF爲您處理所有時鐘同步和重放速度問題,而不是自己做。如果您只能動畫改變的屬性,它也可能導致更少的代碼。

+0

謝謝。我嘗試了選項3和1 - 並且...它工作) – ALOR 2010-03-03 21:41:40

0

在每個迭代過程可以手動調用您需要重畫,這將發送繪製消息重繪控制/窗口的可視對象上InvalidateVisual()方法的結束。

+0

如果在這個調用之後我馬上開始一個新的迭代,它會實際重繪嗎? – ALOR 2010-03-03 10:17:31

+0

IsvalidateVisual線程安全嗎? – 2010-03-03 10:32:29

+0

** InvalidateVisual不是線程安全的。**從多個線程調用它可能會損壞視覺的內部標誌和/或該線程的排列隊列。沒有辦法阻止使用lock(),因爲WPF在調度器回調中內部使用這些結構。因此,InvalidateVisual只能從擁有該可視化的線程中調用,或者擁有線程絕對保證在任何WPF代碼之外的鎖上等待。 – 2010-03-03 14:35:21

0

使用DataBinding,讓我們來考慮通過ObservableCollection來刷新您的UI的機制。 好運

相關問題