之前写过一篇关于listbox虚拟化的文章,那里采用的方法都是自己早期研究的一些思路,然后发现当数据很大的时候,其实性能效果还是不太理想,下面让我们来仔细想一想到底是基于什么原因,我们回去破坏默认的虚拟化呢?首先一个原因就是下来刷新的问题:由于listbox默认是不支持刷新效果的,这个时候我们可能回去重新自定义listbox,但是我们一旦重写listbox就有可能带来虚拟化的问题,针对这一问题,现在比较好的解决方法就是:乱世经典博客,这种方法并不是像现在很多网路通用的listbox刷新,重写三个自定义控件,它里面只是通过切换不同的VisualStateGroup,这样的话listbox的itemplans就还是支持虚拟化的面板。既然刷新的问题,我们有很好的办法解决虚拟化。那么还有其他的原因会让我们破坏虚拟化吗?肯定是有的,就是当整个页面列表的布局很复杂时候,这种情况下我们可能需要在listbox里面放置自定义控件,一旦你放置usercontrol的话,虚拟化就被破坏了,比如有的时候,我们可能需要listbox里面嵌套listbox这个时候虚拟化肯定是不行的,其实这种情况也有一种很好的解决办法就是:阿干@.NET的博客,在这里面我们可以看到针对listbox里面嵌套listbox的情况,我们可以使用itemcontrols,这样一来的话,它的itempanels就还是虚拟化面板,因此虚拟化还是没有被破坏掉的。
其实还有一种变态的情况就是有的时候我们需要listbox显示的数据是横排的,通常我们的做法就是:
1
2
3
4
5
|
<ListBox.ItemsPanel> <ItemsPanelTemplate>
<toolkit:WrapPanel></toolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
|
这样一来的话,就必然会导致虚拟化被破坏,因为你连面板都换掉了。那么针对这种情况有没有好的解决办法呢?其实阿干那篇博客里面就提供了好的解决办法,我们可以自己去实现分列显示的效果,核心的代码是:
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; namespace MoHoo.MultiColumnListBox.Demo { public class RowAdapter<TItemType> : IList<IEnumerable<TItemType>>, INotifyCollectionChanged { private readonly IList<TItemType> _sourceList; private readonly int _columns; public IList<TItemType> SourceList { get { return _sourceList; } } private class RowObject : IEnumerable<TItemType> { internal readonly RowAdapter<TItemType> Parent; internal readonly int StartIndex; public RowObject(RowAdapter<TItemType> parent, int startIndex) { Parent = parent; StartIndex = startIndex; } #region IEnumerable<TItemType> Members public IEnumerator<TItemType> GetEnumerator() { int limit = Parent._sourceList.Count; int end = Math.Min(StartIndex + Parent._columns, limit); for (int pos = StartIndex; pos < end; ++pos) { yield return Parent._sourceList[pos]; } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } public RowAdapter(IList<TItemType> sourceList, int columns) { if (null == sourceList) throw new ArgumentNullException("sourceList", "Resource.RowAdapter_RowAdapter_sourceList_is_null"); if (columns <= 0) throw new ArgumentOutOfRangeException("columns", "Resource.RowAdapter_RowAdapter_ColumnsGreaterOne"); // We require the source list to implement IList because we // need to know how many item there are _sourceList = sourceList; _columns = columns; var sourceNotify = sourceList as INotifyCollectionChanged; if (null != sourceNotify) { sourceNotify.CollectionChanged += OnSourceCollectionChanged; } } #region IList<IEnumerable<TItemType>> Members public int IndexOf(IEnumerable<TItemType> item) { var realItem = item as RowObject; if (null == realItem || !ReferenceEquals(realItem.Parent, this)) return -1; // It does not belong to this collection Debug.Assert(0 == realItem.StartIndex % _columns, "RowObject item has a wierd index"); return realItem.StartIndex / _columns; } public void Insert(int index, IEnumerable<TItemType> item) { throw new NotSupportedException(); } public IEnumerable<TItemType> this[int index] { get { if (index < 0 || index > Count) return null; return InternalGetRow(index); } set { throw new NotSupportedException(); } } public void RemoveAt(int index) { throw new NotSupportedException(); } #endregion #region ICollection<IEnumerable<TItemType>> Members public void Add(IEnumerable<TItemType> item) { throw new NotSupportedException(); } public bool Contains(IEnumerable<TItemType> item) { var realItem = item as RowObject; return null != realItem && object.ReferenceEquals(realItem.Parent, this); } public void CopyTo(IEnumerable<TItemType>[] array, int arrayIndex) { // I haven‘t implemented this. It is easy to implement if you need it throw new NotImplementedException(); } public bool Remove(IEnumerable<TItemType> item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public int Count { get { return (_sourceList.Count + (_columns - 1)) / _columns; } } public bool IsReadOnly { get { return true; } } #endregion #region IEnumerable<IEnumerable<TItemType>> Members public IEnumerator<IEnumerable<TItemType>> GetEnumerator() { for (int i = 0; i < Count; ++i) { yield return InternalGetRow(i); } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region INotifyCollectionChanged Members public event NotifyCollectionChangedEventHandler CollectionChanged; private void FireCollectionChanged() { var handler = CollectionChanged; if (null != handler) { handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { FireCollectionChanged(); } #endregion private RowObject InternalGetRow(int index) { return new RowObject(this, index * _columns); } } }
这里面的核心思想就是自定义一个索引器。