1

I know there are lots of topics on this issues. I looked at them, as well as at Microsoft's website, but I still don't know how to apply possible solutions to my case. So, I have a Windows Forms Desktop app that is supposed to read some text files and then print results to the app interface. I've got this function call after I choose an item from a drop down list in a Combobox:

private async void infosFolderCbx_SelectionChangeCommitted(object sender, EventArgs e)
    {
        progressBar1.Visible = true;
        progressBar1.Style = ProgressBarStyle.Marquee;
        await Task.Run(() => UpdateQuestOrInfoFilesList(infosFolderCbx.SelectedItem.ToString(), infoFoldersHistoryList));infoFoldersHistoryList);
        progressBar1.Visible = false;
    }

And it calls this function:

private void UpdateQuestOrInfoFilesList(string folderWithFiles, List<string> list)
    {

        if (Directory.Exists(folderWithFiles) && (list == questFoldersHistoryList || list == infoFoldersHistoryList))
        {
            int maxHistoryItems = 5;
            ListView listview; ComboBox cbx; string fileExtension; 
            if (list == questFoldersHistoryList) { listview = questFilesList; cbx = questFolderCbx; fileExtension = questFileExtension; }
            else /* if (list == infoFoldersHistoryList) */  { listview = infoFilesList; cbx = infosFolderCbx; fileExtension = infoFileExtension; }

            //This part updates history lists
            if (list.Contains(folderWithFiles)) { list.Remove(folderWithFiles); }
            list.Insert(0, folderWithFiles);
            if (list.Count > maxHistoryItems) { list.RemoveRange(maxHistoryItems, list.Count - maxHistoryItems); }
            cbx.Items.Clear();
            cbx.Items.AddRange(list.ToArray());
            if (list == questFoldersHistoryList) { cbx.Text = questFilesFolder = folderWithFiles; }
            else /* if (list == infoFoldersHistoryList) */  { cbx.Text = infoFilesFolder = folderWithFiles; }

            //This part updates files shown in the corresponding window
            string[] files = Directory.GetFiles(folderWithFiles);
            listview.Items.Clear();

            foreach (string file in files)
            {
                //string textToAdd = file;                                              //This add to the list the whole file path
                //string textToAdd = Path.GetFileName(file);                           //This adds only the file name
                string textToAdd = Path.GetFileName(file).Replace(fileExtension, ""); //This adds only the base file name without: _info_G3_World_01.info
                listview.Items.Add(textToAdd);

                if (list == questFoldersHistoryList)            { LoadQuestToQuestDict(textToAdd); }
                else /* if (list == infoFoldersHistoryList) */  { LoadInfoToInfosDict(textToAdd); }
            }
            //LookForDialogs();
            infoDataTextBox.Text = string.Join(Environment.NewLine, dictInfoFiles.Keys.ToArray());
        }
        else { MessageBox.Show("Invalid List<string> list in UpdateQuestOrInfoFilesList method"); }
    }

Now, it seems I'm getting this error

Cross-thread operation not valid. Control "" accessed from a thread other than the thread it was created on.

on

infosFolderCbx.SelectedItem.

I have tried some solutions, most notably the one from the Microsoft's webpage, but apparently I'm not able to apply to my case correctly. Any ideas on how to solve this issue? Or is there a better way to actually make a working await function, because my overall goal is just to allow the program to get the data from the text file, show the progress bar while it's doing that and then hide the bar again after it has finished. So maybe there is another way of doing, as the solutions that I read seem rather complicated for such a trivial task.

  • 2
    `for such a trivial task` it's anything but. No OS or language, neither Linux, MacOS, Android or Windows allows background threads to modify the UI. .NET makes this *complicated* task trivial by using `async/await` to get back to the UI thread after a background operation, or the [IProgress](https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/) interface, that allows a background thread to report anything it wants and have a delegate on the UI thread process it – Panagiotis Kanavos Apr 12 '23 at 09:35
  • 2
    @GuruStron I think that the OP could be helped by a more targeted advice, than the generic advices offered in the [linked question](https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the). – Theodor Zoulias Apr 12 '23 at 09:36
  • There are dozens of duplicates that answer this question. The original duplicate is a good explanation. There are a lot of other duplicates that show how to use `IProgress` to upgrade from the background – Panagiotis Kanavos Apr 12 '23 at 09:39
  • There are tons of interactions with the UI inside the `UpdateQuestOrInfoFilesList` method, which makes a simple introduction of a `Progress` component not viable. The OP's code is in need of a major overhaul. – Theodor Zoulias Apr 12 '23 at 09:42
  • That's not the case. The question asks only about a ProgressBar and `Progress` can report any object. Nothing says it has to hold a single counter. It can have 3 counters and 5 message fields. Yes, the code needs major refactoring, splitting into individual methods, using proper indentation (if blocks in one line???). The refactored code would probably not need `IProgress<>` – Panagiotis Kanavos Apr 12 '23 at 09:49
  • 1
    modifying the UI from a thread that did not create the control usually throws an invalid operation exception. – Son of Man Apr 12 '23 at 09:49
  • This code can be simplified a *lot* - for starters, split the code that generates the file paths from the UI code. Only the file processing code needs to run in the background, using `var finalFileStrings=await Task.Run(()=>GetThoseFileNames());` Clearing the UI should happen before that. Updating it with the new files should happen afterwards. – Panagiotis Kanavos Apr 12 '23 at 09:55
  • And *definitely* stop putting multiple statements in the same line, especially `if` and their blocks. The method is unreadable right now. – Panagiotis Kanavos Apr 12 '23 at 09:56
  • 1
    Comments are intended for asking for more information about the question, or for suggesting improvements for the question, not for answering the question. Trying to answer the question in the comments is an indication that the question should not be closed in the first place. I really hope that the OP will be able to use the wisdom offered in the questions linked as duplicates to solve their problem, but I am afraid that without targeted help they will get stuck. – Theodor Zoulias Apr 12 '23 at 10:13
  • @PanagiotisKanavos when it comes to putting if statements in the same line - for me personally, that actually makes the code easier to read, if it's a short statement that can actually fit in 1 line. This makes the code shorter so I have more code visible at once. And if I understand your previous comment correctly, I should only apply Task.Run() to the Directory.GetFiles(folderWithFiles) line. Well, the thing is, this is not a problem - they are loaded almost instantaneously. I noticed that listview.Items.Add(textToAdd); is the line that makes the program stuck for a couple of seconds. – Marcin Pagórek Apr 12 '23 at 11:23
  • How far do you have to scroll to read `fileExtension = infoFileExtension;`? If you want help, you need to make sure *others* can read your code and understand it quickly. – Panagiotis Kanavos Apr 12 '23 at 11:31
  • All I'm saying is, it is more comfortable for me to read some code in one line, instead of spreading it across let's say 4 or 5 lines. But that's ok, if you (or maybe other users as well) find it more difficult to read it that way. Still, I think you're probably overreacting a bit, saying that makes my code unreadable. Besides, that is not the problem that I wanted to get some help on :P – Marcin Pagórek Apr 12 '23 at 11:39
  • Marcin making sure that the code is well formatted, and the lines are short enough so that the horizontal scroll bar does not appear, is especially important on this site. Trying to read the code while juggling with both a horizontal and vertical scroll bar around the code, and the vertical scroll bar of the web page, becomes quickly mentally exhausting. Currently the only practical way to read your code is to copy-paste it on Notepad. It would be great if you could edit the question, and add line-breaks where needed so that all code is visible with just vertical scrolling. – Theodor Zoulias Apr 12 '23 at 12:50

0 Answers0