WPF Data Grid - Virtual Sources (v18.1)

WPF Team Blog
18 May 2018

The WPF Data Grid has always supported Server Mode and Instant Feedback sources that allow you to process data requests on the server side. This is a good choice and a general recommendation when your data source represents a large number of records. However, these features depend on a provider implementation specific to the data access technology you use. Given the large number of general purpose data access layers out there, we may not always supply an implementation “in the box” that allows you to do exactly what you need. What’s more, the standard providers we offer don’t restrict queries that may be executed by a client. This behavior can be too “open” for a specific application scenario, and it’s difficult to apply meaningful security rules to the complex processing in a Server Mode provider.

In the past, it would have been possible to address these concerns by creating custom implementations of certain interfaces to build special server side data sources. However, this is a rather complicated process and we tried hard to find a better way.

Virtual Sources

For our v18.1 release, we built the new Virtual Sources. Using this technology, you can easily implement server-side processing for the Data Grid with almost any data source, including these:

  • REST services
  • NoSQL databases
  • N-Tier style data services
  • Object/relational mappers and databases not supported by Server Mode sources (NHibernate, Dapper, etc.)

For example, this image shows the GridControl bound to the StackOverflow web service:

StackOverflow web service

Implementation

Virtual Sources proxy data requests to the server. This gives the developer of a Virtual Source full control over the queries that are executed. For instance, if sorting and filtering are not required or not supported by the server, an implementation only needs to fetch a specific number of rows while applying an offset (this is the basic skip/take feature set):

static async Task<FetchRowsResult> FetchRowsAsync(FetchRowsAsyncEventArgs e) {
  return await MyService.GetRowsAsync(skip: e.Skip, take: 30);
}

If you need sorting and filtering, you can pass these parameters to your service at the same point. The event FetchRows (see below) is called every time an end-user sorts or filters data:

static async Task<FetchRowsResult> FetchRowsAsync(FetchRowsAsyncEventArgs e) {
  return await IssuesService.GetIssuesAsync(
    skip: e.Skip,
    take: 30,
    sortOrder: GetIssueSortOrder(e),
    filter: MakeIssueFilter(e.Filter));
}

For sorting, you need to pass to the server the direction and the column names provided by the grid:

static IssueSortOrder GetIssueSortOrder(FetchRowsAsyncEventArgs e) {
  var sort = e.SortOrder.SingleOrDefault();
  if(sort?.PropertyName == "Votes") {
    return sort.Direction == ListSortDirection.Ascending
      ? IssueSortOrder.VotesAscending
      : IssueSortOrder.VotesDescending;
  }

  return IssueSortOrder.Default;
}

If filtering is applied to the grid, a CriteriaOperator value is supplied that represents the filter setup. You need to convert this value into a filter expression supported by your server:

static IssueFilter MakeIssueFilter(CriteriaOperator filter) {
  return filter.Match(
    binary: (propertyName, value, type) => {
      if (propertyName == "Priority" && type == BinaryOperatorType.Equal)
        return new IssueFilter(priority: (Priority)value);
      throw new InvalidOperationException();
    }
  );
}

To implement a Virtual Source, you begin by instantiating a class derived from VirtualSourceBase, of which there are currently four: InfiniteSource and InfiniteAsyncSource, PagedSource and PagedAsyncSource (see below about infinite scrolling and paging). On that instance, you hook up the following three event handlers. Together, they cover all data operations except for grouping, which is not supported.

  • FetchRows/FetchPage fetches N rows, with parameters specifying offset, filter and sort options

  • GetTotalSummaries calculates total summaries based on grid configuration

  • GetUniqueValues retrieves a list of distinct values for display in a column filter drop-down

Note that you only need to implement those event handlers that are used by your grid configuration! If your grid doesn’t display total summaries or has column filtering disabled, you don’t have to supply an implementation of GetTotalSummaries or GetUniqueValues.

Virtual Sources also have default implementations that are used automatically if your data source supports IQueryable. In contrast to Server Mode implementations, your IQueryable doesn’t need to support all operations. Only the Skip and Take methods are required to load segments of rows.

In addition to the convenient API discussed above, Virtual Sources have several other advantages:

  • All data requests are executed in a separate thread, without ever freezing the UI.

  • Virtual Sources can run multiple data requests in parallel, dispatching tasks and canceling them if results are not needed anymore.

  • Data requests are independent. For example, if your query for summary values fails, the grid will still display records returned by the FetchRows query.

Infinite Scrolling and Paging

Together with Virtual Sources, we ship two new UI modes for the GridControl in v18.1, designed specifically for server-side data. The first mode is called Infinite Scrolling and can often be seen in web interfaces. Virtual Sources make this possible, and the feature is only available when using this new type of data source.

In Infinite Scrolling mode, the scroll bar size is determined on the basis of loaded records only. When the user scrolls to the bottom, the grid loads a new batch of rows:

Infinite Scrolling

Depending on your grid configuration, new rows will either be loaded automatically or when an end-user clicks the Load More button displayed at the bottom.

The second new mode is called Paging. This mode can be used with Server Mode data sources as well as Virtual Sources, or even with local data. Like Infinite Scrolling, it loads data in portions, but it displays a pager below the grid:

Paging

This mode allows the user to navigate directly to a specific page of data, and it detects the total number of pages available in the data source.

UI Restrictions

A final concept we implemented for advanced server-side data access restricts what an end-user can do in the GridControl. By default, the grid enables users to construct complex queries easily by taking advantage of interactive sorting or complicated filters. Depending on the server, there is a risk that such a query will run slowly or even fail entirely, leaving the grid empty.

