-
Notifications
You must be signed in to change notification settings - Fork 5
Configuring the Builder
This page provides rough explanations of the available configuration options. Certain parameters and their meanings are not described in full. The API documentation is available for this purpose.
public IList<int> AllPositiveIntNumbers { get; } =
DataVirtualizingCollectionBuilder
.Build<int>(
pageSize: 100,
notificationScheduler: new DispatcherScheduler(Application.Current.Dispatcher),
backgroundScheduler: TaskPoolScheduler.Default)
.NonPreloading()
.LeastRecentlyUsed(10, 5)
.NonTaskBasedFetchers(
pageFetcher: (offset, pageSize) => Enumerable.Range(offset, pageSize).ToArray(),
countFetcher: () => int.MaxValue)
.SyncIndexAccess();
This data virtualizing collection virtualizes an IList<int>
with a number sequence of 0 to int.Max
- 1. Hence, it emulates a list of maximum count, because the Count
property of IList<T>
is of type int
. Data virtualized collections can only be build with the DataVirtualizingCollectionBuilder
. In fact, it is the only externally public entry point of the whole library. The collections have to be build by calling the Build()
-function and making four decision. The deciding is done by choosing the appropriate function during the method chaining. The example above is the most simple in regard of the chosen options, which makes it the "Hello, world!" of this data virtualizing library. Here is a short introduction to the options:
The Build()
-function doesn't count as a decision. It is a necessity in order to create a new builder instance where onto the decisions are made. The pageSize
-parameter is optional and its default is 100. This parameter determines the usual requested page size (the last page may have a different size). The generic parameter sets up the item type.
The Build()
-function for the sliding window requires two additional parameter which configure the initial offset and size of the window.
This decides the loading process of the pages. The option here are NonPreloading()
and Preloading()
. If NonPreloading()
is active a page is loaded as soon as the first item of this page is requested but not sooner. If Preloading()
is active, then the neighboring pages (next and previous; if not done already) are loaded as soon as an item of the current page is requested. The assumption here is that given a certain starting point, the user is likely to scroll in any direction. Hence, the probability is high that one of the neighboring pages is requested soon. The preloading happens in background, so requests of the current page are not blocked.
The second decision sets the page-removal strategy. At the moment of writing there are three option: Hoarding()
, LeastRecentlyUsed(…)
, and CustomPageRemovalStrategy(…)
.
- Hoarding The pages are never removed until the data virtualizing collection is refreshed or disposed of.
- LeastRecentlyUsed As soon as the page limit (which you have to set) is reached the least recently used page or pages (you can set how many pages are removed at once) are removed.
- CustomPageRemovalStrategy You can define you own page removal strategy. In order to accomplish that you'll get an observable which streams the page key (index of all pages) and page index (index of element inside the page) of the indexer calls of the data virtualizing collection. The LeastRecentlyUsed-strategy was implemented the same way, I would recommend to look into its sources first if you would like to implement your own strategy.
Next, the fetcher have to be defined. In order to work properly the data virtualizing collection needs two kinds of fetcher. The page fetcher is given the integer parameters offset
and pageSize
(doesn't have to be the same value as provided to Build()
) and expects an array of elements with amount of pageSize
starting from the offset
. What the page fetcher has to do in order to accomplish this result depends on the data source which is virtualized. In the example above the numbers are procedurally generated. If the data source is a database, then the page fetcher could delegate the request to ORM or a query of some kind.
The count fetcher tells the amount of items of the data source to the data virtualizing collection. Again, the actual implementation will depend on the kind of data source.
It can be decided whether the provided fetcher are synchronous or task-based asynchronous functions.
If you choose the task-based option then the synchronous Index Access option in the next decision isn't available. For one thing, I did not see any real benefit in combining asynchronous fetchers with synchronous index accesses. On the other hand, implementing this combination would require blocking calls of the asynchronous fetchers. The latter is not only technically unclean, but also dangerous to use. Because if you don't expect hidden blocking calls, you can quickly run into a deadlock.
The last decision determines whether the item access is synchronous (Sync()
) or asynchronous (Async([…])
). The synchronous variant would block the thread and wait if an item from a not-yet-loaded page is requested. In same situation the asynchronous variant would return a placeholder (the user has to provide a placeholder-function) first instead of blocking the current thread and emit a notification event as soon as the item arrives. In case the page is already loaded the right item is returned directly and no notification is emitted. That way or the other the asynchronous variant does not block.
This last call returns the build data virtualized collection.