I have an WPF app that sends a request to an ASP.NET Core 6 MVC app when a button is clicked. The action method in the MVC controller that receives the request, calls a method that runs a long task inside a for loop, and I am trying to use SignalR to update a label in the WPF app with the number of the loop iteration being processed.
The problem: after I click the button, the label does not change at all for the duration of the task, and only when the task ends it changes to show the number of the last iteration.
I guess its a problem with synchronous/asynchronous, but I am a noob at it and still can't seem to understand it...
This is the WPF button handler:
private async void myButton_Click(object sender, RoutedEventArgs e) {
HubConnection connection = new HubConnectionBuilder().WithUrl("https://localhost:7036/miHub").Build();
await connection.StartAsync();
connection.On<string>("Notify", (str) => { lblProgress.Content = str; });
//calls mvc controller action
string url = "https://localhost:7036/Sincro/procesa";
using (var httpClient = new HttpClient()) {
string json = await httpClient.GetStringAsync(url);
}
}
This is the controller:
public class SincroController : Controller {
private readonly IHubContext<MiHub> _hubContext;
//ctor
public SincroController(IHubContext<MiHub> hubContext) {
_hubContext = hubContext;
}
public async Task<JsonResult> procesa() {
await longProcess();
var json = new { foo = "bar" };
return Json(json);
}
private async Task<int> longProcess() {
for(int row=0; row<5000; row++) {
await _hubContext.Clients.All.SendAsync("Notify", $"Processing thing {row}");
}
return 0;
}
}
In your longProcess code, the for loop is actually very fast, try Thread.Sleep(1500);
.
Optimized test code
My Test Result
Your code test result
Sample Code
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button x:Name="button" Content="Invoke Api" HorizontalAlignment="Left" Margin="13,15,0,0" VerticalAlignment="Top" Click="button_Click"/>
<Label x:Name="lblSignalrStatus" Content="" HorizontalAlignment="Left" Margin="115,13,0,0" VerticalAlignment="Top" Width="367"/>
<Label x:Name="lblProgress" Content="" HorizontalAlignment="Left" Margin="205,45,0,0" VerticalAlignment="Top" Width="367"/>
<TextBox x:Name="textBoxProgress" AcceptsReturn="True" HorizontalAlignment="Left" Margin="12,87,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="744" Height="327"/>
<Label x:Name="label" Content="Label Content" HorizontalAlignment="Left" Margin="110,43,0,0" VerticalAlignment="Top"/>
<Label x:Name="label1" Content="TextBox Content" HorizontalAlignment="Left" Margin="12,55,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
HubConnection _connection;
public MainWindow()
{
InitializeComponent();
Connect();
}
private async void button_Click(object sender, RoutedEventArgs e)
{
//calls mvc controller action
string url = "https://localhost:44356/Test/procesa";
using (var httpClient = new HttpClient())
{
string json = await httpClient.GetStringAsync(url);
}
}
public async Task Connect()
{
_connection = new HubConnectionBuilder()
.WithUrl($"https://localhost:44356/mainHub")
.WithAutomaticReconnect()
.Build();
await _connection.StartAsync();
lblSignalrStatus.Content = "Connection started";
_connection.On<string>("Notify", OnNotifyHandler);
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
}
public Func<string, Task> NotifyHandler { get; set; }
private void OnNotifyHandler(string str) // => NotifyHandler?.Invoke(name);
{
this.Dispatcher.Invoke(() =>
{
string dtime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " ";
lblProgress.Content = dtime + str;
textBoxProgress.AppendText(dtime + str + "\r\n");
textBoxProgress.ScrollToEnd();
});
}
}
}