Skip to content

Connects the DevExpress WinForms Data Grid to a .NET Core service.

License

Notifications You must be signed in to change notification settings

DevExpress-Examples/connect-winforms-grid-to-dotnetcore-service

Repository files navigation

Connect the DevExpress WinForms Data Grid to a .NET Core Service

Please read the blog post for details about this sample.

Prerequisites

Getting Started

  1. Open NetCoreServiceAll.sln. Register a DevExpress NuGet feed in the Visual Studio IDE. Skip this step if you have already registered your DevExpress NuGet feed.

  2. Restore NuGet packages in the solution:

    Restore NuGet Packages in the Solution

  3. Run the DataService project. Right-click DataService in the Solution Explorer and select Debug | Start New Instance in the menu. Your default web browser will open.

    IMPORTANT

    Do not close the web browser.

  4. To initialize sample data when you run the service for the first time, navigate to http://localhost:5146/api/populateTestData in the open web browser. You will see the following message: "Data populated successfully".

  5. Run the WinForms.Client project. Right-click WinForms.Client in the Solution Explorer and select Debug | Start New Instance in the menu.

  6. You can scroll to the end of the data list in the grid and observe more data being loaded. You can sort by clicking column headers. The Output tool window within Visual Studio will display information about load status from both running processes. The animation below displays results:

    WinForms Client

Implementation Details

The Backend: an ASP.NET Core WebAPI service using Entity Framework Core

The backend project is called DataService and it was created using the standard ASP.NET Core WebAPI template. There are two endpoint handlers in the service, one to generate some test data and the other to query data.

The second handler, at the URL /data/OrderItems, accepts several optional parameters (to support skip, take, and sort features). The code queries data from the Entity Framework Core database context and uses standard IQueryable<T> based helpers to implement data shaping functionality. The TotalCount field is returned together with data and is used on the client side to determine the total amount of data available for the request.

app.MapGet("/data/OrderItems", async (
  DataServiceDbContext dbContext,
  int skip = 0, int take = 20,
  string sortField = "Id", bool sortAscending = true) =>
{
  var source =
    dbContext.OrderItems.AsQueryable()
      .OrderBy(sortField + (sortAscending ? " ascending" : " descending"));
  var items = await source.Skip(skip).Take(take).ToListAsync();

  var totalCount = await dbContext.OrderItems.CountAsync();

  return Results.Ok(new
  {
    Items = items,
    TotalCount = totalCount
  });
});

The Frontend: a Windows Forms app with a DevExpress Data Grid

In the MainForm of the Windows Forms application, the DevExpress GridControl control is bound to a VirtualServerModeSource instance (a collection of OrderItem objects). To fetch data, the VirtualServerModeSource handles the following events:

  • ConfigurationChanged - Fires when the grid changes relevant parts of its runtime configuration in response to user interaction (for example, when the user clicks a column header to apply sort operations).
  • MoreRows - Fires when an initial fetch operation returns a result, which indicates that more data is available. In this instance, the grid attempts to retrieve additional data rows if and when the user scrolls to the bottom of the currently loaded data set.

In this example, the data loaded from the backend is encoded as JSON. DataFetchResult models the structure of the response provided by the backend endpoint, including the TotalCount property:

public class DataFetchResult {
  public List<OrderItem> Items { get; set; } = null!;
  public int TotalCount { get; set; }
}

The GetRowsAsync method handles retrieved data. The method is invoked during the initial load (from the ConfigurationChanged handler) and subsequent loads (from the MoreRows handler). Data is fetched using HttpClient, with skip, take, and sorting properties passed as URL parameters. The results are deserialized from JSON and returned along with the moreRowsAvailable flag:

public Task<VirtualServerModeRowsTaskResult>
  GetRowsAsync(VirtualServerModeRowsEventArgs e)
{
  return Task.Run(async () =>
    {
      using var client = new HttpClient();
      var response = await client.GetAsync(
        $"{System.Configuration.ConfigurationManager.AppSettings["baseUrl"]}/data/OrderItems?skip={e.CurrentRowCount}&take={BatchSize}&sortField={SortField}&sortAscending={SortAscending}");
      response.EnsureSuccessStatusCode();
      var responseBody = await response.Content.ReadAsStringAsync();

      var dataFetchResult =
        JsonSerializer.Deserialize<DataFetchResult>(
          responseBody, new JsonSerializerOptions
          {
            PropertyNameCaseInsensitive = true
          });

      if (dataFetchResult is null)
        return new VirtualServerModeRowsTaskResult();

      var moreRowsAvailable =
        e.CurrentRowCount + dataFetchResult.Items.Count < dataFetchResult.TotalCount;

    return new VirtualServerModeRowsTaskResult(
      dataFetchResult.Items, moreRowsAvailable);
  }, e.CancellationToken);
}

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)