Force sync'd access and issue with selecting multiple items
This is my current config:
ThrottledLifoPageRequests().AsyncIndexAccess((a, b) => {
return new T();
});
Question 1: It all works beautifully when used in a Grid, but what do I do if I need to actually grab the data? Use case: I want all items of the current collection to be processed in some way. If I just loop through all the items, I end up with a lot that are just filled with the placeholder null values. Is there a way to force grab all items? This is kind of the opposite of what this is meant to do, but it would be useful. However I could workaround this by just doing the fetching directly instead.
Question 2: Near the start of the DataGrid I'm able to select multiple rows, but as I get a bit into the dataset, whenever I select multiple rows, they stay selected for a short time but then immediately deselect again on their own. I did read the notes in https://github.com/Yeah69/BFF.DataVirtualizingCollection/wiki/Issue-of-Refreshes-while-selecting-items and I think this is probably related but I'm not sure what to do here. Selecting a single item works fine no matter where. And I'm not even trying to select items across multiple scrollings, just from the visible items. For fun I tried binding the SelectedIndex thing but it didn't do much.
Seems like when I select multiple items, for some reason the pageFetcher is called multiple times. Even though I am not scrolling or anything. This does not happen near the start of the DataGrid.
Some debugging shows that whenever multiple rows are selected, GetEnumerator() is called, which in turn causes a loop over all the items I guess? I wonder why.
~~Edit: Or I guess it doesn't iterate over all? It just calls Iterate() once starting at 0? And that in turn then causes the entire refresh?~~
Hi again. 👋
Question 1: It all works beautifully when used in a Grid, but what do I do if I need to actually grab the data? Use case: I want all items of the current collection to be processed in some way. If I just loop through all the items, I end up with a lot that are just filled with the placeholder null values. Is there a way to force grab all items? This is kind of the opposite of what this is meant to do, but it would be useful. However I could workaround this by just doing the fetching directly instead.
That's because the "async index access" mode behavior would create and return placeholders for items that are not fetched yet. You can only be sure that the index points to the real item after a CollectionChanged event for this index (I don't recommend to manually attaching the to event). That's how the DVC tells the UI to replace the temporary placeholder with real item.
I think your workaround is legit. It's not the focus of the DVC to iterate over all items. Also the DVC is meant to be a "bridge" between UI and data source, that means that the data source should be able to be worked with independent of the DVC. Just keep in mind - but you probably are aware of that - that the DVC wouldn't automatically update, so after processing the data source the DVC should be reset in order to update its items.
That said, you could still use a DVC for your use case. If you want to try I have some suggestions:
- If you need to have the "async index access" mode behavior for the UI. Use a second DVC for iterating over all items which is set to "sync index access". That way you don't need to wait for
CollectionChangedevents and can assume that the iterated items are the real ones, because no placeholders will be created. However, if your data source has async-API only, then there probably needs to be thread blocking somewhere along the way. Be cautious as such things might cause deadlocks. - Use "Least Recently Used" (LRU) page removal mode instead of the "Hoarding" mode. If iterating from start to finish with the "Hoarding" mode all items will be kept in memory. With LRU you'll set the DVC up to remove the pages of items that were least recently accessed (you can setup how many pages to hold and how many will get removed at once). That means you can have the DVC remove the items that are already processed as you progress.
Question 2: Near the start of the DataGrid I'm able to select multiple rows, but as I get a bit into the dataset, whenever I select multiple rows, they stay selected for a short time but then immediately deselect again on their own. I did read the notes in https://github.com/Yeah69/BFF.DataVirtualizingCollection/wiki/Issue-of-Refreshes-while-selecting-items and I think this is probably related but I'm not sure what to do here. Selecting a single item works fine no matter where. And I'm not even trying to select items across multiple scrollings, just from the visible items. For fun I tried binding the SelectedIndex thing but it didn't do much.
Selection is a tricky topic for the DVC. The main issue is that WPF-controls assume an ordinary list since the DVC implements IList. So they are probably are implemented with the assumption that in doubt all items may be iterated. One of such behaviors I have documented in the linked article. So far it is just known to me to happen whenever something is selected and the Reset() method is triggered which in turn leads to the DVC emitting an "Reset"-CollectionChanged-event. The control's reaction is to re-render everything and try to restore the previous state like the selections. For the selections it seemingly caches the references and tries to find their current new positions, first looking up their previous positions it that fails then iterating the list.
It's complicated with DVCs and selections. Also the behavior of WPF-controls is out of control, so workaround are quickly necessary. It's disappointing, but I would recommend restrict selection functionalities as much as possible. Avoid the headaches if selections aren't required.
With your selections issue were/are there Reset()-calls involved? If not can you reproduce it with the sample app in this repository? I've played around with the sample app myself and couldn't reproduce without Reset()-calls.
Seems like when I select multiple items, for some reason the pageFetcher is called multiple times. Even though I am not scrolling or anything. This does not happen near the start of the DataGrid.
Some debugging shows that whenever multiple rows are selected, GetEnumerator() is called, which in turn causes a loop over all the items I guess? I wonder why.
Yes. That's likely not caused by scrolling, but by some "confusion" of the DataGrid.
Let me know if you have ideas how to cope with such situation. Selection is an aspect to which I haven't found a satisfying solution yet.
I guess for question 1 I'll just go with fetching the data directly, that's going to be cleaner. But thank you for your suggestions.
For question 2, I just tried it with the Sample.View app that's part of the repo and I think I have successfully reproduced it. It shows you that list with all positive numbers. You scroll down a tiny little bit (but already end up in the millions) and you click on an entry, and that works fine. But click on an entry, and then hold shift and click a visible entry below to select multiple ones without scrolling or shifting the view at all and suddenly the app becomes unresponsive and if you pause it you'll realize it's going through the enumerator, extremely slowly. Tested this in Debug mode.
Thank you for the description. With it I could reproduce it as well. It's like you said, the DVC seems to get forced to load a bunch of pages. That issue is completely new to me. I would need to get deeper look into what exactly is happening there in order to maybe find a solution or workaround. Unfortunately it's hectic for me around the holidays so I can't promise to get back to it soon.
Glad you were able to reproduce it. No worries about the time schedule, I'm not in a great hurry on this.