Reputation: 3386
I have a small WPF
application that needs to enumerate through all files in a specified directory and check if a certain string exists in it. this is the search method:
private void btnSearch_Click_1(object sender, RoutedEventArgs e)
{
Thread t = new Thread(()=>search(@"c:\t", "url", true));
t.Start();
}
private void search(string path, string textToSearch, bool ignoreCase)
{
foreach (string currentFile in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
int lineNumber = 0;
foreach (string line in File.ReadLines(currentFile))
{
lineNumber++;
if (line.Contains(textToSearch))
{
lbFiles.Dispatcher.BeginInvoke((Action)(() =>
{
//add the file name and the line number to a ListBox
lbFiles.Items.Add(currentFile + " " + lineNumber);
}));
}
}
}
}
My problem is that if the specified string is found more than once in the file, the line number will be the latter for all occurrences. for a text file that has the following lines:
abcd
EFG
url
hijk123
url
the listbox
will look like this:
when stepping through the code with a breakpoint I can see that immediately after steping out of the search method it "jumps" back into the BeginInvoke
declaration.
Please advise.
Thanks
Upvotes: 0
Views: 59
Reputation: 203821
The issue is that you're closing over the variable lineNumber
. BeginInvoke
is asynchronous, it doesn't wait for the delegate to be called on the UI thread. By the time it manages to get invoked lineNumber
has been incremented a number of times.
There are two solutions. Create a more localized copy of lineNumber
to close over so that the changes aren't seen later:
foreach (string line in File.ReadLines(currentFile))
{
lineNumber++;
if (line.Contains(textToSearch))
{
var lineNumberCopy = lineNumber;
lbFiles.Dispatcher.BeginInvoke((Action)(() =>
{
//add the file name and the line number to a ListBox
lbFiles.Items.Add(currentFile + " " + lineNumberCopy );
}));
}
}
Or use Invoke
instead of BeginInvoke
, so that lineNumber
is read from before it has a chance to be incremented.
Upvotes: 1