2

我最近遇到了一个相当严重的性能问题,即我如何将 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();
  }
}
4

0 回答 0