0

Edit for up-front clarity: This needs to work in Universal Windows Platform application. Maybe it's not relevant, but things such as Thread.Start() are not available to me.

I want text blocks to update as indicated in the code below.

But text blocks only update AFTER "Btn_Click()" is finished.

How can I make this work?

private void Btn_Click()
{
     TextBlock1.Text = "Work started."
     DoWork();
     TextBlock1.Text = "Work done."
}

private void DoWork()
{
     myTextBlock2.Text = "Step 1"
     Step1();
     myTextBlock2.Text = "Step 2"
     Step2();
     myTextBlock2.Text = ""
}
Dpt
  • 29
  • 4

2 Answers2

0

This is a classic async problem.

You have to make your DoWork function asynchronous, and than call it using await. Like this.

private async void Btn_Click()
{
    TextBlock1.Text = "Work started."
    await DoWork();
    TextBlock1.Text = "Work done."
}

If you have questions about asynchronous programming, visit these documentations:

https://learn.microsoft.com/en-us/dotnet/csharp/async

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

https://learn.microsoft.com/en-us/dotnet/standard/async-in-depth

EDIT:

This is an example on how to implement an async program.

private async void Btn_Click()
{
    TextBlock1.Text = "Work started."
    await DoWork();
    TextBlock1.Text = "Work done."
}

private async Task DoWork()
{
    myTextBlock2.Text = "Requesting html from the web.";

    HttpClient _httpClient = new HttpClient();
    string data = await _httpClient.GetStringAsync("example.com/stringdata.html");
    //here the function stops executing, and doesn't continue until GetStringAsync returns a value. 
    //While this, it gives back control to the callee (AKA the UI thread, so it doesn't block it, 
    //and can process data like mouse movement, and textblock text changes.)

    myTextBlock2.Text = "Processing data";
    string outputData = await Task.Run(() => HighCPUBoundWork(data));

    //The CPU bound work doesn't block the UI thread, because we created a new thread for it.
    //When it completes, we continue.

    myTextBlock2.Text = "Writing output data to file";

    using (StreamWriter writer = File.CreateText("output.txt"))
    {
        await writer.WriteAsync(outputData);
        //Same thing applies here, we are waiting for the OS to write to the specified file, 
        //and while it is doing that, we can get back to the UI.
    }
}

private string HighCPUBoundWork(string data)
{
    //Here you can do some high cpu intensity work, like computing PI to the 1000000000th digit, or anything.
    //The html files processing would be more likely tho.. :)
    //Notice, that this function is not async.

    return modifiedInputData;
}
rokkerboci
  • 1,167
  • 1
  • 7
  • 14
  • Thank you for responding to my question. Can we get into greater detail about exactly how to implement it in this case? I've been doing research prior to posting this question, and got myself in a lot of difficulty trying to use Task Asynchronous Pattern to fix things. In an attempt to ask for an explicit answer to the question, I ended up dumbing down the question to what we see here. – Dpt Nov 28 '17 at 16:31
  • I request an explicit answer so that I can build off of it. Problem with just recommending async is that compiler complains it cannot await DoWork since it returns void. Well, okay- I change DoWork into an async Task. But then things continue to not behave as I expect, and my troubleshooting leads me to a very complicated and not fully functional solution. This is the link to my question where I laid things out in more detail: (it wasn't well-received, and I didn't find a workable solution) https://stackoverflow.com/questions/47520701/c-sharp-uwp-xaml-updating-controls-synchronously – Dpt Nov 28 '17 at 16:34
  • I have looked at that question, but I think it would be better if you showed me the original Step1() and Step2() functions so I can show you how to make them async – rokkerboci Nov 28 '17 at 16:55
  • Thanks again for replying! In this example, I hope I can get away with saying that I don't care what's inside step1 or step2. As a matter of fact, I think it would greatly help me understand how to build a solution even if we assume they are defined like so: "private void Step1() {}"... or let them be defined however you want them to be defined, or we could remove them from the example too! – Dpt Nov 28 '17 at 17:30
  • @Dpt How you make an operation asynchronous is *highly* dependant on what that operation is. There *is* no general purpose way to make any operation asynchronous. – Servy Nov 28 '17 at 17:38
  • I think I understand your point. If you insist, I can send you the code I've developed (actually, it can be found in the link from my 2nd comment on your reply). I just hoped to avoid confusing things by adding more variables to this question. I am hopeful that if I can understand how to make this work in the MOST simple case (where there is nothing inside Step1/Step2), then I can go off and do FRUITFUL research about how to apply it to a real scenario! – Dpt Nov 28 '17 at 17:42
  • You should take a look at my answer, I have edited it with an example. – rokkerboci Nov 28 '17 at 19:12
  • @rokkerboci I appreciate the effort, but I wasn't able to make it work. If you think you can still assist... I've boiled down the question about as far as I think I can over here: https://stackoverflow.com/questions/47557626/c-sharp-user-interface-updates-are-not-deterministic – Dpt Nov 29 '17 at 17:56
