{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/10752","repository_url":"https://api.github.com/repos/dotnet/corefx","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/10752/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/10752/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/10752/events","html_url":"https://github.com/dotnet/corefx/issues/10752","id":170996032,"number":10752,"title":"Collection<T> and ObservableCollection<T> do not support ranges","user":{"login":"robertmclaws","id":1657085,"avatar_url":"https://avatars3.githubusercontent.com/u/1657085?v=4","gravatar_id":"","url":"https://api.github.com/users/robertmclaws","html_url":"https://github.com/robertmclaws","followers_url":"https://api.github.com/users/robertmclaws/followers","following_url":"https://api.github.com/users/robertmclaws/following{/other_user}","gists_url":"https://api.github.com/users/robertmclaws/gists{/gist_id}","starred_url":"https://api.github.com/users/robertmclaws/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robertmclaws/subscriptions","organizations_url":"https://api.github.com/users/robertmclaws/orgs","repos_url":"https://api.github.com/users/robertmclaws/repos","events_url":"https://api.github.com/users/robertmclaws/events{/privacy}","received_events_url":"https://api.github.com/users/robertmclaws/received_events","type":"User","site_admin":false},"labels":[{"id":163655927,"url":"https://api.github.com/repos/dotnet/corefx/labels/api-approved","name":"api-approved","color":"159818","default":false},{"id":154923328,"url":"https://api.github.com/repos/dotnet/corefx/labels/area-System.Collections","name":"area-System.Collections","color":"d4c5f9","default":false},{"id":145399958,"url":"https://api.github.com/repos/dotnet/corefx/labels/up-for-grabs","name":"up-for-grabs","color":"159818","default":false}],"state":"open","locked":false,"assignee":{"login":"robertmclaws","id":1657085,"avatar_url":"https://avatars3.githubusercontent.com/u/1657085?v=4","gravatar_id":"","url":"https://api.github.com/users/robertmclaws","html_url":"https://github.com/robertmclaws","followers_url":"https://api.github.com/users/robertmclaws/followers","following_url":"https://api.github.com/users/robertmclaws/following{/other_user}","gists_url":"https://api.github.com/users/robertmclaws/gists{/gist_id}","starred_url":"https://api.github.com/users/robertmclaws/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robertmclaws/subscriptions","organizations_url":"https://api.github.com/users/robertmclaws/orgs","repos_url":"https://api.github.com/users/robertmclaws/repos","events_url":"https://api.github.com/users/robertmclaws/events{/privacy}","received_events_url":"https://api.github.com/users/robertmclaws/received_events","type":"User","site_admin":false},"assignees":[{"login":"robertmclaws","id":1657085,"avatar_url":"https://avatars3.githubusercontent.com/u/1657085?v=4","gravatar_id":"","url":"https://api.github.com/users/robertmclaws","html_url":"https://github.com/robertmclaws","followers_url":"https://api.github.com/users/robertmclaws/followers","following_url":"https://api.github.com/users/robertmclaws/following{/other_user}","gists_url":"https://api.github.com/users/robertmclaws/gists{/gist_id}","starred_url":"https://api.github.com/users/robertmclaws/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robertmclaws/subscriptions","organizations_url":"https://api.github.com/users/robertmclaws/orgs","repos_url":"https://api.github.com/users/robertmclaws/repos","events_url":"https://api.github.com/users/robertmclaws/events{/privacy}","received_events_url":"https://api.github.com/users/robertmclaws/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/dotnet/corefx/milestones/5","html_url":"https://github.com/dotnet/corefx/milestone/5","labels_url":"https://api.github.com/repos/dotnet/corefx/milestones/5/labels","id":1328080,"number":5,"title":"Future","description":"Milestone tracking issues not triaged to a planned release. Work in this milestone may happen, or may not: it is not committed.","creator":{"login":"joshfree","id":1302850,"avatar_url":"https://avatars1.githubusercontent.com/u/1302850?v=4","gravatar_id":"","url":"https://api.github.com/users/joshfree","html_url":"https://github.com/joshfree","followers_url":"https://api.github.com/users/joshfree/followers","following_url":"https://api.github.com/users/joshfree/following{/other_user}","gists_url":"https://api.github.com/users/joshfree/gists{/gist_id}","starred_url":"https://api.github.com/users/joshfree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joshfree/subscriptions","organizations_url":"https://api.github.com/users/joshfree/orgs","repos_url":"https://api.github.com/users/joshfree/repos","events_url":"https://api.github.com/users/joshfree/events{/privacy}","received_events_url":"https://api.github.com/users/joshfree/received_events","type":"User","site_admin":false},"open_issues":1230,"closed_issues":109,"state":"open","created_at":"2015-09-29T00:25:45Z","updated_at":"2017-08-31T16:43:25Z","due_on":null,"closed_at":null},"comments":88,"created_at":"2016-08-13T06:31:14Z","updated_at":"2017-08-31T17:05:30Z","closed_at":null,"author_association":"NONE","body":"Let's keep the final speclet at the top for easier search\n# Speclet (Updated 9/23/2016)\n## Scope\n\nModernize `Collection<T>` and `ObservableCollection<T>` by allowing them to handle operations against multiple items simultaneously.\n## Rationale\n\nThe `ObservableCollection` is a critical collection when it comes to XAML-based development, though it can also be useful when building API client libraries as well. Because it implements `INotifyPropertyChanged` and `INotifyCollectionChanged`, nearly every XAML app in existence uses some form of this collection to bind a set of objects against UI.\n\nHowever, this class has some shortcomings. Namely, it cannot currently handle adding or removing multiple objects in a single call. Because of that, it also cannot manipulate the collection in such a way that the `PropertyChanged` events are raised at the very end of the operation.\n\nConsider the following situation: \n- You have a XAML app that accesses an API.\n- That API call returns 25 objects that need to be bound to the UI.\n- In order to get the data displayed into the UI, you likely have to cycle through the results, and add them one at a time to the ObservableCollection. \n- This has the side-effect of firing the `CollectionChanged` event 25 times. If you are also using that event to do other processing on incoming items, then those events are firing 25 times too. This can get very expensive, very quickly.\n- Additionally, that event will have `ChangedItems` Lists that will only ever have 0 or 1 objects in them. That is... not ideal.\n\nThis behavior is unnecessary, especially considering that `NotifyCollectionChangedEventArgs` already has the components necessary to handle firing the event once for multiple items, but that capability is presently not being used at all.\n\nImplementing this properly would allow for better performance in these types of apps, and would negate the need for the plethora of replacements out there ([here](http://blog.ddeysel.com/technicals/introducing-the-awesome-observable-collection/), [here](http://tliangnet.blogspot.com/2013/04/observablecollection-performance-issue.html), and [here](https://github.com/Windows-XAML/Template10/blob/master/Template10%20%28Library%29/Utils/IEnumerableUtils.cs#L34-L45), for example).\n## Usage\n\nGiven the above scenario as an example, usage would look like this pseudocode:\n\n``` cs\n    var observable = new ObservableCollection<SomeObject>();\n    var client = new HttpClient();\n    var result = client.GetStringAsync(\"http://someapi.com/someobject\");\n    var results = JsonConvert.DeserializeObject<SomeObject>(result);\n    observable.AddRange(results);\n```\n## Implementation\n\nThis is not the complete implementation, because other `*Range` functionality would need to be implemented as well. You can see the start of this work in PR #10751\n\n``` cs\n\n    // Adds a range to the end of the collection.\n    // Raises CollectionChanged (NotifyCollectionChangedAction.Add)\n    public void AddRange(IEnumerable<T> collection)\n\n    // Inserts a range\n    // Raises CollectionChanged (NotifyCollectionChangedAction.Add)\n    public void InsertRange(int index, IEnumerable<T> collection);\n\n    // Removes a range.\n    // Raises CollectionChanged (NotifyCollectionChangedAction.Remove)\n    public void RemoveRange(int index, int count);\n\n    // Will allow to replace a range with fewer, equal, or more items.\n    // Raises CollectionChanged (NotifyCollectionChangedAction.Replace)\n    public void ReplaceRange(int index, int count, IEnumerable<T> collection);\n\n    // Removes any item that matches the search criteria.\n    // Raises CollectionChanged (NotifyCollectionChangedAction.Remove)\n    // RWM: Excluded for now, will see if possible to add back in after implementation and testing.\n    // public int RemoveAll(Predicate<T> match);\n```\n## Obstacles\n\nDoing this properly, and having the methods intuitively named, could potentially have the side effect of breaking existing classes that inherit from `ObservableCollection` to solve this problem. A good way to test this would be to make the change, compile something like Template10 against this new assembly, and see if it breaks.\n\n---\n\nSo the `ObservableCollection` is one of the cornerstones of software development, not just in Windows, but on the web. One issue that comes up constantly is that, while the `OnCollectionChanged` event has a structure and constructors that support signaling the change for multiple items being added, the `ObservableCollection` does not have a method to support this.\n\nIf you look at the web as an example, Knockout has a way to be able to add multiple items to the collection, but not signal the change until the very end. The `ObservableCollection` needs the same functionality, but does not have it.\n\nIf you look at other extension methods to solve this problem, like the one in [Template10](https://github.com/Windows-XAML/Template10/blob/master/Template10%20%28Library%29/Utils/IEnumerableUtils.cs#L34-L45), they let you add multiple items, but do not solve the signaling problem. That's because the `ObservableCollection.InsertItem()` method overrides `Collection.InsertItem()`, and all of the other methods are private. So the only way to fix this properly is in the `ObservableCollection` itself.\n\nI'm proposing an \"AddRange\" function that accepts an existing collection as input, optionally clears the collection before adding, and then throws the `OnCollectionChanging` event AFTER all the objects have been added. I have already implemented this in a PR #10751 so you can see what the implementation would look like.\n\nI look forward to your feedback. Thanks!\n"},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/326360354","html_url":"https://github.com/dotnet/corefx/issues/10752#issuecomment-326360354","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/10752","id":326360354,"user":{"login":"robertmclaws","id":1657085,"avatar_url":"https://avatars3.githubusercontent.com/u/1657085?v=4","gravatar_id":"","url":"https://api.github.com/users/robertmclaws","html_url":"https://github.com/robertmclaws","followers_url":"https://api.github.com/users/robertmclaws/followers","following_url":"https://api.github.com/users/robertmclaws/following{/other_user}","gists_url":"https://api.github.com/users/robertmclaws/gists{/gist_id}","starred_url":"https://api.github.com/users/robertmclaws/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/robertmclaws/subscriptions","organizations_url":"https://api.github.com/users/robertmclaws/orgs","repos_url":"https://api.github.com/users/robertmclaws/repos","events_url":"https://api.github.com/users/robertmclaws/events{/privacy}","received_events_url":"https://api.github.com/users/robertmclaws/received_events","type":"User","site_admin":false},"created_at":"2017-08-31T17:05:30Z","updated_at":"2017-08-31T17:05:30Z","author_association":"NONE","body":"My plan for this weekend: \r\n- [ ] Review the following implementations:\r\n    - [ ] Xamarin's ObservableRangeCollection\r\n    - [ ] My existing PR to my personal .NET fork\r\n    - [ ] @weitzhandler's implementation\r\n- [ ] Pull the best from all 3 in a way that meets the pre-approved API surface and performs well w/o unnecessary allocations.\r\n- [ ] Submit a PR that includes testing for compatibility scenarios.\r\n\r\n**My over-arching concern is that we implement this in a way that does not break subclasses that already try to implement this functionality.**\r\n\r\nOnce we establish that this implementation meets the API surface already approved, and doesn't break compatibility at _this_ level, we can decide if the functions can be moved further down the stack without breaking anything else. But I don't think we should confuse the two right now, this whole process has been hard enough to follow ;).\r\n\r\nThoughts?"}}
{"actor":{"display_login":"robertmclaws"}}