With Virtual Sources, all data operations other than simple row fetching are disabled by default. If you support a certain data operation in your Virtual Source implementation, you can enable this data operation in the UI. For instance, this line enables sorting for the Date column:

<dxg:GridColumn FieldName="Date" AllowSorting="True" DefaultSortOrder="Descending" />

It is also possible to specify which filter operators a column supports. The GridControl will hide all unsupported operators for a column in the Filter Editor, the column filter drop-down and the automatic filter row.

<dxg:GridColumn FieldName="Votes" AllowedBinaryFilters="GreaterOrEqual,LessOrEqual" />
<dxg:GridColumn FieldName="Priority" AllowedBinaryFilters="Equals" />

Filtering Restrictions

Try it now!

Virtual Sources and UI restriction settings are available in our recent v18.1 release. Documentation is available, as well as an example on GitHub. If you have installed the local demos, you can also look for the Infinite Scrolling Source and Paged Source demos in the WPF/Data Grid category (here are direct links, which may work depending on your browser: Infinite Scrolling Source Demo, Paged Source Demo).

What do you think? Is server-side data processing something you’d like to implement in your app? What data source are you using? Let us know in the comments section below.

Free DevExpress Products – Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We’ll be happy to follow-up.
Jochen Möller
Jochen Möller

This looks great! I especially like the filter method enabling stuff and the infinite scroll. this is what we have currently implemented with a custom servermode.

just to be curious: are there plans to support grouping in this scenario, and how would it look like if you get push notifications that an item was changed (like item with ID 1234 was changed) how would the grid be invalidated and how does caching work? :)

thanks for sharing, keep up the good work! :)

18 May 2018
Alex Chuev (DevExpress)
Alex Chuev (DevExpress)

I'm glad you like the feature, Jochen!

Grouping is tricky with Infinite Scrolling, since it will significantly complicate both the API and our internal implementation. Therefore, we are not currently planning to implement it with Virtual Sources.

Push notifications are a broad topic - it depends on what source you use, whether you want to update properties or replace loaded objects, how updated records should be filtered/sorted, etc. In general, the easiest solution is to call the RefreshRows method to re-fetch records. For more complex cases, please feel free to submit a support ticket where we can discuss the details.

18 May 2018
Andy Turner 1
Andy Turner 1

Can you use OData as a data source?

18 May 2018
Noufal Aboobacker 1
Noufal Aboobacker 1

How to use Virtual Sources using MVVM Pattern?

18 May 2018
Espirit Developer
Espirit Developer

How to implement Virtual Sources in MVVM architecture?

20 May 2018
Hedi Guizani
Hedi Guizani

Is it available is winform grid?

21 May 2018
Dmitry Babich (DevExpress)
Dmitry Babich (DevExpress)

@Hedi

Yes, we have added support for Infinite Scrolling in WinForms starting with version 18.1 as well. You can use VirtualServerModeSource for this purpose. If v18.1 is installed on your side, you can see it in action in the Infinite Scrolling module of the XtraGrid MainDemo.

21 May 2018
Ivan (DevExpress Support)
Ivan (DevExpress Support)

@Andy

Yes, our Virtual Source components can be used with any data access technology including OData. We've just published a corresponding example - How to Bind to OData. Please take a moment to review it.


@Noufal @Espirit Developer

I have described our position in the How to implement Virtual Data Source with Grid control in MVVM Framwork ticket. Let's continue our discussion there.

21 May 2018
Andrei Semianenka
Andrei Semianenka

Great feature! Just what I was need to implement in current project. I work with REST service.

Are there any chances that WPF Scaffolding Wizard will support Virtual Sources some day?

community.devexpress.com/.../devexpress-wpf-scaffolding-new-ui-templates-features-and-tutorials-in-14-2.aspx

10 July 2018
Alex Chuev (DevExpress)
Alex Chuev (DevExpress)

Hi Andrei,

We have no plans to support custom data sources in Scaffolding Wizards at the moment.

Thanks,

Alex

10 July 2018
Customer37300
Customer37300

Hello, I've just tested virtual sources with Dapper and MVVM pattern and it works great.

But I've run up to a small problem with binding gridcontrols SelectedItem property.

It is returnig type DevExpress.Xpf.Data.Native.SyncSourceEventsHelper.ThreadSafeProxy, which has one object[] field "Values" that contains all selected row data.

Is there any way to get SelectedItem as instance of my Model class, which is used in GridControls ItemSource collection?

25 July 2018
Alex Chuev (DevExpress)
Alex Chuev (DevExpress)

The InfiniteSource/PagedSource classes raise events where you can request data in a separate thread. Accessing your data objects and their properties/methods in the UI thread may lead to unexpected issues. This is why the SelectedItem property returns the ThreadSafeProxy class with column values instead.

If you are sure that accessing your data objects does affect other records, you can set the AreSourceRowsThreadSafe property (documentation.devexpress.com/.../DevExpress.Xpf.Data.InfiniteSource.AreSourceRowsThreadSafe.property) for your InfiniteSource/PagedSource instance.

Another solution is to use AsyncInfiniteSource/AsyncPagedSource classes. These classes can process multiple data requests in parallel and expect your data requests to be independent (e.g., a new DbContext instance for every request). Since data objects from different requests cannot affect each other, they will be available in the UI thread.

Please check the following article to learn more about the difference between Regular and Async sources: documentation.devexpress.com/.../Virtual-Sources-Types

25 July 2018
steve frierdich
steve frierdich
How can one prevent the  Load More button from being shown
6 March 2023
steve frierdich
steve frierdich
How can one prevent the  Load More button from being shown
6 March 2023

Please login or register to post comments.