-1

I saw many code that they use BeginInvoke to update UI from another thread. is it possible to update UI from async function without BeginInvoke ?

private async void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            var count = 0;

            await Task.Run(() =>
            {
                for (var i = 0; i <= 500; i++)
                {
                    count = i;
                    BeginInvoke((Action)(() =>
                    {
                        label1.Text = i.ToString();

                    }));
                   Thread.Sleep(100);
                }
            });

            label1.Text = @"Counter " + count;
            button1.Enabled = true;
        }

Edit

see the below code i have got from a link which show that without BeginInvoke we can update UI when using task.run.

private readonly SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;

public Form1()
{
    InitializeComponent();
    synchronizationContext = SynchronizationContext.Current;
}

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    var count = 0;

    await Task.Run(() =>
    {
        for (var i = 0; i <= 5000000; i++)
        {
            UpdateUI(i);
            count = i;
        }
    });

    label1.Text = @"Counter " + count;
    button1.Enabled = true;
}

public void UpdateUI(int value)
{
    var timeNow = DateTime.Now;

    if ((DateTime.Now - previousTime).Milliseconds <= 50) return;

    synchronizationContext.Post(new SendOrPostCallback(o =>
    {
        label1.Text = @"Counter " + (int)o;
    }), value);             

    previousTime = timeNow;
} 

So tell me synchronizationContext and BeginInvoke both are same ? which one should use to update UI from different thread ? which one is most efficient ?

please guide me i am new in async/await & Task usage.

Mist
  • 684
  • 9
  • 30
  • create a static webmethod and call it with ajax in timeinterval. – Murat Can OĞUZHAN Dec 22 '18 at 19:17
  • Either asynchronous code with Invoke or synchronous code without Invoke. You cannot modify UI objects from another thread. – Camilo Terevinto Dec 22 '18 at 19:20
  • Why do you try to avoid `(Begin)Invoke`? These methods were designed specially to make easier an interprocess communications. I think, you can take a look on the `ConfigureAwait` method of the `Task` class - it deals with execution context and can be helpful for you. – Miamy Dec 22 '18 at 19:24
  • @CamiloTerevinto - async doesn't mean threaded. – H H Dec 22 '18 at 20:20
  • @HenkHolterman You're right, the comment wasn't the best. The OP *is* explicitly using multi-threading though – Camilo Terevinto Dec 22 '18 at 20:21
  • please see my edit part and guide me again for my new sample code. – Mist Dec 23 '18 at 08:38
  • You should have asked a new separate question about SynchronizationContext. | In short: Yes, are the same. – Alexander Petrov Dec 23 '18 at 08:52
  • "... we can update UI when using task.run." Yes, but we shouldn't want/need to. Design to separate workload from the GUI. – H H Dec 23 '18 at 09:11
  • just post a new thread https://stackoverflow.com/questions/53902437/c-begininvoke-and-synchronizationcontext-both-does-the-same-thing please guide me there if possible. – Mist Dec 23 '18 at 09:14
  • @HenkHolterman it is not clear what you try to mention. – Mist Dec 23 '18 at 09:15

3 Answers3

2

Use Progress class.

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    var count = 0;

    // The Progress<T> constructor captures UI context,
    // so the lambda will be run on the UI thread.
    IProgress<int> progress = new Progress<int>(value =>
    {
        label1.Text = value.ToString();
    });

    await Task.Run(() =>
    {
        for (var i = 0; i <= 500; i++)
        {
            count = i;
            progress.Report(i);
            Thread.Sleep(100);
        }
    });

    label1.Text = @"Counter " + count;
    button1.Enabled = true;
}
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
2

The thing to avoid is Task.Run(). And when you manage that, you won't need [Begin]Invoke().

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    var count = 0;

    for (var i = 0; i <= 500; i++)
    {
        count = i;          
        label1.Text = i.ToString();
        await Task.Delay(100);         // do async I/O or Task.Run() here
    }    
}
H H
  • 263,252
  • 30
  • 330
  • 514
1

It will depend on what kind of asynchronous method you are using. For example asynchronous method which access external resources (database, webservices, file system etc.) will be executed on the same thread and you don't need to bother with Invoke.

private async Task UpdateDatabase()
{
    using (var connection = new SqlConnection(connectionString))
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT Id FROM Table";
        await connection.OpenAsync();
        using (var reader = await command.ExecuteReaderAsync())
        {
            var rowsCount = 0;
            // Since execution will happen on same thread 
            // you will be able update UI controls.
            Label1.Text = $"Rows: {rowsCount}";

            while (await reader.ReadAsync())
            {
                rowsCount ++;
                Label1.Text = $"Rows: {rowsCount}";
            }
        }
    }
}

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;

    await UpdateDatabase();

    button1.Enabled = true;
}

For methods which are executed on other threads, best practice is to execute those methods without "touching" UI controls, instead return result of the method back to main thread and then update UI.

For your particular case where you want update UI with "progress" information, then you can use BackgroundWorker class or already mentioned Progress class.

Fabio
  • 31,528
  • 4
  • 33
  • 72