Note that there are some explanatory texts on larger screens.

plurals
  1. PODynamically updating a DataGrid using a background worker
    primarykey
    data
    text
    <p>I have an <code>ObservableCollection</code> of custom class that holds a string and an int:</p> <pre><code>public class SearchFile { public string path { set; get; } public int occurrences { set; get; } } </code></pre> <p>I want to display the collection in a <code>dataGrid</code>. The collection has methods that notify whenever it has been updated, so so far it's only a matter of linking it to the <code>DataGrid.ItemsSource</code> (correct?). Here's the grid XAML (with <code>dataGrid1.ItemsSource = files;</code> in the C# codebehind):</p> <pre><code> &lt;DataGrid AutoGenerateColumns="False" Height="260" Name="dataGrid1" VerticalAlignment="Stretch" IsReadOnly="True" ItemsSource="{Binding}" &gt; &lt;DataGrid.Columns&gt; &lt;DataGridTextColumn Header="path" Binding="{Binding path}" /&gt; &lt;DataGridTextColumn Header="#" Binding="{Binding occurrences}" /&gt; &lt;/DataGrid.Columns&gt; &lt;/DataGrid&gt; </code></pre> <p>Now things are more complicated. I first want to display the <code>path</code>s with the default values of <code>occurrence</code> of zero. Then, I want to go through every <code>SearchFile</code> and update it with a calculated value of <code>occurrence</code>. Here's the helper function:</p> <pre><code> public static void AddOccurrences(this ObservableCollection&lt;SearchFile&gt; collection, string path, int occurrences) { for(int i = 0; i &lt; collection.Count; i++) { if(collection[i].path == path) { collection[i].occurrences = occurrences; break; } } } </code></pre> <p>And here's the placeholder worker function:</p> <pre><code> public static bool searchFile(string path, out int occurences) { Thread.Sleep(1000); occurences = 1; return true; //for other things; ignore here } </code></pre> <p>I'm using a <code>BackgroundWorker</code> as the background thread. Here's how:</p> <pre><code> private void worker_DoWork(object sender, DoWorkEventArgs e) { List&lt;string&gt; allFiles = new List&lt;string&gt;(); //allFiles = some basic directory searching this.Dispatcher.Invoke(new Action(delegate { searchProgressBar.Maximum = allFiles.Count; files.Clear(); // remove the previous file list to build new one from scratch })); /* Create a new list of files with the default occurrences value. */ foreach(var file in allFiles) { SearchFile sf = new SearchFile() { path=file, occurrences=0 }; this.Dispatcher.Invoke(new Action(delegate { files.Add(sf); })); } /* Add the occurrences. */ foreach(var file in allFiles) { ++progress; // advance the progress bar this.Dispatcher.Invoke(new Action(delegate { searchProgressBar.Value = progress; })); int occurences; bool result = FileSearcher.searchFile(file, out occurences); files.AddOccurrences(file, occurences); } } </code></pre> <p>Now when I run it, there are two problems. First, updating the progress bar's value throws the <code>The calling thread cannot access this object because a different thread owns it.</code> exception. Why? It's in a dispatcher, so it should work just fine. And second, the <code>foreach</code> loop breaks on the <code>bool result =...</code> line. I commenting it out and tried setting <code>int occurences = 1</code>, and then the loop goes around, but there's something weird going on: whenever I call the method, it's either all zeroes, all ones, or a between state, with onez beginning after a seemingly random number of zeroes).</p> <p>Why's that? </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload