我最近遇到了一个相当严重的性能问题,即我如何将 ObservableCollection 中的项目“分箱”到单个 ListBox 中。这在其当前迭代中有效,当用户想要放大/缩小时间轴时,我的问题就出现了。
缩放会改变时间轴上每个块所代表的时间量,并且可能需要将一个 ListBox 中的一些元素移动到相邻的 ListBox 中。它有时还需要添加或删除 ListBox。
我猜问题可能在于我如何重新洗牌 OnZoomChanged 方法中的条目。另一个问题是每次添加一个条目时似乎都会重新绘制所有条目。这两者都可以在更新过程(添加/缩放)过程中产生相当大的延迟。当我超过大约 50-60 个条目时,我真的开始注意到缩放对性能的影响。
我在这里的最终目标是能够将彼此重叠的条目分类到单独的箱(StackPanel/ListBox 等)中,并允许用户在需要时仍然能够浏览所有条目。这些条目是固定大小的(这是造成重叠问题的原因)。
感谢您的任何见解/建议!
以下是所有设置方式的细分:
View - 授予这是控件的简化版本。DateTime(GroupingTime)在我的自定义视图中用于放置在时间轴上(这有效)。然后将时间线条目列表 (GroupEntries) 馈送到 TimelineEntryControl 以在 ListBox 中适当地绘制条目。
<ItemsControl ItemsSource="{Binding TimelineEntries}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Views:CustomView>
<ListBox>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Controls:TimelineEntryControl TimelineEntry="{Binding}" Height="50" Width="50" Margin="0,0,0,12"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Views:CustomView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel - TimelineEntryViewModel
internal class TimelineEntryViewModel : ViewModel, IDisposable
{
private readonly ObservableList<TimelineEntry> _groupEntries = new ObservableList<TimelineEntry>();
private readonly ObservableCollectionHandler<TimelineEntry> _groupEntriesHandler;
public TimelineEntryViewModel( TimelineViewModel timelineViewModel )
{
Tvm = timelineViewModel;
_groupEntriesHandler = new ObservableCollectionHandler<TimelineEntry>(_groupEntries, OnAdded, OnRemoved);
GroupEntries = _groupEntries.CreateView();
GroupEntries.SortDescriptions.Add( new SortDescription( "TimeOccured", ListSortDirection.Ascending) );
}
private void OnAdded( TimelineEntry tle )
{
GroupEntries.Refresh();
}
private void OnRemoved( TimelineEntry tle )
{
GroupEntries.Refresh();
}
public void AddEntry( TimelineEntry tle )
{
_groupEntries.Add( tle );
}
public void Dispose()
{
_groupEntriesHandler.Dispose();
}
public TimelineViewModel Tvm {get; private set;}
public DateTime GroupingTime {get; set;}
public ICollectionView GroupEntries {get; private set;}
}
TimelineViewModel - 控制条目/缩放处理的部分。我使用 eventAggregator 响应包含进入这些条目的信息的特定消息,并通过创建调用 AddEntry 函数以将其放置在视图中的适当位置的条目来响应。
internal sealed class TimelineViewModel : ViewModel, IDisposable
{
private readonly ObservableList<TimelineEntryViewModel> _timelineEntries = new ObservableList<TimelineEntryViewModel>();
private readonly ObservableHandler _timelineEntriesHandler;
public TimelineViewModel ( )
{
// Omitting unnecessary code here
_timelineEntriesHandler = new ObservableHandler( _timelineEntries );
TimelineEntries = _timelineEntries.CreateView();
TimelineEntries.SortDescription.Add( new SortDescription( "GroupingTime", ListSortDirection.Ascending ) );
TimelineEntries.Refresh();
}
private void AddEntry( TimelineEntry tle )
{
var timeForNoOverlap = Projection.UnprojectDuration( 90 ); //This just gets the number of pixels needed for no overlap between panel groups.
if( _timelineEntries.Count == 0 )
{
_timelineEntries.Add( new TimelineEntryViewModel( this ) );
_timelineEntries[0].GroupingTime = tle.TimeOccured;
_timelineEntries[0].AddEntry( tle );
}
else if ( tle.TimeOccured > _timelineEntries.Last().GroupingTime + timeForNoOverlap )
{
_timelineEntries.Add( new TimeLineViewModel( this );
_timelineEntries.Last().GroupingTime = tle.TimeOccured;
_timelineEntries.Last().AddEntry( tle );
}
else
{
foreach( TimelineEntryViewModel tevm in _timelineEntries )
{
if( tle.TimeOccured >= tevm.GroupingTime && tle.TimeOccured <= tevm.GroupingTime + timeForNoOverlap)
{
tevm.AddEntry( tle );
break;
}
}
}
TimelineEntries.Refresh();
}
private void OnZoomChanged()
{
var tempEntries = new ObservableList<TimelineEntryViewModel>( _timelineEntries );
foreach( var group in _timelineEntries )
group.Dispose();
_timelineEntries.Clear();
foreach( var group in tempEntries )
{
foreach( var item in group.GroupEntries )
AddEntry( (TimelineEntry)item );
}
foreach( var group in tempEntries )
group.Dispose();
tempEntries.Clear();
}
}