我有(同時通常10和20之間)計算數量的元件的常數改變位置的應用程序。計算是在一個專用線程上完成的,然後該線程觸發一個事件來指示計算已完成。這個專用線程以每秒穩定100幀的速度運行。
可視化
的元件的可視化使用WPF完成。我實現了一個畫布,並編寫了自定義代碼,將每個元素的可視化表示添加到畫布。定位使用Canvas.SetLeft和Canvas.SetTop。此代碼在專用線程的事件處理程序內運行,因此不會影響性能。
問題
由於這樣的事實,所述元件被不斷地移動,運動似乎是口吃。我現在唯一能夠做出的假設是,WPF根據自己的信念提出並試圖達到每秒最多60幀。如果您對爲什麼會發生這種情況有額外的評論,請啓發我。
問題
我如何才能知道當專用線程已經調用完成計算WPF來呈現?如果可能的話,我想阻止渲染窗口/畫布直到計算完成,此時幀應該前進並呈現新的信息。
評論
很多人不喜歡嘗試超越默認的每秒60幀,而我也不會。然而,這是絕對必須要能夠影響當渲染髮生在每秒60幀之前。這是爲了確保每秒60幀不影響口吃問題。
渲染(碼)
這是實際的佈局更新所涉及的代碼,如每安德魯伯內特-湯普森博士的請求。 MainViewModel包含一個由專用工作線程創建的具有ActorViewModel實例的ObservableCollection。然後將它們轉換成ActorView實例以可視化計算的數據。在添加和刪除實例時,我故意使用調用,而不是BeginInvoke確保這些例程是而不是性能問題的原因。
完成專用工作計算後,將在MainViewModel中調用UpdateLayout,此時會調用UpdateSynchronizationCollectionLayout以更新可視化。此時,縮放和不透明度未被利用,並且當實例視框被省略時觀察到相同的口吃行爲。除非我錯過了一些東西,否則這個問題應該與我無法檢查或控制渲染時間或速度的事實有關。
/// <summary>
/// Contains the added ActorViewModel instances and the created ActorView wrapped in a ViewBox.
/// </summary>
private Dictionary<ActorViewModel, KeyValuePair<Viewbox, ActorView>> _hSynchronizationCollection = new Dictionary<ActorViewModel, KeyValuePair<Viewbox, ActorView>>();
/// <summary>
/// Update the synchronization collection with the modified data.
/// </summary>
/// <param name="sender">Contains the sender.</param>
/// <param name="e"></param>
private void _UpdateSynchronizationCollection(object sender, NotifyCollectionChangedEventArgs e)
{
// Check if the action that caused the event is an Add event.
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Invoke the following code on the UI thread.
Dispatcher.Invoke(new Action(delegate()
{
// Iterate through the ActorViewModel instances that have been added to the collection.
foreach(ActorViewModel hActorViewModel in e.NewItems)
{
// Initialize a new _hInstance of the ActorView class.
ActorView hActorView = new ActorView(hActorViewModel);
// Initialize a new _hInstance of the Viewbox class.
Viewbox hViewBox = new Viewbox { StretchDirection = StretchDirection.Both, Stretch = Stretch.Uniform };
// Add the _hInstance of the ActorView to the synchronized collection.
_hSynchronizationCollection.Add(hActorViewModel, new KeyValuePair<Viewbox, ActorView>(hViewBox, hActorView));
// Set the child of the Viewbox to the ActorView.
hViewBox.Child = hActorView;
// Add the _hInstance of the ActorView to the canvas.
CanvasDisplay.Children.Add(hViewBox);
}
}));
}
// Check if the action that caused the event is a Remove event.
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
// Invoke the following code on the UI thread.
Dispatcher.Invoke(new Action(delegate()
{
// Iterate through the ActorViewModel instances that have been removed to the collection.
foreach(ActorViewModel hActorViewModel in e.OldItems)
{
// Check if the ActorViewModel _hInstance is contained in the synchronization collection.
if (_hSynchronizationCollection.ContainsKey(hActorViewModel))
{
// Remove the ActorView from the canvas.
CanvasDisplay.Children.Remove(_hSynchronizationCollection[hActorViewModel].Key);
// Remove the ActorViewModel from the collection.
_hSynchronizationCollection.Remove(hActorViewModel);
}
}
}));
}
}
/// <summary>
/// Update the synchronization collection layout with the modified data.
/// </summary>
private void _UpdateSynchronizationCollectionLayout()
{
// Invoke the following code on the UI thread.
Dispatcher.Invoke(new Action(delegate()
{
// Iterate through each ActorViewModel in the synchronization collection.
foreach(KeyValuePair<ActorViewModel, KeyValuePair<Viewbox, ActorView>> hDictionaryKeyValuePair in _hSynchronizationCollection)
{
// Retrieve the ActorViewModel.
ActorViewModel hActorViewModel = hDictionaryKeyValuePair.Key;
// Retrieve the KeyValuePair for this ActorViewModel.
KeyValuePair<Viewbox, ActorView> hKeyValuePair = hDictionaryKeyValuePair.Value;
// Sets the height of the ViewBox in which the ActorView is displayed.
hKeyValuePair.Key.Height = hKeyValuePair.Value.ActualHeight * hActorViewModel.LayoutScale;
// Sets the width of the ViewBox in which the ActorView is displayed.
hKeyValuePair.Key.Width = hKeyValuePair.Value.ActualWidth * hActorViewModel.LayoutScale;
// Set the opacity factor of the ActorView.
hKeyValuePair.Value.Opacity = hActorViewModel.LayoutOpacity;
// Sets the hValue of the Left attached property for the given dependency object.
Canvas.SetLeft(hKeyValuePair.Key, hActorViewModel.LayoutLeft - (hActorViewModel.LayoutAlignment == MainAlignment.Center ? hKeyValuePair.Key.ActualWidth/2 : (hActorViewModel.LayoutAlignment == MainAlignment.Right ? hKeyValuePair.Key.ActualWidth : 0)));
// Sets the hValue of the Top attached property for the given dependency object.
Canvas.SetTop(hKeyValuePair.Key, hActorViewModel.LayoutTop);
// Sets the hValue of the ZIndex attached property for the given dependency object.
Canvas.SetZIndex(hKeyValuePair.Key, hActorViewModel.LayoutLayerIndex);
}
}));
}
/// <summary>
/// Initialize a new _hInstance of the MainWindow class.
/// </summary>
/// <param name="hMainViewModel">Contains the object that is used as data context in MainView.</param>
internal MainView(MainViewModel hMainViewModel)
{
// Add a subscriber that occurs when an item is added, removed, changed, moved, or the entire list is refreshed.
hMainViewModel.ActorViewModel.CollectionChanged += new NotifyCollectionChangedEventHandler(_UpdateSynchronizationCollection);
// Initialize the component.
InitializeComponent();
// Set the subscriber that occurs when the layout changes.
hMainViewModel.LayoutChanged += new Action(_UpdateSynchronizationCollectionLayout);
}
+1,因爲出色的措辭Q :) 你不應該在60FPS口吃。人眼只能檢測到25FPS(或更少)。你可以發佈一些代碼片段,因爲可能有更多的東西在這裏看到(沒有雙關語意圖; P) – 2012-01-04 16:35:50
@ Dr.AndrewBurnett-Thompson謝謝你的友好評論:)更新與'渲染'部分的問題,根據請求。我希望這會增加目前的狀況和問題。 – 2012-01-04 17:20:54
沒問題Roel,這就是知識共享。在這裏有一些不太友善的字符;-) – 2012-01-04 17:22:00