我最近遇到了一个相当严重的性能问题,即如何将我的ObservableCollection中的项目“合并”到单个ListBox中。这适用于当前的迭代,当用户想要在时间轴上放大/缩小时,就会出现问题。
缩放会更改时间轴上每个块所代表的时间量,并可能需要将一个ListBox中的某些元素移动到相邻的ListBox中。它偶尔也需要添加或删除ListBox。
我猜测问题可能出在我如何重新调整OnZoomChanged方法中的条目。另一个问题是,每次添加一个条目时,所有条目似乎都会重新绘制。在更新过程中(添加/缩放),这两者都可以配对以产生相当大的延迟。当我超过50-60个条目时,我真的开始注意到缩放时的性能。
我的最终目标是能够将彼此重叠的条目分类到单独的区域(StackPanel / ListBox等),并允许用户仍然能够浏览所有这些条目,如果他们愿意的话。条目是固定大小(这是产生重叠问题的原因)。
感谢您的任何见解/建议!
以下是一切设置的细分:
视图 - 授予此权限是控件的简化版本。 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函数的条目来响应,将其放在View中的适当位置。
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();
}
}