WPF Data Grid - Virtual Sources (v18.1)

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.

8 comment(s)

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

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

Can you use OData as a data source?

18 May, 2018

How to use Virtual Sources using MVVM Pattern?

18 May, 2018

How to implement Virtual Sources in MVVM architecture?

20 May, 2018

Is it available is winform grid?

21 May, 2018

@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

@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

Please login or register to post comments.