-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.

10
  • create a static webmethod and call it with ajax in timeinterval. Commented Dec 22, 2018 at 19:17
  • Either asynchronous code with Invoke or synchronous code without Invoke. You cannot modify UI objects from another thread. Commented Dec 22, 2018 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. Commented Dec 22, 2018 at 19:24
  • @CamiloTerevinto - async doesn't mean threaded. Commented Dec 22, 2018 at 20:20
  • @HenkHolterman You're right, the comment wasn't the best. The OP is explicitly using multi-threading though Commented Dec 22, 2018 at 20:21

3 Answers 3

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;
}
Sign up to request clarification or add additional context in comments.

4 Comments

You still have a Task.Run to execute Thread.Sleep()
@HenkHolterman - I think the OP does CPU-bound calculations in real code instead of Sleep.
Maybe. Maybe not.
please see my edit part and guide me again for my new sample code.
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
    }    
}

1 Comment

please see my edit part and guide me again for my new sample code.
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.

1 Comment

please see my edit part and guide me again for my new sample code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.