3

I have a code that allows the user pick a file, adds some info about it to an observable collection and shows the upload progress, when finished, it binds the image to an Image view. It works properly at once but if you repeat the process, an exception is threw:

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
  at at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.43(intptr,intptr)
  at at (wrapper native-to-managed) Android.Runtime.DynamicMethodNameCounter.43(intptr,intptr)

Code that inserts new element and get its index

switch(x){

 // some code...

    case 6:

        // .... some code 


            _ = Task.Run(async () => {
            try
            {
                int i = default;

            Msg newmsg2 = new Msg() { UploadProgress = 0.0, UploadProgressVisibility = true, IsImageSend = true, ImageSend = "@drawable/icon_default" };

            valuesLock.EnterWriteLock();
            try
            {
                // THE ISSUE IS RELATED WITH THIS WORK
                Device.BeginInvokeOnMainThread(() => ListaMsg.Add(newmsg2));
            }
            finally
            {
                valuesLock.ExitWriteLock();
                valuesLock.EnterReadLock();
                try
                {
                    // THE ISSUE IS RELATED WITH THIS WORK

                    i = ListaMsg.IndexOf(newmsg2); 

                    // Some data can be added while the progress is being updated, I need to get the index of this exactly Item, can't get it with Count-1

                }
                finally
                {
                    valuesLock.ExitReadLock();
                    Resultado a = await Task.Run(()=>UploadFile(filename, i));
                    if (a.status == "ok")
                    {
                        valuesLock.EnterWriteLock();
                        try
                        {
                            Device.BeginInvokeOnMainThread(() =>
                            {
                                MyList[i].msg = "Image was sent";
                                MyList[i].status = true;
                                MyList[i].ImageSend = "mydomain/assets/libs/thumb.php?h=150&w=150&img=" + a.msg;
                            });
                        }
                        finally
                        {
                            valuesLock.ExitWriteLock();
                            SendMsg(6, a.msg, i);
                        }
                    }
                    else
                    {
                        valuesLock.EnterWriteLock();
                        try
                        {
                            Device.BeginInvokeOnMainThread(() =>
                            {
                                MyList[i].ImageSend = "@drawable/icon_default";
                                MyList[i].icon = "\uf057";
                                MyList[i].IconColor = Xamarin.Forms.Color.Red;
                            });
                        }
                        finally { valuesLock.ExitWriteLock(); }
                    }
                }
            }
        }
        catch (Exception e)
        {
            ...
        }
    });
break;
}

UPLOAD METHOD

public async Task<Resultado> UploadFile(string url, string filepath)
{
  //some code

  webclient.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);

  //some code
}

And finnaly the ProgressChanged callback:

private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e, int idreference)
{
    double temp = ((double)e.BytesSent) / filesize;
    Device.BeginInvokeOnMainThread(() => {
        valuesLock.EnterWriteLock();
        try
        {
            // THE EXCEPTION IS BEING THREW AT LINE BELOW, idreference value is -1 and not the correct index
            MyList[idreference].UploadProgress = temp;
        }
        finally { valuesLock.ExitWriteLock(); }
    });
}

Why this works only at once? Did I miss or did something wrong?

Éder Rocha
  • 1,538
  • 11
  • 29
  • Note: Xamarin is not my area, but I understand threading. What happens if `IndexOf` is called before the main thread adds the item to the list? You're calling `BeginInvokeOnMainThread`, which sounds like it doesn't wait for completion. – ProgrammingLlama May 02 '20 at 03:30
  • If I call IndexOf before, it won't get any index since the List doesn't contains the desired element. I think BeginInvokeOnMainThread is needed here because adding the new item will update the UI. – Éder Rocha May 02 '20 at 03:34
  • Read what I said again. – ProgrammingLlama May 02 '20 at 03:35
  • I'll rephrase what I think happens: 1) You call `BeginInvokeOnMainThread` 2) You call `IndexOf` 3) The "main thread" becomes available and `ListaMsg.Add` is performed. -- You have to understand that methods with "Begin" in their name when related to threading usually only return once the work is started/scheduled. They _do not_ wait for the work to _finish_. – ProgrammingLlama May 02 '20 at 03:37
  • [Related](https://stackoverflow.com/questions/54573940/is-it-possible-to-wait-for-device-begininvokeonmainthread-code-to-finish-contin) – ProgrammingLlama May 02 '20 at 03:38
  • @John I understand your point but since IndexOf is inside the "finally" block, it doesn't "waits" until the ancedent work is finished? – Éder Rocha May 02 '20 at 03:42
  • `finally` doesn't mean "once work on other threads is done", it's part of the try/catch/finally pattern. `finally` will always be called regardless of whether the code in the `try` section throws an exception or not. Perhaps you have a misunderstanding of its purpose? – ProgrammingLlama May 02 '20 at 03:44
  • @John hmm, I will try do something to wait the ancedent work and let you know! thanks. – Éder Rocha May 02 '20 at 03:45
  • @John yeah, it was that. Fixed by using ```await Device.InvokeOnMainThreadAsync(() => { ... } );``` Thanks in advance. – Éder Rocha May 02 '20 at 04:27
  • 1
    Glad to hear you fixed it. I recommend adding an answer (you're allowed to answer your own question) so that anyone with the same issue in future can learn from it. :) – ProgrammingLlama May 02 '20 at 05:19

1 Answers1

6

The root of problem was the part that contains IndexOf() method. It was executing before the ancedent execution on Main Thread and index was always -1. Fixed by changing:

Device.BeginInvokeOnMainThread(() => ... );

by

await Device.InvokeOnMainThreadAsync(() => { ... } );
Éder Rocha
  • 1,538
  • 11
  • 29