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:
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 optionsGetTotalSummaries
calculates total summaries based on grid configurationGetUniqueValues
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:
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:
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" />
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.