1

This is my first question here. As English is not my first language, forgive me for any mistakes.

I'm trying to develop an application for Windows Phone 8.1 (XAML and C#) and I'm using .NET Framework 4.5.2. I just started studying multithreading in C# and would appreciate if anyone here could help me. All answers to related questions I've found so far are too complex.

All I need is to create a new task from a button click that displays a message in a textblock control.

private void myButton_Click(object sender, RoutedEventArgs e)
{
    Task t = new Task(MyMethod);
    t.Start();
}

private void MyMethod()
{
    myTextBlock.Text = "Worked!";
}

I'm getting the following exception: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)).

How can I correct this?

Thanks in advance!

Saadi
  • 2,211
  • 4
  • 21
  • 50
Alexandre
  • 13
  • 1
  • 4
  • 2
    Possible duplicate of [How to update the GUI from another thread in C#?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) –  Jun 04 '16 at 11:55
  • Search for App.Current.Dispatcher.Invoke this way you can invoke a action in the main thread, i guess thats what your looking for. gl! – natschz Jun 04 '16 at 12:09

3 Answers3

0

Designer Code:

namespace Tasks
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(41, 43);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(131, 43);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 1;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(41, 84);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(165, 20);
            this.textBox1.TabIndex = 2;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(240, 151);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.TextBox textBox1;
    }
}

This is the form code:

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Tasks
{
    public partial class Form1 : Form
    {
        //Just incase you need to stop the current task
        CancellationTokenSource cts;

        public Form1()
        {
            InitializeComponent();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //showing that the form is still working
            MessageBox.Show(this,"This button still works :)");
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            cts = new CancellationTokenSource();

            await CreateTask();
        }

        private async Task CreateTask()
        {
            //Create a progress object that can be used within the task
            Progress<string> mProgress; //you can set this to Int for ProgressBar
            //Set the Action to a function that will catch the progress sent within the task
            Action<string> progressTarget = ReportProgress;
            //Your new Progress with the included action function
            mProgress = new Progress<string>(progressTarget); 

            //start your task
            string result = await MyProcess(mProgress);

            MessageBox.Show(this, result);
        }

        //notice the myProgress this can be used within your task to report back to UI thread.
        private Task<string> MyProcess(IProgress<string> myProgress)
        {
            return Task.Run(() =>
            {
                //here you will sen out to your UI thread whatever text you want.
                //typically used for progress bar.
                myProgress.Report("It Works..");
                //your tasks return
                return "Yes it really does work...";

            }, cts.Token);
        }

        private void ReportProgress(string message)
        {
            //typically to update a progress bar or whatever
            //this is where you Update your UI thread with text from within the Task.
            textBox1.Text = message;
        }
    }
}

Basically what it you are doing is passing in a Progress that you can use with your task.

EJD
  • 751
  • 6
  • 19
0

If you are inside a Windows Phone (WinRT, not Silverlight) Project you can also use

await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
        {
            // your code
        }));
Khrimm
  • 136
  • 1
  • 6
-1

If you use the async/await programming model, you could do this quite easily.

Instead of what you have, try something like this:

private async void myButton_Click(object sender, RoutedEventArgs e)
{
    Task t = MyMethod();
    await t;
}

private async Task MyMethod()
{
    myTextBlock.Text = "Worked!";
}
bodangly
  • 2,473
  • 17
  • 28
  • 1
    Thanks! That was exactly what I needed. Just two more questions: 1 - Shouldn't I use 'await' operator in the code of private async Task MyMethod()? 2 - Can you suggest any further reading about multithreading and async/await in C#? Thanks again! – Alexandre Jun 05 '16 at 19:45
  • 2
    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx http://blog.stephencleary.com/2012/02/async-and-await.html And the rest of Stephen Cleary's blog. I learned my information from a combination of him and CLR via C# by Jeremy Richter. You don't need to await anything in MyMethod, you can ignore the warning safely in this case. – bodangly Jun 05 '16 at 20:21
  • 1
    @Alexandre By the way your English seems pretty spot on to me, never would have guessed it wasn't your first language. – bodangly Jun 05 '16 at 20:24
  • 1
    @EJD I don't believe it will. The Task will run in the threadpool and marshal the results back through the SynchronizationContext of the GUI thread. Control will return to the clicked handler after the Text property has been set. It should not lock up the thread. I am not a WinForms developer but I do this exact thing in Xamarin all the time and have never seen it lock up the thread. – bodangly Jun 06 '16 at 03:05
  • @bodangly Thank you very much for the tips. – Alexandre Jun 06 '16 at 11:42
  • @bodangly Put a log running code before `myTextBlock.Text = "Worked!";` and you'll see that the UI thread will lock. This answer is completely misleading and using tasks in a wrong way. **NOTHING** in the code above is running on a different thread. – Zein Makki Jul 12 '17 at 12:14