8

目前我有两个模仿以下功能的 WPF 列表框

Word 2007 自定义屏幕
(来源:psu.edu

我正在使用 2 ObservableCollections 来允许用户选择他们需要的任何项目(灵活性是这里的关键)。主要问题是我有数千个项目分组在两个列表框中。总而言之,设计工作得非常好(有几十个项目),但我的绊脚石是当屏幕冻结时用户从左到右复制所有可用项目(是时候在不同的线程上运行?)。

查看 ObservableCollection 它缺少 AddRange 方法,并且互联网上有各种可用的实现。我也知道 CollectionChanged 事件被不必要地触发了,因为每个项目都被复制到可怕的消耗性能上。

很可能我将来必须允许用户从超过 10 000 个项目的组中进行选择,这听起来是个坏主意,但由于列表框 (CollectionViewSource) 上的分组工作得很好,但这是不可协商的关闭两个列表框的虚拟化的副作用

当数据绑定到 ObservableCollection 时,加载包含数千个项目的列表框时,我能做些什么来提高性能?您会推荐任何 AddRange 类型的实现吗?我在这里唯一的选择是在后台线程上运行它吗?因为我没有从数据库加载数据,所以看起来很昂贵?

4

4 回答 4

2

我已经删除了 CollectionViewSource 和分组,并且项目在 1/2 秒内被复制,但是分组可能需要一分钟,因为虚拟化不适用于分组。

我需要决定是否使用 CollectionViewSource

于 2009-09-09T10:48:45.600 回答
2

我忍不住回答这个问题。我认为您不再需要此答案,但也许其他人可以使用它。

不要想太多(不要接近这个多线程(这会使事情容易出错和不必要的复杂。只使用线程进行硬计算/IO),所有这些不同的动作类型都会使其非常难以缓冲。最烦人的部分也就是说,如果您删除或添加 10000 个项目,您的应用程序(列表框)将非常忙于处理 ObservableCollection 引发的事件。该事件已经支持多个项目。所以.....

您可以缓冲项目,直到它更改操作。因此,如果“用户”更改操作或刷新操作,添加操作将被缓冲并作为批处理提升。还没有测试它,但你可以做这样的事情:

// Written by JvanLangen
public class BufferedObservableCollection<T> : ObservableCollection<T>
{
    // the last action used
    public NotifyCollectionChangedAction? _lastAction = null;
    // the items to be buffered
    public List<T> _itemBuffer = new List<T>();

    // constructor registeres on the CollectionChanged
    public BufferedObservableCollection()
    {
        base.CollectionChanged += new NotifyCollectionChangedEventHandler(ObservableCollectionUpdate_CollectionChanged);
    }

    // When the collection changes, buffer the actions until the 'user' changes action or flushes it.
    // This will batch add and remove actions.
    private void ObservableCollectionUpdate_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // if we have a lastaction, check if it is changed and should be flush else only change the lastaction
        if (_lastAction.HasValue)
        {
            if (_lastAction != e.Action)
            {
                Flush();
                _lastAction = e.Action;
            }
        }
        else
            _lastAction = e.Action;

        _itemBuffer.AddRange(e.NewItems.Cast<T>());
    }

    // Raise the new event.
    protected void RaiseCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (this.CollectionChanged != null)
            CollectionChanged(sender, e);
    }

    // Don't forget to flush the list when your ready with your action or else the last actions will not be 'raised'
    public void Flush()
    {
        if (_lastAction.HasValue && (_itemBuffer.Count > 0))
        {
            RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(_lastAction.Value, _itemBuffer));
            _itemBuffer.Clear();
            _lastAction = null;
        }
    }

    // new event
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
}

玩得开心!, J3R03N

于 2011-12-22T23:41:33.263 回答
1

您可能可以继承ObservableCollection<T>(或直接实现INotifyCollectionChanged)添加BeginUpdateEndUpdate方法。BeginUpdate在调用和之间所做的更改EndUpdate将被排队,然后组合成一个(或多个,如果有单独的范围)对象,当被调用时NotifyCollectionChangedEventArgs将传递给事件的处理程序。CollectionChangedEndUpdate

于 2009-09-09T09:38:52.450 回答
1

你可以在这里找到一个线程安全的 observable 集合。使您的 Observable 集合线程安全并将其绑定到列表框。

于 2009-09-09T09:42:58.473 回答