NOTE This example extends the capabilities (introduces data editing support when using the WinForms Data Grid through accessible endpoints) of the following example: Connect the DevExpress WinForms Data Grid to a .NET Core Service. This example implements a REST-style access pattern.
Review the following step-by-step tutorial to run the example: Getting Started.
The ASP.NET Core MapPost
, MapPut
, and MapDelete
methods manage different HTTP methods when processing incoming requests.
MapPost
: Manages HTTP POST requests, generally used to create new data.MapPut
: Processes HTTP PUT requests, used to update existing data.MapDelete
: Manages HTTP DELETE requests, used to remove records.
app.MapPost("/data/OrderItem", async (DataServiceDbContext dbContext, OrderItem orderItem) => {
dbContext.OrderItems.Add(orderItem);
await dbContext.SaveChangesAsync();
return Results.Created($"/data/OrderItem/{orderItem.Id}", orderItem);
});
app.MapPut("/data/OrderItem/{id}", async (DataServiceDbContext dbContext, int id, OrderItem orderItem) => {
if (id != orderItem.Id) {
return Results.BadRequest("Id mismatch");
}
dbContext.Entry(orderItem).State = EntityState.Modified;
await dbContext.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/data/OrderItem/{id}", async (DataServiceDbContext dbContext, int id) => {
var orderItem = await dbContext.OrderItems.FindAsync(id);
if (orderItem is null) {
return Results.NotFound();
}
dbContext.OrderItems.Remove(orderItem);
await dbContext.SaveChangesAsync();
return Results.NoContent();
});
This example includes a standalone data editing form. The edit form is used to modify existing data rows and add new rows.
The example uses the DevExpress WinForms Form Layout component to automatically arrange DevExpress Data Editors and eliminate the hassles associated with pixel-based form design.
The DataServiceClient
class is an abstraction designed to simplify data service interactions (by encapsulating common HTTP operations).
The following code in VirtualServerModeDataLoader.cs calls the DataServiceClient.GetOrderItemsAsync
method to fetch data. The GetOrderItemsAsync
method fetches a list of order items by specifying row counts, batch sizes, and sorting parameters:
public Task<VirtualServerModeRowsTaskResult> GetRowsAsync(VirtualServerModeRowsEventArgs e) {
return Task.Run(async () => {
Debug.WriteLine($"Fetching data rows {e.CurrentRowCount} to {e.CurrentRowCount + BatchSize}, sorting by {SortField} ({(SortAscending ? "asc" : "desc")})");
var dataFetchResult = await DataServiceClient.GetOrderItemsAsync(e.CurrentRowCount, BatchSize, SortField, SortAscending);
if (dataFetchResult is null)
return new VirtualServerModeRowsTaskResult();
var moreRowsAvailable = e.CurrentRowCount + dataFetchResult.Items.Count < dataFetchResult.TotalCount;
Debug.WriteLine($"Returning {dataFetchResult.Items.Count} items, more rows available: {moreRowsAvailable}");
return new VirtualServerModeRowsTaskResult(dataFetchResult.Items, moreRowsAvailable);
}, e.CancellationToken);
}
The DataServiceClient.UpdateOrderItemAsync
method encodes the transfer object as JSON and sends it to the service URL using a PUT request. The UpdateOrderItemAsync
method invokes EnsureSuccessStatusCode()
after the PUT request to ensure the object is successfully updated.
public static async Task UpdateOrderItemAsync(OrderItem orderItem) {
using var client = CreateClient();
var response = await client.PutAsync($"{baseUrl}/data/OrderItem/{orderItem.Id}",
new StringContent(JsonSerializer.Serialize(orderItem), Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
The edit form is displayed when a user double-clicks a data row:
async void gridView1_DoubleClick(object sender, EventArgs e) {
if (sender is GridView view) {
if (view.FocusedRowObject is OrderItem oi) {
var editResult = EditForm.EditItem(oi);
if (editResult.changesSaved) {
await DataServiceClient.UpdateOrderItemAsync(editResult.item);
view.RefreshData();
}
}
}
}
The DataServiceClient.CreateOrderItemAsync
method creates a new OrderItem
. The CreateOrderItemAsync
method serializes the object, sends it to the service, and deserializes the response back into an OrderItem
. This pattern supports server-generated values, such as auto-incremented keys.
public static async Task<OrderItem?> CreateOrderItemAsync(OrderItem orderItem) {
using var client = CreateClient();
var response = await client.PostAsync($"{baseUrl}/data/OrderItem",
new StringContent(JsonSerializer.Serialize(orderItem), Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
return responseBody.AsOrderItem();
}
static OrderItem? AsOrderItem(this string responseBody) {
return JsonSerializer.Deserialize<OrderItem>(responseBody, new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
To add a new data row, click the "Add Order Item" item within the toolbar.
async void addItemButton_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
if (gridControl.FocusedView is ColumnView view) {
var createResult = EditForm.CreateItem();
if (createResult.changesSaved) {
await DataServiceClient.CreateOrderItemAsync(createResult.item!);
view.RefreshData();
}
}
}
The DataServiceClient.DeleteOrderItemAsync
method catches any exceptions and returns a Boolean value to indicate success or failure. The DeleteOrderItemAsync
method logs errors using Debug.WriteLine()
and returns false if an error occurs (allowing the UI or business logic to respond appropriately).
public static async Task<bool> DeleteOrderItemAsync(int id) {
try {
using var client = CreateClient();
var response = await client.DeleteAsync($"{baseUrl}/data/OrderItem/{id}");
response.EnsureSuccessStatusCode();
return true;
}
catch (Exception ex) {
Debug.WriteLine(ex);
return false;
}
}
To delete the focused data row, click the "Delete Focused Item" item within the toolbar.
async void deleteItemButton_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
if (gridControl.FocusedView is ColumnView view &&
view.GetFocusedRow() is OrderItem orderItem) {
await DataServiceClient.DeleteOrderItemAsync(orderItem.Id);
view.RefreshData();
}
}
- Connect the DevExpress WinForms Data Grid to a .NET Core Service
- Connect a WinForms Data Grid to an ASP.NET Core WebAPI Service Powered by EF Core — Authenticate Users and Protect Data
- Connect the DevExpress WinForms Data Grid to a Backend using a Middle Tier Server (EF Core without OData)
- Connect a WinForms Data Grid to an Arbitrary ASP.NET Core WebAPI Service Powered by EF Core — Architecture and Data Binding (Part 1)
- Connect a WinForms Data Grid to an Arbitrary ASP.NET Core WebAPI Service Powered by EF Core — Add Editing Features (Part 2)
(you will be redirected to DevExpress.com to submit your response)