Background Worker giving exceptions.
Hey so I'm working on a File Manager in WPF and I'm trying to use a background worker to calculate file/directory sizes in the background so the application doesn't freeze. It does work but the main problem is that when a directory is getting calculated if the user selects another item while the background worker is running in the background it gives out an exception about how it cannot run multiple instances of itself at the same time so I added logic to cancel the previous background worker if another one is selected, I did everything from setting WorkerSupportsCancellation to true and adding logic to cancel it in the DoWork function and I tried everything from asking ChatGPT to browsing the entire Microsoft documentation about it and tried dozens upon dozens of diffrent solutions but NOTHING worked. At this point I am desperate and any help towards fixing this issue would mean THE WORLD to me. I have linked my MainWindow xaml file and cs file.
45 Replies
Also here's my entire project if you guys need it.
I'd suggesting putting your code out in a remote somewhere like github; I don't know you well enough to trust you with a a zip file on discord.
damn ok
I'll try to figure that out
it all sounds like you're queuing work
and not using a background worker
so it's likely a bad fit
oh
then what should I use? I tried asyncronious stuff with Task's and async and await but that didn't work either
so, the challenge becomes, a function of, if I click on Folder A, then Folder B, you want to calculate size for both
but
what if I click on A, then B, then C, then B again whilst B is still calculating
what should happen in that case?
the thing is that, if the user selects another item while one is calculating, I want to just cancel that one and start the new one so it never ends up calculating multiple items at the same time
you're also going with WPF as winfoms route, which makes this ... harder; it'll be simpler in mvvm landscape tbh.
Are you doing this because it feels easier to you?
I'll try but I started using WPF a few days ago so this is all new teritory for me
fair
kinda, also because I don't need the other item's size if the user selected another one
You can cancel a backgorund worker
but cancelling a background worker is tricky, essentially you have to check if cancellation is pending in your DoWork handler
oh you are
this is all a big management problem then
yeah kind of
I'm thinking
https://github.com/ItsCristi/FileManager
okay i manager to put it on git hub
is it a private repo?
i dont think so im not sure let me check
GitHub
ItsCristi/File-Manager
Contribute to ItsCristi/File-Manager development by creating an account on GitHub.
but not FileManager
and File-Manager is empty
oh ok
https://github.com/ItsCristi/FileManager/releases/tag/v1.0
i think i did it
can you acces it
sorry it's like the second time im using git as well lol
probably you're easiest next step is something like....
that should bounce requests until either processing is done, or cancelled.
but that doesn't help your UX
oh okay I'll try it, I'm sorry but right now I have to go to sleep so I really gotta go
thank you so much though
you have no idea how much it means to me
You can make the _isProcessing a public property like public bool IsProcessing {get;set;} and set some button press-ability to enabled or disabled.
I'd have to play with that a bit in WPF, it might have to be a dependecy property.
It is very uncommon to use the very old
BackgroundWorker
instead of async
/await
and IProgress<T>
.yeah I'm really not a fan of background worker
I'd change a lot on the whole approach tbh.
@🧨 New Years Mayor McCheese 🧨
I'm sorry for the ping but I added the async and Task logic but it still doesn't work, my application freezes after I select an item until it's done calculating the size, what did I do wrong?
The main logic is here
string getReadableSize(long sib) {
string[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB" };
int i = 0;
double size = sib;
while (size >= 1024 && i < sizeSuffixes.Length - 1) { size /= 1024; i++; }
return $"{size:0.##} {sizeSuffixes[i]}";
}
string getSelectedItemExtension() {
foreach (StackPanel item in FileList.SelectedItems) {
if (item.Children[1] is Label label) {
string path = Path.Combine(CurrentPath, label.Content.ToString());
if(File.Exists(path)) return new FileInfo(path).Extension;
else if(Directory.Exists(path)) return "Folder";
}
} return null;
}
async Task<long> getDirSize(string path) {
long res = 0;
try {
foreach (string file in Directory.GetFiles(path)) res += new FileInfo(file).Length;
foreach (string dir in Directory.GetDirectories(path)) res += await getDirSize(dir);
} catch{}
return res;
}
async Task<string> getSelectionSize() {
long res = 0;
foreach(StackPanel item in FileList.SelectedItems) {
if (item.Children[1] is Label label) {
string path = Path.Combine(CurrentPath,label.Content.ToString());
if(Directory.Exists(path)) res += await getDirSize(path);
else if(File.Exists(path)) res += new FileInfo(path).Length;
}
} return getReadableSize(res);
}
void FileList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (FileList.SelectedItems.Count == 0) {
CurrentItemSize = "Size: --";
CurrentItemType = "Type: --";
} else {
CurrentItemSize = "Size: Calculating...";
CurrentItemSize = getSelectionSize().Result;
if (FileList.SelectedItems.Count == 1) CurrentItemType = $"Type: {getSelectedItemExtension()}";
else CurrentItemType = "Type: --";
}
}
it should work but it doesn't and I have no clue why
The keyword
async
does not guarantee that your method runs asynchron. Your code is synchron all the way and that is the reason why it is only blocking. getSelectionSize().Result;
is the perfect dead-lock pattern. Even if your code were perfect asynchron this call will block or dead-lock your app anyway.i changed it again and made the FileList_SelectionChanged function async and instead of getSelectionSize().Result I did await getSelectionSize() and it still didn't work
I'll check in a bit, I've got to wrap up some eoy stuff at work.
It's ok thank you a lot
Your
async
methods are all running synchron so it is normal that your code will block.but why? how can I make them asyncronious??
like I seriously tried everything if you could help me id mean the world to me
at this point I have tried everything and nothing works
if you have nothing to await, then build a normal (sync) method and when you need an async result call it with
var result = await Task.Run( () => CalulateWithSyncMethod() );
But your method is somehow difficult, because you access UI components within the calculation (StackPanel, Label)
That is one reason for MVVM pattern.problem spaces in this arena are much easier with mvvm
With MVVM you will never face problems like this, but other 😁
background worker is a dated concept, it's more applicable to winforms as a managed component.
( opinion )
The OP has already switched to the async/await pattern
ah, I didn't get a chance to look back yet
it's also more complex because of cancellation and such
There's no reason to use it in Forms either
BW has been completely supplanted by Task
It's a component that you can ( probably should not ) add to a form.
Still not a reason to use it
Sure, but in winforms people are going to, because 1. they don't know better, 2. it's accessible in the tool window.
omg i finally managed to do it
this line of code saved my god damn live you have no idea how fricking thankful I am
I finally managed to get it working exacly how I wanted it
Thank you all SO, SOOOO much! ❤️
@Cristi I'd be careful with Task.Run generally; it's okay here, because it's a WPF app; but you can run into some serious problems in other types of apps.
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Task.Run Etiquette Examples: Don't Use Task.Run for the Wrong Thing
I had quite a few comments on my last post asking for more explicit examples of Correct vs. Incorrect Task.Run usage.
Okay thank you a lot <3