-1

What you need to do is to use Async by make Thread-Safe Calls in Windows Forms Controls. This is also a good example by Microsft Example

Try this code:

 private async void button1_Click_1(object sender, EventArgs e)
    {
        button1.Text = "Work started.";
        Task myTask = new Task(DoWork);
        myTask.Start();
        await myTask;
        button1.Text = "Work done.";
    }

 private void DoWork()
    {
        this.SetMessageText("Step 1");
        Thread.Sleep(2000); // --> you can replace it with your actual method
        this.SetMessageText("Step 2");
        Thread.Sleep(2000); // --> you can replace it with your actual method
        this.SetMessageText(" ");
    }

This delegate enables asynchronous calls for setting the text property on a TextBox control.

delegate void StringArgReturningVoidDelegate(string text); 

This method demonstrates a pattern for making thread-safe calls on a Windows Forms control.

If the calling thread is different from the thread that created the TextBox control, this method creates a StringArgReturningVoidDelegate and calls itself asynchronously using the Invoke method.

If the calling thread is the same as the thread that created the TextBox control, the Text property is set directly.

private void SetMessageText(string text)
    {
        // InvokeRequired required compares the thread ID of the  
        // calling thread to the thread ID of the creating thread.  
        // If these threads are different, it returns true.  
        if (this.label1.InvokeRequired)
        {
            StringArgReturningVoidDelegate d = new StringArgReturningVoidDelegate(SetMessageText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.label1.Text = text;
        }
    }
behdad
  • 44
  • 5
  • Thank you so much for responding! Can you think of any reason that, when I try to implement your suggestion, Visual Studio complains the following: "'TextBlock' does not contain a definition for 'InvokeRequired' and no extension method 'InvokeRequired' accepting a first argument of type 'TextBlock' could be found" Similar complaint from Visual Studio regarding "this.Invoke(d, new object[] {text})". Is this a limitation of UPW, or am I making a mistake? – Dpt Nov 28 '17 at 17:38
  • Is your TextBlock a label or a textBox? what version of .NET framework are you using? – behdad Nov 28 '17 at 17:55
  • The class of the object "TextBlock1" in my example is "TextBlock". I hope that I answered your question, but please let me know if I did not and I can try again. I am not sure which version of the .NET Framework I am using. I don't know because I simply installed Visual Studio 2017 and began building a UWP application. I understand the version is probably very relevant to this question, so I can get back to you on that question ASAP. – Dpt Nov 28 '17 at 18:31
  • My UWP application targets "Windows 10 Creators Update" which corresponds to .NET Framework 4.7 – Dpt Nov 28 '17 at 18:42
  • I am out of guess now. it should work fine. If I were you, I would create one simple windows application and try that code to see the behavior of the code – behdad Nov 28 '17 at 18:53