2

我将 ObservableCollection 绑定到 WPF ListView。该列表中的数据来自 REST 服务。所以我从 REST-Service 获取数据并将其放入绑定的 ObservableCollection。我定期调用 REST-Service 来检查更新的数据,这意味着可以删除、添加数据或更改项目的顺序。如何将这些更改反映到 ObservableCollection 中?每次从 REST 服务获取更新数据时,我都不想完全替换 ObservableCollection。如果 ObservableCollection 仅针对源数据中更改的条目进行更改,则会更加用户友好。因此,当在源数据中添加一个项目时,我想将此项目添加到 ObservableCollection 中与源数据(REST 服务)中完全相同的位置。对于已删除的项目和重新使用的项目也是如此。所以我只想更新更改的项目而不是整个集合。那可能吗?

4

5 回答 5

1

更新:似乎没有标准的方法可以做到这一点,我试图自己实现一个解决方案。这绝对不是生产代码,我可能已经忘记了很多用例,但也许这是一个开始?这是我想出的:

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    public void RecreateCollection( IList<T> newList )
    {
        // nothing changed => do nothing
        if( this.IsEqualToCollection( newList ) ) return;

        // handle deleted items
        IList<T> deletedItems = this.GetDeletedItems( newList );
        if( deletedItems.Count > 0 )
        {
            foreach( T deletedItem in deletedItems )
            {
                this.Remove( deletedItem );
            }
        }

        // handle added items
        IList<T> addedItems = this.GetAddedItems( newList );           
        if( addedItems.Count > 0 )
        {
            foreach( T addedItem in addedItems )
            {
                this.Add( addedItem );
            }
        }

        // equals now? => return
        if( this.IsEqualToCollection( newList ) ) return;

        // resort entries
        for( int index = 0; index < newList.Count; index++ )
        {
            T item = newList[index];
            int indexOfItem = this.IndexOf( item );
            if( indexOfItem != index ) this.Move( indexOfItem, index );
        }
    }

    private IList<T> GetAddedItems( IEnumerable<T> newList )
    {
        IList<T> addedItems = new List<T>();
        foreach( T item in newList )
        {
            if( !this.ContainsItem( item ) ) addedItems.Add( item );
        }
        return addedItems;
    }

    private IList<T> GetDeletedItems( IEnumerable<T> newList )
    {
        IList<T> deletedItems = new List<T>();
        foreach( var item in this.Items )
        {
            if( !newList.Contains( item ) ) deletedItems.Add( item );
        }
        return deletedItems;
    }

    private bool IsEqualToCollection( IList<T> newList )
    {   
        // diffent number of items => collection differs
        if( this.Items.Count != newList.Count ) return false;

        for( int i = 0; i < this.Items.Count; i++ )
        {
            if( !this.Items[i].Equals( newList[i] ) ) return false;
        }
        return true;
    }

    private bool ContainsItem( object value )
    {
        foreach( var item in this.Items )
        {
            if( value.Equals( item ) ) return true;
        }
        return false;
    }
}

方法“ RecreateCollection ”是调用将更新的列表从数据源(newList)“同步”到现有的 ObservableCollection 的方法。我确定使用方法是错误的,所以也许有人可以帮助我解决这个问题?另外值得一提的是:Collections 中的 Items 必须覆盖 EqualsTo 以便按内容而不是按引用比较对象。

于 2013-10-24T11:30:03.843 回答
0

我自己也为此苦苦挣扎,恐怕我还没有找到一个特别优雅的解决方案。如果有人在这里发布更好的解决方案,我也很感兴趣。但到目前为止我所做的基本上是拥有一个 ObservableCollection 的 ObservableCollection。

所以声明看起来像:

      ObservableCollection<ObservableCollection<string>> collectionThatUpdatesAtAppropriatePosition = new 
ObservableCollection<ObservableCollection<string>>();

现在这里的想法是,ObservableCollection 在列表中它自己的特定位置总是只有在其 zero[0] 位置有一个数据值,因此表示数据。因此,示例更新如下所示:

    if (this.collectionThatUpdatesAtAppropriatePosition.Count > 0)
    {
        this.collectionThatUpdatesAtAppropriatePosition[0].RemoveAt(0);
        this.collectionThatUpdatesAtAppropriatePosition[0].Add(yourData);
    }

我知道它不漂亮。我想知道 NotificationObjects 是否有什么不能更好地尝试的东西。理论上,我认为任何实现 INotifyPropertyChanged 的​​事情都应该做。但它确实有效。祝你好运。我将密切关注这个问题,看看是否有其他人想出更复杂的东西。

于 2013-10-24T07:14:02.797 回答
0

你要完成的任务并不容易。您必须根据“旧”集合的相关项目检查新集合的每个项目,以查看是否发生了变化。

然后,出于性能原因,此解决方案不是那么有用。

简单的解决方案是将当前集合替换为使用服务数据创建的新集合。伪代码是这样的:

ObservableCollection<DataItem> baseCollection = new ObservableCollection<DataItem>();

// adding/removing items
ObservableCollection<DataItem> serviceCollection = new ObservableCollection<DataItem>();

// adding/removing items
baseCollection.Clear();

