"分档" ObservableCollection中的项目分成单独的ListBoxes

时间:2012-10-26 13:57:17

标签: c# wpf wpf-controls

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

0 个答案:

没有答案