0

When I press the button, decoding a Qrcode works well but I want to do it with a Thread. I wrote like below but the thread doesn't work. Does anyone know, how can i fix it?
Thanks.
Here is my code:

private async void button2_Click(object sender, EventArgs e)
{
    await Task.Run(() =>
    {
        pictureBox1.Image = Image.FromFile(@"C:\Users...\1.jpg");
        MessagingToolkit.QRCode.Codec.QRCodeDecoder decoder = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();
        textBox2.Text = decoder.Decode(new QRCodeBitmapImage(pictureBox1.Image as Bitmap));
    });
}
Airn5475
  • 2,452
  • 29
  • 51
Flash
  • 85
  • 10
  • 2
    Does this answer your question? [How do I update the GUI from another thread?](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) – Crowcoder Nov 22 '19 at 13:08
  • 1
    why are accessing an UI element "textBox2.Text" from another thread – Erwin Draconis Nov 22 '19 at 13:11
  • @ErwinDraconis it is not uncommon to offload some cpu bound work to another thread and then update the UI when it is done. – Crowcoder Nov 22 '19 at 13:16
  • @ErwinDraconis i added new textbox3 just right now but it still has the same problem: This is the error message: Cross-thread operation not valid: Control 'textBox3' accessed from a thread other than the thread it was created on.' – Flash Nov 22 '19 at 13:17
  • If the image for your picturebox is always the same, then load it into your app as an **embedded resource**, or possibly an ImageList. If the QR decoder gets used in multiple places, then declare it at class/form level and re-use the same instance of creating a new one each time. – Idle_Mind Nov 22 '19 at 14:39

3 Answers3

2

This will keep your UI responsive

private async void button2_Click(object sender, EventArgs e)
    {
        var Result = await Decode("Image Path");

        textBox2.Text = Result;
    }

    private async Task<string> Decode(string PathOfImage)
    {
        var DecodedText = string.Empty;
        var decoder = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();

        await Task.Run(() =>
        {
            DecodedText = decoder.Decode(new QRCodeBitmapImage(Image.FromFile(PathOfImage) as Bitmap));
        });

        return DecodedText;
    }

Update 2 : here is how you can do it in one function :

private async void button2_Click(object sender, EventArgs e)
    {
        var DecodedText = string.Empty;
        var decoder = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();

        await Task.Run(() => {
            DecodedText = decoder.Decode(new QRCodeBitmapImage(Image.FromFile(PathOfImage) as Bitmap));
        });

        textBox2.Text = DecodedText;
    }
Erwin Draconis
  • 764
  • 8
  • 20
  • Thanks for the answer but it has an error with the word "Image" in the line : "Image.FromFile(...)" i guess one "=" is extra but even with removing the second "=" it shows an error with the word "Image" – Flash Nov 22 '19 at 13:26
  • @gromit190 Thanks for the answer but it has an error with the word "Image" in the line : "Image.FromFile(...)" i guess one "=" is extra but even with removing the second "=" it shows an error with the word "Image" – Flash Nov 22 '19 at 13:33
  • 1
    @arash6657 im not sur why you get this exception, try to pass Image.FromFile(PathOfImage) directly to the QRCodeBitmapImage constructor, see the edited answer – Erwin Draconis Nov 22 '19 at 13:36
  • Thanks for the updated code. it works pretty well :) just one more question, is it also possible to write it easier in one function? (sth like what i wrote but it didn't work) – Flash Nov 22 '19 at 13:40
1

You should update the TextBox after the completion of the Task, at a point where you are back to the UI thread.

private async void button2_Click(object sender, EventArgs e)
{
    var bitmap = await Task.Run(() =>
    {
        return (Bitmap)Image.FromFile(@"C:\Users...\1.jpg");
    });
    pictureBox1.Image = bitmap;
    var result = await Task.Run(() =>
    {
        var decoder = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();
        return decoder.Decode(new QRCodeBitmapImage(bitmap));
    });
    textBox2.Text = result;
}

Update: The code that updates the PictureBox should be moved out of the Task.Run body too.


Update: The loading of image can block the UI too, so I moved it in a separate Task.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • @arash6657 I updated my answer because there is another potential issue with the `pictureBox1`. – Theodor Zoulias Nov 22 '19 at 13:49
  • @arash6657 my advice is to set the property [`Control.CheckForIllegalCrossThreadCalls` = true;](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.checkforillegalcrossthreadcalls) at the start of your program (before `Application.Run`), to check for other problems that may have passed unnoticed. Personally I always set this property on Debug mode. I don't set it on Release mode because it has a performance overhead. – Theodor Zoulias Nov 22 '19 at 13:55
  • Thanks for the update code but it still has a problem. as you mentioned it block the UI. but with the new code, it showed me this error: Object is currently in use elsewhere.' – Flash Nov 22 '19 at 14:42
  • @arash6657 Hmm, it is possible that the `pictureBox1` locks the image. Could you try `pictureBox1.Image = new Bitmap(bitmap);`? – Theodor Zoulias Nov 22 '19 at 14:47
  • Thanks, yes it works well but i would like also to delete the image at the end of the process and it showed me the error :"it is being used by another process ", Could you please tell how to fix it also? Thankss i added the code below at the end: if (File.Exists(@"C:\Users...\1.jpg")) { File.Delete(@"C:\Users...\1.jpg"); } – Flash Nov 22 '19 at 14:58
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202902/discussion-between-arash6657-and-theodor-zoulias). – Flash Nov 22 '19 at 15:01
-1

The dispatcher object is used to modify the UI from a thread or a Task

Below how to use a Task

Method 1

bool result ;
Task<bool> task = Task.Run<bool>(async () => await RefreshUIAsync());
str = task.Result;

public async Task<bool> RefreshUIAsync()
{
   bool result;
   result= await Task.Factory.StartNew(() => RefreshUI());
   return result;
}

private string RefreshUI()
{           
  bool result;
  try
  {
     this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
    {   
      string ImagePath="";
      pictureBox1.Image = Image.FromFile(ImagePath);
      .......
    });
    result=true;
  }
  catch (Exception ex)
  {
      result=false;
  }  
 return result;
}

Method 2

RefreshUIAsync().Wait();  

public async Task RefreshUIAsync()
{
   await Task.Run(() => {
   This.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
    {   
      string ImagePath="";
      pictureBox1.Image = Image.FromFile(ImagePath);
      .......
    });
   });
}

And below how to use a Thread

Method 1

myThread = new Thread(() => ThreaRefreshUI());
myThread.Start();

private void ThreaRefreshUI()
{

    try
    {
    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
    {   
      string ImagePath="";
      pictureBox1.Image = Image.FromFile(ImagePath);
      .......
    });
    }
    catch (Exception ex)
    {

    }
}

Method 2

Thread thread = new Thread(new ThreadStart(ThreaRefreshUI));
thread.Start();

 public void ThreaRefreshUI()
 {
   try
   {
    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
    {   
      string ImagePath="";
      pictureBox1.Image = Image.FromFile(ImagePath);
      .......
    });
   }
   catch (Exception ex)
   {

   }
 }

Method 3

 Thread thread = new Thread(ThreaRefreshUI);
 thread.Start();

 public void ThreaRefreshUI()
 {
   try
   {
    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
    {   
      string ImagePath="";
      pictureBox1.Image = Image.FromFile(ImagePath);
      .......
    });
   }
   catch (Exception ex)
   {

   }
 }
LPGTE SOFTS
  • 120
  • 8