// replacing old collection with the new one
baseCollection = serviceCollection;
于 2013-10-24T07:56:23.400 回答
0

这是我在设备的 ObservableCollection 中使用的 GET 方法,使用 REST 异步检索 Json 对象集合,并将结果合并到现有的 ObservableCollection 中,UI DataGrids 等也可以使用 caller.BeginInvoke(),未经生产测试,但到目前为止似乎工作正常。

public class tbDevices
{
    public tbDevices()
    {
        this.Items = new ObservableCollection<tbDevice>();
    }

    public ObservableCollection<tbDevice> Items { get; }

    public async Task<IRestResponse> GET(Control caller, int limit = 0, int offset = 0, int timeout = 10000)
    {
        return await Task.Run(() =>
        {
            try
            {
                IRestResponse response = null;

                var request = new RestRequest(Globals.restDevices, Method.GET, DataFormat.Json);
                if (limit > 0)
                {
                    request.AddParameter("limit", limit);
                }
                if (offset > 0)
                {
                    request.AddParameter("offset", offset);
                }
                request.Timeout = timeout;

                try
                {
                    var client = new RestClient(Globals.apiProtocol + Globals.apiServer + ":" + Globals.apiPort);
                    client.Authenticator = new HttpBasicAuthenticator(Globals.User.email.Trim(), Globals.User.password.Trim());
                    response = client.Execute(request);
                }
                catch (Exception err)
                {
                    throw new System.InvalidOperationException(err.Message, response.ErrorException);
                }

                if (response.ResponseStatus != ResponseStatus.Completed)
                {
                    throw new System.InvalidOperationException("O servidor informou erro HTTP " + (int)response.StatusCode + ": " + response.ErrorMessage, response.ErrorException);
                }

                // Will do a one-by-one data refresh to preserve sfDataGrid UI from flashing
                List<tbDevice> result_objects_list = null;
                try
                {
                    result_objects_list = JsonConvert.DeserializeObject<List<tbDevice>>(response.Content);
                }
                catch (Exception err)
                {
                    throw new System.InvalidOperationException("Não foi possível decodificar a resposta do servidor: " + err.Message);
                }

                // Convert to Dictionary for faster DELETE loop
                Dictionary<string, tbDevice> result_objects_dic = result_objects_list.ToDictionary(x => x.id, x => x);

                // Async update this collection as this may be a UI cross-thread call affecting Controls that use this as datasource
                caller?.BeginInvoke((MethodInvoker)delegate ()
                {
                    // DELETE devices NOT in current_devices 
                    for (int i = this.Items.Count - 1; i > -1; i--)
                    {
                        result_objects_dic.TryGetValue(this.Items[i].id, out tbDevice found);
                        if (found == null)
                        {
                            this.Items.RemoveAt(i);
                        }
                    }

                    // UPDATE/INSERT local devices
                    foreach (var obj in result_objects_dic)
                    {
                        tbDevice found = this.Items.FirstOrDefault(f => f.id == obj.Key);
                        if (found == null)
                        {
                            this.Items.Add(obj.Value);
                        }
                        else
                        {
                            found.Merge(obj.Value);
                        }
                    }
                });

                return response;
            }
            catch (Exception)
            {
                throw; // This preserves the stack trace
            }
        });
    }
}
于 2019-06-28T17:22:15.353 回答
0

这是我的ObservableCollection扩展实现,添加UpdateCollection任何方法IEnumerable

using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;

namespace MyApp.Extensions
{
    /// <summary>
    /// Observable collection extension.
    /// </summary>
    public static class ObservableCollectionExtension
    {

        /// <summary>
        /// Replaces the collection without destroy it
        /// Note that we don't Clear() and repopulate collection to avoid and UI winking
        /// </summary>
        /// <param name="collection">Collection.</param>
        /// <param name="newCollection">New collection.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static void UpdateCollection<T>(this ObservableCollection<T> collection, IEnumerable<T> newCollection)
        {
            IEnumerator<T> newCollectionEnumerator = newCollection.GetEnumerator();
            IEnumerator<T> collectionEnumerator = collection.GetEnumerator();

            Collection<T> itemsToDelete = new Collection<T>();
            while( collectionEnumerator.MoveNext())
            {
                T item = collectionEnumerator.Current;

                // Store item to delete (we can't do it while parse collection.
                if( !newCollection.Contains(item)){
                    itemsToDelete.Add(item);
                }
            }

            // Handle item to delete.
            foreach( T itemToDelete in itemsToDelete){
                collection.Remove(itemToDelete);
            }

            var i = 0;
            while (newCollectionEnumerator.MoveNext())
            {
                T item = newCollectionEnumerator.Current;

                // Handle new item.
                if (!collection.Contains(item)){
                    collection.Insert(i, item);
                }

                // Handle existing item, move at the good index.
                if (collection.Contains(item)){
                    int oldIndex = collection.IndexOf(item);
                    collection.Move(oldIndex, i);
                }

                i++;
            }
        }
    }
}

用法 :

using MyApp.Extensions;

var _refreshedCollection = /// You data refreshing stuff            
MyObservableExistingCollection.UpdateCollection(_refreshedCollection);

希望它会帮助某人。欢迎任何优化!

于 2017-06-21T20:22:29.460 回答